You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2017/04/18 14:13:18 UTC
[1/8] ignite git commit: IGNITE-4565: Implemented CREATE INDEX and
DROP INDEX. This closes #1773. This closes #1804.
Repository: ignite
Updated Branches:
refs/heads/ignite-2.0 9e7421f26 -> 2edb935cb
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexTransactionalReplicatedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexTransactionalReplicatedSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexTransactionalReplicatedSelfTest.java
new file mode 100644
index 0000000..85a8837
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexTransactionalReplicatedSelfTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+
+/** */
+public class H2DynamicIndexTransactionalReplicatedSelfTest extends H2DynamicIndexAbstractSelfTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.REPLICATED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected CacheAtomicityMode atomicityMode() {
+ return CacheAtomicityMode.TRANSACTIONAL;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean nearCache() {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/SchemaExchangeSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/SchemaExchangeSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/SchemaExchangeSelfTest.java
new file mode 100644
index 0000000..95ad2f1
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/SchemaExchangeSelfTest.java
@@ -0,0 +1,589 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.IgniteClientDisconnectedException;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.query.QueryTypeDescriptorImpl;
+import org.apache.ignite.internal.util.lang.GridAbsPredicate;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.lang.IgniteFuture;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.testframework.GridTestUtils;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Tests for schema exchange between nodes.
+ */
+public class SchemaExchangeSelfTest extends AbstractSchemaSelfTest {
+ /** Node on which filter should be applied (if any). */
+ private static String filterNodeName;
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ stopAllGrids();
+
+ filterNodeName = null;
+
+ super.afterTest();
+ }
+
+ /**
+ * Test propagation of empty query schema for static cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testEmptyStatic() throws Exception {
+ checkEmpty(false);
+ }
+
+ /**
+ * Test propagation of empty query schema for dynamic cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testEmptyDynamic() throws Exception {
+ checkEmpty(true);
+ }
+
+ /**
+ * Check empty metadata propagation.
+ *
+ * @param dynamic Dynamic start flag.
+ * @throws Exception If failed.
+ */
+ private void checkEmpty(boolean dynamic) throws Exception {
+ IgniteEx node1;
+
+ if (dynamic) {
+ node1 = startNoCache(1);
+
+ node1.getOrCreateCache(cacheConfiguration());
+ }
+ else
+ node1 = start(1);
+
+ assertTypes(node1);
+
+ IgniteEx node2 = start(2, KeyClass.class, ValueClass.class);
+
+ assertTypes(node1);
+ assertTypes(node2);
+
+ IgniteEx node3 = start(3, KeyClass.class, ValueClass.class, KeyClass2.class, ValueClass2.class);
+
+ assertTypes(node1);
+ assertTypes(node2);
+ assertTypes(node3);
+ }
+
+ /**
+ * Test propagation of non-empty query schema for static cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testNonEmptyStatic() throws Exception {
+ checkNonEmpty(false);
+ }
+
+ /**
+ * Test propagation of non-empty query schema for dynamic cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testNonEmptyDynamic() throws Exception {
+ checkNonEmpty(true);
+ }
+
+ /**
+ * Check \u0442\u0449\u0442-empty metadata propagation.
+ *
+ * @param dynamic Dynamic start flag.
+ * @throws Exception If failed.
+ */
+ private void checkNonEmpty(boolean dynamic) throws Exception {
+ IgniteEx node1;
+
+ if (dynamic) {
+ node1 = startNoCache(1);
+
+ node1.getOrCreateCache(cacheConfiguration(KeyClass.class, ValueClass.class));
+ }
+ else
+ node1 = start(1, KeyClass.class, ValueClass.class);
+
+ assertTypes(node1, ValueClass.class);
+
+ IgniteEx node2 = start(2);
+
+ assertTypes(node1, ValueClass.class);
+ assertTypes(node2, ValueClass.class);
+
+ IgniteEx node3 = start(3, KeyClass.class, ValueClass.class, KeyClass2.class, ValueClass2.class);
+
+ assertTypes(node1, ValueClass.class);
+ assertTypes(node2, ValueClass.class);
+ assertTypes(node3, ValueClass.class);
+ }
+
+ /**
+ * Make sure that new metadata can be propagated after destroy.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDynamicRestarts() throws Exception {
+ IgniteEx node1 = start(1, KeyClass.class, ValueClass.class);
+ IgniteEx node2 = startNoCache(2);
+ IgniteEx node3 = startClientNoCache(3);
+ IgniteEx node4 = startClientNoCache(4);
+
+ assertTypes(node1, ValueClass.class);
+ assertTypes(node2, ValueClass.class);
+
+ assertTypes(node3);
+
+ node3.cache(CACHE_NAME);
+ assertTypes(node3, ValueClass.class);
+
+ // Check restarts from the first node.
+ node1.destroyCache(CACHE_NAME);
+
+ node1.getOrCreateCache(cacheConfiguration());
+
+ assertTypes(node1);
+ assertTypes(node2);
+ assertTypes(node3);
+
+ node1.destroyCache(CACHE_NAME);
+
+ node1.getOrCreateCache(cacheConfiguration(KeyClass.class, ValueClass.class,
+ KeyClass2.class, ValueClass2.class));
+
+ assertTypes(node1, ValueClass.class, ValueClass2.class);
+ assertTypes(node2, ValueClass.class, ValueClass2.class);
+
+ assertTypes(node3);
+
+ node3.cache(CACHE_NAME);
+ assertTypes(node3, ValueClass.class, ValueClass2.class);
+
+ // Check restarts from the second node.
+ node2.destroyCache(CACHE_NAME);
+
+ node2.getOrCreateCache(cacheConfiguration());
+
+ assertTypes(node1);
+ assertTypes(node2);
+ assertTypes(node3);
+
+ node2.destroyCache(CACHE_NAME);
+
+ node2.getOrCreateCache(cacheConfiguration(KeyClass.class, ValueClass.class,
+ KeyClass2.class, ValueClass2.class));
+
+ assertTypes(node1, ValueClass.class, ValueClass2.class);
+ assertTypes(node2, ValueClass.class, ValueClass2.class);
+
+ assertTypes(node3);
+
+ node3.cache(CACHE_NAME);
+ assertTypes(node3, ValueClass.class, ValueClass2.class);
+
+ assertTypes(node4);
+
+ node4.cache(CACHE_NAME);
+ assertTypes(node4, ValueClass.class, ValueClass2.class);
+
+ // Make sure that joining node observes correct state.
+ assertTypes(start(5), ValueClass.class, ValueClass2.class);
+ assertTypes(startNoCache(6), ValueClass.class, ValueClass2.class);
+
+ assertTypes(startClient(7), ValueClass.class, ValueClass2.class);
+
+ IgniteEx node8 = startClientNoCache(8);
+
+ assertTypes(node8);
+
+ node8.cache(CACHE_NAME);
+ assertTypes(node8, ValueClass.class, ValueClass2.class);
+ }
+
+ /**
+ * Test client join for static cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testClientJoinStatic() throws Exception {
+ checkClientJoin(false);
+ }
+
+ /**
+ * Test client join for dynamic cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testClientJoinDynamic() throws Exception {
+ checkClientJoin(true);
+ }
+
+ /**
+ * Check client join.
+ *
+ * @throws Exception If failed.
+ */
+ private void checkClientJoin(boolean dynamic) throws Exception {
+ IgniteEx node1;
+
+ if (dynamic) {
+ node1 = startNoCache(1);
+
+ node1.getOrCreateCache(cacheConfiguration(KeyClass.class, ValueClass.class));
+ }
+ else
+ node1 = start(1, KeyClass.class, ValueClass.class);
+
+ IgniteEx node2 = startClient(2);
+
+ assertTypes(node1, ValueClass.class);
+ assertTypes(node2, ValueClass.class);
+
+ IgniteEx node3 = startClient(3, KeyClass.class, ValueClass.class,
+ KeyClass2.class, ValueClass2.class);
+
+ assertTypes(node1, ValueClass.class);
+ assertTypes(node2, ValueClass.class);
+ assertTypes(node3, ValueClass.class);
+
+ IgniteEx node4 = startClientNoCache(4);
+
+ assertTypes(node1, ValueClass.class);
+ assertTypes(node2, ValueClass.class);
+ assertTypes(node3, ValueClass.class);
+
+ assertTypes(node4);
+
+ node4.cache(CACHE_NAME);
+ assertTypes(node4, ValueClass.class);
+ }
+
+ /**
+ * Test client cache start (static).
+ *
+ * @throws Exception If failed.
+ */
+ public void testClientCacheStartStatic() throws Exception {
+ checkClientCacheStart(false);
+ }
+
+ /**
+ * Test client cache start (dynamic).
+ *
+ * @throws Exception If failed.
+ */
+ public void testClientCacheStartDynamic() throws Exception {
+ checkClientCacheStart(true);
+ }
+
+ /**
+ * Check client cache start.
+ *
+ * @throws Exception If failed.
+ */
+ private void checkClientCacheStart(boolean dynamic) throws Exception {
+ IgniteEx node1 = startNoCache(1);
+
+ IgniteEx node2;
+
+ if (dynamic) {
+ node2 = startClientNoCache(2);
+
+ node2.getOrCreateCache(cacheConfiguration(KeyClass.class, ValueClass.class));
+ }
+ else
+ node2 = startClient(2, KeyClass.class, ValueClass.class);
+
+ assertTypes(node1, ValueClass.class);
+ assertTypes(node2, ValueClass.class);
+
+ IgniteEx node3 = start(3);
+ IgniteEx node4 = start(4, KeyClass.class, ValueClass.class, KeyClass2.class, ValueClass2.class);
+ IgniteEx node5 = startNoCache(5);
+
+ IgniteEx node6 = startClient(6);
+ IgniteEx node7 = startClient(7, KeyClass.class, ValueClass.class, KeyClass2.class, ValueClass2.class);
+ IgniteEx node8 = startClientNoCache(8);
+
+ assertTypes(node1, ValueClass.class);
+ assertTypes(node2, ValueClass.class);
+ assertTypes(node3, ValueClass.class);
+ assertTypes(node4, ValueClass.class);
+ assertTypes(node5, ValueClass.class);
+ assertTypes(node6, ValueClass.class);
+ assertTypes(node7, ValueClass.class);
+
+ assertTypes(node8);
+ node8.cache(CACHE_NAME);
+ assertTypes(node8, ValueClass.class);
+
+ node2.destroyCache(CACHE_NAME);
+ node2.getOrCreateCache(
+ cacheConfiguration(KeyClass.class, ValueClass.class, KeyClass2.class, ValueClass2.class));
+
+ assertTypes(node1, ValueClass.class, ValueClass2.class);
+ assertTypes(node2, ValueClass.class, ValueClass2.class);
+ assertTypes(node3, ValueClass.class, ValueClass2.class);
+ assertTypes(node4, ValueClass.class, ValueClass2.class);
+ assertTypes(node5, ValueClass.class, ValueClass2.class);
+
+ assertTypes(node6);
+ assertTypes(node7);
+ assertTypes(node8);
+
+ node6.cache(CACHE_NAME);
+ node7.cache(CACHE_NAME);
+ node8.cache(CACHE_NAME);
+
+ assertTypes(node6, ValueClass.class, ValueClass2.class);
+ assertTypes(node7, ValueClass.class, ValueClass2.class);
+ assertTypes(node8, ValueClass.class, ValueClass2.class);
+ }
+
+ /**
+ * Test behavior when node filter is set.
+ *
+ * @throws Exception If failed.
+ */
+ public void testNodeFilter() throws Exception {
+ filterNodeName = getTestIgniteInstanceName(1);
+
+ IgniteEx node1 = start(1, KeyClass.class, ValueClass.class);
+ assertTypes(node1, ValueClass.class);
+
+ IgniteEx node2 = start(2, KeyClass.class, ValueClass.class);
+ assertTypes(node1, ValueClass.class);
+ assertTypes(node2, ValueClass.class);
+
+ IgniteEx node3 = startNoCache(3);
+ assertTypes(node1, ValueClass.class);
+ assertTypes(node2, ValueClass.class);
+
+ assertTypes(node3);
+
+ node3.cache(CACHE_NAME);
+ assertTypes(node3, ValueClass.class);
+ }
+
+ /**
+ * Test client reconnect.
+ *
+ * @throws Exception If failed.
+ */
+ public void testClientReconnect() throws Exception {
+ IgniteEx node1 = start(1, KeyClass.class, ValueClass.class);
+ assertTypes(node1, ValueClass.class);
+
+ IgniteEx node2 = startClientNoCache(2);
+ assertTypes(node2);
+
+ node2.cache(CACHE_NAME);
+ assertTypes(node2, ValueClass.class);
+
+ stopGrid(1);
+
+ assert GridTestUtils.waitForCondition(new GridAbsPredicate() {
+ @Override public boolean apply() {
+ return grid(2).context().clientDisconnected();
+ }
+ }, 10_000L);
+
+ IgniteFuture reconnFut = null;
+
+ try {
+ node2.cache(CACHE_NAME);
+
+ fail();
+ }
+ catch (IgniteClientDisconnectedException e) {
+ reconnFut = e.reconnectFuture();
+ }
+
+ node1 = start(1, KeyClass.class, ValueClass.class, KeyClass2.class, ValueClass2.class);
+ assertTypes(node1, ValueClass.class, ValueClass2.class);
+
+ reconnFut.get();
+
+ assertTypes(node2);
+
+ node2.cache(CACHE_NAME);
+ assertTypes(node2, ValueClass.class, ValueClass2.class);
+ }
+
+ /**
+ * Ensure that only provided types exists for the given cache.
+ *
+ * @param node Node.
+ * @param clss Classes.
+ */
+ private static void assertTypes(IgniteEx node, Class... clss) {
+ Map<String, QueryTypeDescriptorImpl> types = types(node, CACHE_NAME);
+
+ if (clss == null || clss.length == 0)
+ assert types.isEmpty();
+ else {
+ assertEquals(clss.length, types.size());
+
+ for (Class cls : clss) {
+ String tblName = tableName(cls);
+
+ assert types.containsKey(tblName);
+ }
+ }
+ }
+
+ /**
+ * Start node with the given cache configuration.
+ *
+ * @param clss Key-value classes.
+ * @return Node.
+ */
+ private IgniteEx start(int idx, Class... clss) throws Exception {
+ return start(idx, false, clss);
+ }
+
+ /**
+ * Start client node with the given cache configuration.
+ *
+ * @param clss Key-value classes.
+ * @return Node.
+ */
+ private IgniteEx startClient(int idx, Class... clss) throws Exception {
+ return start(idx, true, clss);
+ }
+
+ /**
+ * Start node with the given cache configuration.
+ *
+ * @param idx Index.
+ * @param client Client flag.
+ * @param clss Key-value classes.
+ * @return Node.
+ */
+ private IgniteEx start(int idx, boolean client, Class... clss) throws Exception {
+ String name = getTestIgniteInstanceName(idx);
+
+ IgniteConfiguration cfg = getConfiguration(name);
+
+ cfg.setClientMode(client);
+ cfg.setLocalHost("127.0.0.1");
+ cfg.setCacheConfiguration(cacheConfiguration(clss));
+
+ if (filterNodeName != null && F.eq(name, filterNodeName))
+ cfg.setUserAttributes(Collections.singletonMap("AFF_NODE", true));
+
+ return (IgniteEx)Ignition.start(cfg);
+ }
+
+ /**
+ * Start node without cache.
+ *
+ * @param idx Index.
+ * @return Node.
+ * @throws Exception If failed.
+ */
+ private IgniteEx startNoCache(int idx) throws Exception {
+ return startNoCache(idx, false);
+ }
+
+ /**
+ * Start client node without cache.
+ *
+ * @param idx Index.
+ * @return Node.
+ * @throws Exception If failed.
+ */
+ private IgniteEx startClientNoCache(int idx) throws Exception {
+ return startNoCache(idx, true);
+ }
+
+ /**
+ * Start node without cache.
+ *
+ * @param idx Index.
+ * @param client Client mode flag.
+ * @return Node.
+ * @throws Exception If failed.
+ */
+ private IgniteEx startNoCache(int idx, boolean client) throws Exception {
+ String name = getTestIgniteInstanceName(idx);
+
+ IgniteConfiguration cfg = getConfiguration(name);
+
+ cfg.setClientMode(client);
+ cfg.setLocalHost("127.0.0.1");
+
+ return (IgniteEx)Ignition.start(cfg);
+ }
+
+ /**
+ * Get cache configuration.
+ *
+ * @param clss QUery classes.
+ * @return Configuration.
+ */
+ @SuppressWarnings("unchecked")
+ private static CacheConfiguration cacheConfiguration(Class... clss) {
+ CacheConfiguration ccfg = new CacheConfiguration().setName(CACHE_NAME).setIndexedTypes(clss);
+
+ if (filterNodeName != null) {
+ ccfg.setNodeFilter(new IgnitePredicate<ClusterNode>() {
+ @Override public boolean apply(ClusterNode node) {
+ return node.attribute("AFF_NODE") != null;
+ }
+ });
+ }
+
+ return ccfg;
+ }
+
+ // TODO: Start/stop many nodes with static configs and dynamic start/stop.
+
+ /**
+ * Key class 2.
+ */
+ @SuppressWarnings("unused")
+ private static class KeyClass2 {
+ @QuerySqlField
+ private String keyField2;
+ }
+
+ /**
+ * Value class 2.
+ */
+ @SuppressWarnings("unused")
+ private static class ValueClass2 {
+ @QuerySqlField
+ private String valField2;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/local/IgniteCacheLocalQuerySelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/local/IgniteCacheLocalQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/local/IgniteCacheLocalQuerySelfTest.java
index 6013b80..e0c6396 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/local/IgniteCacheLocalQuerySelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/local/IgniteCacheLocalQuerySelfTest.java
@@ -85,6 +85,6 @@ public class IgniteCacheLocalQuerySelfTest extends IgniteCacheAbstractQuerySelfT
List<List<?>> res = cache.query(new SqlFieldsQuery(
"explain select _key from String where _val > 'value1'").setLocal(true)).getAll();
- assertTrue("__ explain: \n" + res, ((String)res.get(0).get(0)).contains("_val_idx"));
+ assertTrue("__ explain: \n" + res, ((String)res.get(0).get(0)).toLowerCase().contains("_val_idx"));
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteQueryDedicatedPoolTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteQueryDedicatedPoolTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteQueryDedicatedPoolTest.java
index 711db2f..fe966f9 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteQueryDedicatedPoolTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteQueryDedicatedPoolTest.java
@@ -83,7 +83,6 @@ public class IgniteQueryDedicatedPoolTest extends GridCommonAbstractTest {
if ("client".equals(gridName))
cfg.setClientMode(true);
-
cfg.setIndexingSpi(new TestIndexingSpi());
return cfg;
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSplitterSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSplitterSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSplitterSelfTest.java
index b180eba..2a03796 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSplitterSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlSplitterSelfTest.java
@@ -413,7 +413,7 @@ public class IgniteSqlSplitterSelfTest extends GridCommonAbstractTest {
info("Plan: " + plan);
- assertTrue(plan.contains("grpIdx"));
+ assertTrue("_explain: " + plan, plan.toLowerCase().contains("grpidx"));
// Sorted list
List<GroupIndexTestValue> list = F.asList(
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java
index 1c30412..e62199a 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java
@@ -87,7 +87,7 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
return cfg;
}
- /**
+ /*
* Fields initialization.
*/
static {
@@ -246,22 +246,22 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
public void testSpi() throws Exception {
IgniteH2Indexing spi = getIndexing();
- assertEquals(-1, spi.size(typeAA.space(), typeAA));
- assertEquals(-1, spi.size(typeAB.space(), typeAB));
- assertEquals(-1, spi.size(typeBA.space(), typeBA));
+ assertEquals(-1, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(-1, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(-1, spi.size(typeBA.space(), typeBA.name()));
IgniteCache<Integer, BinaryObject> cacheA = ignite0.createCache(cacheACfg());
- assertEquals(0, spi.size(typeAA.space(), typeAA));
- assertEquals(0, spi.size(typeAB.space(), typeAB));
- assertEquals(-1, spi.size(typeBA.space(), typeBA));
+ assertEquals(0, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(0, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(-1, spi.size(typeBA.space(), typeBA.name()));
IgniteCache<Integer, BinaryObject> cacheB = ignite0.createCache(cacheBCfg());
// Initially all is empty.
- assertEquals(0, spi.size(typeAA.space(), typeAA));
- assertEquals(0, spi.size(typeAB.space(), typeAB));
- assertEquals(0, spi.size(typeBA.space(), typeBA));
+ assertEquals(0, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(0, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(0, spi.size(typeBA.space(), typeBA.name()));
assertFalse(spi.queryLocalSql(typeAA.space(), "select * from A.A", null, Collections.emptySet(), typeAA.name(), null, null).hasNext());
assertFalse(spi.queryLocalSql(typeAB.space(), "select * from A.B", null, Collections.emptySet(), typeAB.name(), null, null).hasNext());
@@ -288,48 +288,48 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
cacheA.put(1, aa("A", 1, "Vasya", 10).build());
- assertEquals(1, spi.size(typeAA.space(), typeAA));
- assertEquals(0, spi.size(typeAB.space(), typeAB));
- assertEquals(0, spi.size(typeBA.space(), typeBA));
+ assertEquals(1, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(0, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(0, spi.size(typeBA.space(), typeBA.name()));
cacheA.put(1, ab(1, "Vasya", 20, "Some text about Vasya goes here.").build());
// In one space all keys must be unique.
- assertEquals(0, spi.size(typeAA.space(), typeAA));
- assertEquals(1, spi.size(typeAB.space(), typeAB));
- assertEquals(0, spi.size(typeBA.space(), typeBA));
+ assertEquals(0, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(1, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(0, spi.size(typeBA.space(), typeBA.name()));
cacheB.put(1, ba(2, "Petya", 25, true).build());
// No replacement because of different space.
- assertEquals(0, spi.size(typeAA.space(), typeAA));
- assertEquals(1, spi.size(typeAB.space(), typeAB));
- assertEquals(1, spi.size(typeBA.space(), typeBA));
+ assertEquals(0, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(1, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(1, spi.size(typeBA.space(), typeBA.name()));
cacheB.put(1, ba(2, "Kolya", 25, true).build());
// Replacement in the same table.
- assertEquals(0, spi.size(typeAA.space(), typeAA));
- assertEquals(1, spi.size(typeAB.space(), typeAB));
- assertEquals(1, spi.size(typeBA.space(), typeBA));
+ assertEquals(0, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(1, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(1, spi.size(typeBA.space(), typeBA.name()));
cacheA.put(2, aa("A", 2, "Valera", 19).build());
- assertEquals(1, spi.size(typeAA.space(), typeAA));
- assertEquals(1, spi.size(typeAB.space(), typeAB));
- assertEquals(1, spi.size(typeBA.space(), typeBA));
+ assertEquals(1, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(1, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(1, spi.size(typeBA.space(), typeBA.name()));
cacheA.put(3, aa("A", 3, "Borya", 18).build());
- assertEquals(2, spi.size(typeAA.space(), typeAA));
- assertEquals(1, spi.size(typeAB.space(), typeAB));
- assertEquals(1, spi.size(typeBA.space(), typeBA));
+ assertEquals(2, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(1, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(1, spi.size(typeBA.space(), typeBA.name()));
cacheA.put(4, ab(4, "Vitalya", 20, "Very Good guy").build());
- assertEquals(2, spi.size(typeAA.space(), typeAA));
- assertEquals(2, spi.size(typeAB.space(), typeAB));
- assertEquals(1, spi.size(typeBA.space(), typeBA));
+ assertEquals(2, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(2, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(1, spi.size(typeBA.space(), typeBA.name()));
// Query data.
Iterator<IgniteBiTuple<Integer, BinaryObjectImpl>> res =
@@ -376,7 +376,7 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
// Text queries
Iterator<IgniteBiTuple<Integer, BinaryObjectImpl>> txtRes = spi.queryLocalText(typeAB.space(), "good",
- typeAB, null);
+ typeAB.name(), null);
assertTrue(txtRes.hasNext());
assertEquals(ab(4, "Vitalya", 20, "Very Good guy").build(), value(txtRes.next()));
@@ -410,32 +410,32 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
// Remove
cacheA.remove(2);
- assertEquals(1, spi.size(typeAA.space(), typeAA));
- assertEquals(2, spi.size(typeAB.space(), typeAB));
- assertEquals(1, spi.size(typeBA.space(), typeBA));
+ assertEquals(1, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(2, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(1, spi.size(typeBA.space(), typeBA.name()));
cacheB.remove(1);
- assertEquals(1, spi.size(typeAA.space(), typeAA));
- assertEquals(2, spi.size(typeAB.space(), typeAB));
- assertEquals(0, spi.size(typeBA.space(), typeBA));
+ assertEquals(1, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(2, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(0, spi.size(typeBA.space(), typeBA.name()));
// Unregister.
- spi.unregisterType(typeAA.space(), typeAA);
+ spi.unregisterType(typeAA.space(), typeAA.name());
- assertEquals(-1, spi.size(typeAA.space(), typeAA));
- assertEquals(2, spi.size(typeAB.space(), typeAB));
- assertEquals(0, spi.size(typeBA.space(), typeBA));
+ assertEquals(-1, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(2, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(0, spi.size(typeBA.space(), typeBA.name()));
- spi.unregisterType(typeAB.space(), typeAB);
+ spi.unregisterType(typeAB.space(), typeAB.name());
- assertEquals(-1, spi.size(typeAA.space(), typeAA));
- assertEquals(-1, spi.size(typeAB.space(), typeAB));
- assertEquals(0, spi.size(typeBA.space(), typeBA));
+ assertEquals(-1, spi.size(typeAA.space(), typeAA.name()));
+ assertEquals(-1, spi.size(typeAB.space(), typeAB.name()));
+ assertEquals(0, spi.size(typeBA.space(), typeBA.name()));
- spi.unregisterType(typeBA.space(), typeBA);
+ spi.unregisterType(typeBA.space(), typeBA.name());
- assertEquals(-1, spi.size(typeAA.space(), typeAA));
+ assertEquals(-1, spi.size(typeAA.space(), typeAA.name()));
}
/**
@@ -500,6 +500,11 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
}
/** {@inheritDoc} */
+ @Override public String name() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
@Override public Collection<String> fields() {
return fields;
}
@@ -640,8 +645,12 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
/** */
@Override public Map<String, GridQueryIndexDescriptor> indexes() {
- return textIdx == null ? Collections.<String, GridQueryIndexDescriptor>emptyMap() :
- Collections.singletonMap("index", textIdx);
+ return Collections.emptyMap();
+ }
+
+ /** */
+ @Override public GridQueryIndexDescriptor textIndex() {
+ return textIdx;
}
/** */
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/IgniteSqlQueryMinMaxTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/IgniteSqlQueryMinMaxTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/IgniteSqlQueryMinMaxTest.java
index 0f50d7e..e78b695 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/IgniteSqlQueryMinMaxTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/IgniteSqlQueryMinMaxTest.java
@@ -142,8 +142,8 @@ public class IgniteSqlQueryMinMaxTest extends GridCommonAbstractTest {
QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("explain select min(_key), max(_key) from ValueObj"));
List<List<?>> result = cursor.getAll();
assertEquals(2, result.size());
- assertTrue(((String) result.get(0).get(0)).contains("_key_PK"));
- assertTrue(((String) result.get(0).get(0)).contains("direct lookup"));
+ assertTrue(((String) result.get(0).get(0)).toLowerCase().contains("_key_pk"));
+ assertTrue(((String) result.get(0).get(0)).toLowerCase().contains("direct lookup"));
}
}
@@ -158,8 +158,8 @@ public class IgniteSqlQueryMinMaxTest extends GridCommonAbstractTest {
QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("explain select min(idxVal), max(idxVal) from ValueObj"));
List<List<?>> result = cursor.getAll();
assertEquals(2, result.size());
- assertTrue(((String)result.get(0).get(0)).contains("idxVal_idx"));
- assertTrue(((String)result.get(0).get(0)).contains("direct lookup"));
+ assertTrue(((String)result.get(0).get(0)).toLowerCase().contains("idxval_idx"));
+ assertTrue(((String)result.get(0).get(0)).toLowerCase().contains("direct lookup"));
}
}
@@ -174,8 +174,8 @@ public class IgniteSqlQueryMinMaxTest extends GridCommonAbstractTest {
QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("explain select min(_key), max(_key) from Integer"));
List<List<?>> result = cursor.getAll();
assertEquals(2, result.size());
- assertTrue(((String)result.get(0).get(0)).contains("_key_PK"));
- assertTrue(((String)result.get(0).get(0)).contains("direct lookup"));
+ assertTrue(((String)result.get(0).get(0)).toLowerCase().contains("_key_pk"));
+ assertTrue(((String)result.get(0).get(0)).toLowerCase().contains("direct lookup"));
}
}
@@ -190,8 +190,8 @@ public class IgniteSqlQueryMinMaxTest extends GridCommonAbstractTest {
QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("explain select min(_val), max(_val) from Integer"));
List<List<?>> result = cursor.getAll();
assertEquals(2, result.size());
- assertTrue(((String)result.get(0).get(0)).contains("_val_idx"));
- assertTrue(((String)result.get(0).get(0)).contains("direct lookup"));
+ assertTrue(((String)result.get(0).get(0)).toLowerCase().contains("_val_idx"));
+ assertTrue(((String)result.get(0).get(0)).toLowerCase().contains("direct lookup"));
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java
index 4bd9302..88ff61e 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java
@@ -79,7 +79,7 @@ public class GridH2TableSelfTest extends GridCommonAbstractTest {
private static final String NON_UNIQUE_IDX_NAME = "__GG_IDX_";
/** */
- private static final String SCAN_IDX_NAME = GridH2Table.ScanIndex.SCAN_INDEX_NAME_SUFFIX;
+ private static final String SCAN_IDX_NAME = GridH2PrimaryScanIndex.SCAN_INDEX_NAME_SUFFIX;
/** */
private Connection conn;
@@ -89,31 +89,36 @@ public class GridH2TableSelfTest extends GridCommonAbstractTest {
/** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
- Driver.load();
-
- conn = DriverManager.getConnection(DB_URL);
-
- tbl = GridH2Table.Engine.createTable(conn, CREATE_TABLE_SQL, null, new GridH2Table.IndexesFactory() {
- @Override public H2RowFactory createRowFactory(GridH2Table tbl) {
- return null;
- }
-
- @Override public ArrayList<Index> createIndexes(GridH2Table tbl) {
- ArrayList<Index> idxs = new ArrayList<>();
-
- IndexColumn id = tbl.indexColumn(0, SortOrder.ASCENDING);
- IndexColumn t = tbl.indexColumn(1, SortOrder.ASCENDING);
- IndexColumn str = tbl.indexColumn(2, SortOrder.DESCENDING);
- IndexColumn x = tbl.indexColumn(3, SortOrder.DESCENDING);
-
- idxs.add(new H2PkHashIndex(null, tbl, HASH, F.asList(id)));
- idxs.add(new GridH2TreeIndex(PK_NAME, tbl, true, F.asList(id)));
- idxs.add(new GridH2TreeIndex(NON_UNIQUE_IDX_NAME, tbl, false, F.asList(x, t, id)));
- idxs.add(new GridH2TreeIndex(STR_IDX_NAME, tbl, false, F.asList(str, id)));
-
- return idxs;
- }
- }, null);
+ // TODO: IGNITE-4994: Restore mock.
+// Driver.load();
+//
+// conn = DriverManager.getConnection(DB_URL);
+//
+// tbl = GridH2Table.Engine.createTable(conn, CREATE_TABLE_SQL, null, new GridH2Table.IndexesFactory() {
+// @Override public void onTableCreated(GridH2Table tbl) {
+// // No-op.
+// }
+//
+// @Override public H2RowFactory createRowFactory(GridH2Table tbl) {
+// return null;
+// }
+//
+// @Override public ArrayList<Index> createIndexes(GridH2Table tbl) {
+// ArrayList<Index> idxs = new ArrayList<>();
+//
+// IndexColumn id = tbl.indexColumn(0, SortOrder.ASCENDING);
+// IndexColumn t = tbl.indexColumn(1, SortOrder.ASCENDING);
+// IndexColumn str = tbl.indexColumn(2, SortOrder.DESCENDING);
+// IndexColumn x = tbl.indexColumn(3, SortOrder.DESCENDING);
+//
+// idxs.add(new H2PkHashIndex(null, tbl, HASH, F.asList(id)));
+// idxs.add(new GridH2TreeIndex(PK_NAME, tbl, true, F.asList(id)));
+// idxs.add(new GridH2TreeIndex(NON_UNIQUE_IDX_NAME, tbl, false, F.asList(x, t, id)));
+// idxs.add(new GridH2TreeIndex(STR_IDX_NAME, tbl, false, F.asList(str, id)));
+//
+// return idxs;
+// }
+// }, null);
}
/** {@inheritDoc} */
@@ -285,120 +290,6 @@ public class GridH2TableSelfTest extends GridCommonAbstractTest {
}
/**
- * Multithreaded indexes consistency test.
- *
- * @throws Exception If failed.
- */
- public void testIndexesMultiThreadedConsistency() throws Exception {
- fail("https://issues.apache.org/jira/browse/IGNITE-3484");
-
- final int threads = 19;
- final int iterations = 1500;
-
- multithreaded(new Callable<Void>() {
- @Override public Void call() throws Exception {
- Random rnd = new Random();
-
- PreparedStatement ps1 = null;
-
- for (int i = 0; i < iterations; i++) {
- UUID id = UUID.randomUUID();
-
- int x = rnd.nextInt(50);
-
- long t = System.currentTimeMillis();
-
- GridH2Row row = row(id, t, rnd.nextBoolean() ? id.toString() : UUID.randomUUID().toString(), x);
-
- assertTrue(tbl.doUpdate(row, false));
-
- if (rnd.nextInt(100) == 0) {
- tbl.lock(null, false, false);
-
- long cnt = 0;
-
- try {
- ArrayList<Index> idxs = tbl.getIndexes();
-
- // Consistency check.
- Set<Row> rowSet = checkIndexesConsistent(idxs, null);
-
- // Order check.
- checkOrdered(idxs);
-
- checkIndexesConsistent(idxs, rowSet);
-
- cnt = idxs.get(0).getRowCount(null);
- }
- finally {
- tbl.unlock(null);
- }
-
- // Row count is valid.
- ResultSet rs = conn.createStatement().executeQuery("select count(*) from t");
-
- assertTrue(rs.next());
-
- int cnt2 = rs.getInt(1);
-
- rs.close();
-
- assertTrue(cnt2 + " must be >= " + cnt, cnt2 >= cnt);
- assertTrue(cnt2 <= threads * iterations);
-
- // Search by ID.
- rs = conn.createStatement().executeQuery("select * from t where id = '" + id.toString() + "'");
-
- assertTrue(rs.next());
- assertFalse(rs.next());
-
- rs.close();
-
- // Scan search.
- if (ps1 == null)
- ps1 = conn.prepareStatement("select id from t where x = ? order by t desc");
-
- ps1.setInt(1, x);
-
- rs = ps1.executeQuery();
-
- for (;;) {
- assertTrue(rs.next());
-
- if (rs.getObject(1).equals(id))
- break;
- }
-
- rs.close();
- }
- }
- return null;
- }
- }, threads);
- }
-
- /**
- * Run test in endless loop.
- *
- * @param args Arguments.
- * @throws Exception If failed.
- */
- @SuppressWarnings("InfiniteLoopStatement")
- public static void main(String ... args) throws Exception {
- for (int i = 0;;) {
- GridH2TableSelfTest t = new GridH2TableSelfTest();
-
- t.beforeTest();
-
- t.testDataLoss();
-
- t.afterTest();
-
- System.out.println("..." + ++i);
- }
- }
-
- /**
* @throws Exception If failed.
*/
public void testRangeQuery() throws Exception {
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
index 1d8893e..631adeb 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
@@ -22,9 +22,15 @@ import java.sql.Connection;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
import org.apache.ignite.Ignite;
import org.apache.ignite.cache.CacheAtomicityMode;
import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.QueryIndexType;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
import org.apache.ignite.cache.query.annotations.QuerySqlFunction;
import org.apache.ignite.configuration.CacheConfiguration;
@@ -32,15 +38,19 @@ import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
+import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.h2.command.Prepared;
import org.h2.engine.Session;
import org.h2.jdbc.JdbcConnection;
+import org.h2.message.DbException;
import static org.apache.ignite.cache.CacheRebalanceMode.SYNC;
import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
@@ -453,6 +463,206 @@ public class GridQueryParsingTest extends GridCommonAbstractTest {
/**
*
*/
+ public void testParseCreateIndex() throws Exception {
+ assertCreateIndexEquals(
+ buildCreateIndex(null, "Person", "sch1", false, QueryIndexType.SORTED, "name", true),
+ "create index on Person (name)");
+
+ assertCreateIndexEquals(
+ buildCreateIndex("idx", "Person", "sch1", false, QueryIndexType.SORTED, "name", true),
+ "create index idx on Person (name ASC)");
+
+ assertCreateIndexEquals(
+ buildCreateIndex("idx", "Person", "sch1", false, QueryIndexType.GEOSPATIAL, "name", true),
+ "create spatial index sch1.idx on sch1.Person (name ASC)");
+
+ assertCreateIndexEquals(
+ buildCreateIndex("idx", "Person", "sch1", true, QueryIndexType.SORTED, "name", true),
+ "create index if not exists sch1.idx on sch1.Person (name)");
+
+ // When we specify schema for the table and don't specify it for the index, resulting schema is table's
+ assertCreateIndexEquals(
+ buildCreateIndex("idx", "Person", "sch1", true, QueryIndexType.SORTED, "name", false),
+ "create index if not exists idx on sch1.Person (name dEsC)");
+
+ assertCreateIndexEquals(
+ buildCreateIndex("idx", "Person", "sch1", true, QueryIndexType.GEOSPATIAL, "old", true, "name", false),
+ "create spatial index if not exists idx on Person (old, name desc)");
+
+ // Schemas for index and table must match
+ assertParseThrows("create index if not exists sch2.idx on sch1.Person (name)",
+ DbException.class, "Schema name must match [90080-191]");
+
+ assertParseThrows("create hash index if not exists idx on Person (name)",
+ IgniteSQLException.class, "Only SPATIAL modifier is supported for CREATE INDEX");
+
+ assertParseThrows("create unique index if not exists idx on Person (name)",
+ IgniteSQLException.class, "Only SPATIAL modifier is supported for CREATE INDEX");
+
+ assertParseThrows("create primary key on Person (name)",
+ IgniteSQLException.class, "Only SPATIAL modifier is supported for CREATE INDEX");
+
+ assertParseThrows("create primary key hash on Person (name)",
+ IgniteSQLException.class, "Only SPATIAL modifier is supported for CREATE INDEX");
+
+ assertParseThrows("create index on Person (name nulls first)",
+ IgniteSQLException.class, "NULLS FIRST and NULLS LAST modifiers are not supported for index columns");
+
+ assertParseThrows("create index on Person (name desc nulls last)",
+ IgniteSQLException.class, "NULLS FIRST and NULLS LAST modifiers are not supported for index columns");
+ }
+
+ /**
+ *
+ */
+ public void testParseDropIndex() throws Exception {
+ // Schema that is not set defaults to default schema of connection which is empty string
+ assertDropIndexEquals(buildDropIndex("idx", "sch1", false), "drop index idx");
+ assertDropIndexEquals(buildDropIndex("idx", "sch1", true), "drop index if exists idx");
+ assertDropIndexEquals(buildDropIndex("idx", "sch1", true), "drop index if exists sch1.idx");
+ assertDropIndexEquals(buildDropIndex("idx", "sch1", false), "drop index sch1.idx");
+
+ // Message is null as long as it may differ from system to system, so we just check for exceptions
+ assertParseThrows("drop index schema2.", DbException.class, null);
+ assertParseThrows("drop index", DbException.class, null);
+ assertParseThrows("drop index if exists", DbException.class, null);
+ assertParseThrows("drop index if exists schema2.", DbException.class, null);
+ }
+
+ /**
+ * @param sql Statement.
+ * @param exCls Exception class.
+ * @param msg Expected message.
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ private void assertParseThrows(final String sql, Class<? extends Exception> exCls, String msg) {
+ GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ Prepared p = parse(sql);
+
+ return new GridSqlQueryParser(false).parse(p);
+ }
+ }, exCls, msg);
+ }
+
+ /**
+ * Parse SQL and compare it to expected instance.
+ */
+ private void assertCreateIndexEquals(GridSqlCreateIndex exp, String sql) throws Exception {
+ Prepared prepared = parse(sql);
+
+ GridSqlStatement stmt = new GridSqlQueryParser(false).parse(prepared);
+
+ assertTrue(stmt instanceof GridSqlCreateIndex);
+
+ assertCreateIndexEquals(exp, (GridSqlCreateIndex) stmt);
+ }
+
+ /**
+ * Parse SQL and compare it to expected instance of DROP INDEX.
+ */
+ private void assertDropIndexEquals(GridSqlDropIndex exp, String sql) throws Exception {
+ Prepared prepared = parse(sql);
+
+ GridSqlStatement stmt = new GridSqlQueryParser(false).parse(prepared);
+
+ assertTrue(stmt instanceof GridSqlDropIndex);
+
+ assertDropIndexEquals(exp, (GridSqlDropIndex) stmt);
+ }
+
+ /**
+ * Test two instances of {@link GridSqlDropIndex} for equality.
+ */
+ private static void assertDropIndexEquals(GridSqlDropIndex exp, GridSqlDropIndex actual) {
+ assertEqualsIgnoreCase(exp.name(), actual.name());
+ assertEqualsIgnoreCase(exp.schemaName(), actual.schemaName());
+ assertEquals(exp.ifExists(), actual.ifExists());
+ }
+
+ /**
+ *
+ */
+ private static GridSqlDropIndex buildDropIndex(String name, String schema, boolean ifExists) {
+ GridSqlDropIndex res = new GridSqlDropIndex();
+
+ res.name(name);
+ res.schemaName(schema);
+ res.ifExists(ifExists);
+
+ return res;
+ }
+
+ /**
+ * Test two instances of {@link GridSqlCreateIndex} for equality.
+ */
+ private static void assertCreateIndexEquals(GridSqlCreateIndex exp, GridSqlCreateIndex actual) {
+ assertEquals(exp.ifNotExists(), actual.ifNotExists());
+ assertEqualsIgnoreCase(exp.schemaName(), actual.schemaName());
+ assertEqualsIgnoreCase(exp.tableName(), actual.tableName());
+
+ assertEqualsIgnoreCase(exp.index().getName(), actual.index().getName());
+
+ Iterator<Map.Entry<String, Boolean>> expFldsIt = exp.index().getFields().entrySet().iterator();
+ Iterator<Map.Entry<String, Boolean>> actualFldsIt = actual.index().getFields().entrySet().iterator();
+
+ while (expFldsIt.hasNext()) {
+ assertTrue(actualFldsIt.hasNext());
+
+ Map.Entry<String, Boolean> expEntry = expFldsIt.next();
+ Map.Entry<String, Boolean> actualEntry = actualFldsIt.next();
+
+ assertEqualsIgnoreCase(expEntry.getKey(), actualEntry.getKey());
+ assertEquals(expEntry.getValue(), actualEntry.getValue());
+ }
+
+ assertFalse(actualFldsIt.hasNext());
+
+ assertEquals(exp.index().getIndexType(), actual.index().getIndexType());
+ }
+
+ /**
+ *
+ */
+ private static void assertEqualsIgnoreCase(String exp, String actual) {
+ assertEquals((exp == null), (actual == null));
+
+ if (exp != null)
+ assertTrue(exp.equalsIgnoreCase(actual));
+ }
+
+ /**
+ *
+ */
+ private static GridSqlCreateIndex buildCreateIndex(String name, String tblName, String schemaName, boolean ifNotExists,
+ QueryIndexType type, Object... flds) {
+ QueryIndex idx = new QueryIndex();
+
+ idx.setName(name);
+
+ assert !F.isEmpty(flds) && flds.length % 2 == 0;
+
+ LinkedHashMap<String, Boolean> trueFlds = new LinkedHashMap<>();
+
+ for (int i = 0; i < flds.length / 2; i++)
+ trueFlds.put((String)flds[i * 2], (Boolean)flds[i * 2 + 1]);
+
+ idx.setFields(trueFlds);
+ idx.setIndexType(type);
+
+ GridSqlCreateIndex res = new GridSqlCreateIndex();
+
+ res.schemaName(schemaName);
+ res.tableName(tblName);
+ res.ifNotExists(ifNotExists);
+ res.index(idx);
+
+ return res;
+ }
+
+ /**
+ *
+ */
private JdbcConnection connection() throws Exception {
GridKernalContext ctx = ((IgniteEx)ignite).context();
@@ -508,7 +718,7 @@ public class GridQueryParsingTest extends GridCommonAbstractTest {
System.out.println(normalizeSql(res));
- assertSqlEquals(prepared.getPlanSQL(), res);
+ assertSqlEquals(U.firstNotNull(prepared.getPlanSQL(), prepared.getSQL()), res);
}
@QuerySqlFunction
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
index de341d0..b61affe 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
@@ -81,6 +81,18 @@ import org.apache.ignite.internal.processors.cache.distributed.replicated.Ignite
import org.apache.ignite.internal.processors.cache.distributed.replicated.IgniteCacheReplicatedFieldsQuerySelfTest;
import org.apache.ignite.internal.processors.cache.distributed.replicated.IgniteCacheReplicatedQueryP2PDisabledSelfTest;
import org.apache.ignite.internal.processors.cache.distributed.replicated.IgniteCacheReplicatedQuerySelfTest;
+import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexAtomicPartitionedNearSelfTest;
+import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexAtomicPartitionedSelfTest;
+import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexAtomicReplicatedSelfTest;
+import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexTransactionalPartitionedNearSelfTest;
+import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexTransactionalPartitionedSelfTest;
+import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexTransactionalReplicatedSelfTest;
+import org.apache.ignite.internal.processors.cache.index.DynamicIndexClientBasicSelfTest;
+import org.apache.ignite.internal.processors.cache.index.DynamicIndexServerCoordinatorBasicSelfTest;
+import org.apache.ignite.internal.processors.cache.index.DynamicIndexServerNodeFIlterBasicSelfTest;
+import org.apache.ignite.internal.processors.cache.index.DynamicIndexServerNodeFilterCoordinatorBasicSelfTest;
+import org.apache.ignite.internal.processors.cache.index.DynamicIndexServerBasicSelfTest;
+import org.apache.ignite.internal.processors.cache.index.SchemaExchangeSelfTest;
import org.apache.ignite.internal.processors.cache.local.IgniteCacheLocalAtomicQuerySelfTest;
import org.apache.ignite.internal.processors.cache.local.IgniteCacheLocalFieldsQuerySelfTest;
import org.apache.ignite.internal.processors.cache.local.IgniteCacheLocalQueryCancelOrTimeoutSelfTest;
@@ -98,7 +110,6 @@ import org.apache.ignite.internal.processors.query.IgniteSqlSplitterSelfTest;
import org.apache.ignite.internal.processors.query.h2.GridH2IndexingInMemSelfTest;
import org.apache.ignite.internal.processors.query.h2.GridH2IndexingOffheapSelfTest;
import org.apache.ignite.internal.processors.query.h2.IgniteSqlQueryMinMaxTest;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2TableSelfTest;
import org.apache.ignite.internal.processors.query.h2.sql.BaseH2CompareQueryTest;
import org.apache.ignite.internal.processors.query.h2.sql.GridQueryParsingTest;
import org.apache.ignite.internal.processors.query.h2.sql.H2CompareBigQueryDistributedJoinsTest;
@@ -117,8 +128,20 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite {
public static TestSuite suite() throws Exception {
IgniteTestSuite suite = new IgniteTestSuite("Ignite Cache Queries Test Suite");
+ // Dynamic index create/drop tests.
+ suite.addTest(new TestSuite(SchemaExchangeSelfTest.class));
+
+ suite.addTest(new TestSuite(DynamicIndexServerCoordinatorBasicSelfTest.class));
+ suite.addTest(new TestSuite(DynamicIndexServerBasicSelfTest.class));
+ suite.addTest(new TestSuite(DynamicIndexServerNodeFilterCoordinatorBasicSelfTest.class));
+ suite.addTest(new TestSuite(DynamicIndexServerNodeFIlterBasicSelfTest.class));
+ suite.addTest(new TestSuite(DynamicIndexClientBasicSelfTest.class));
+
// H2 tests.
- suite.addTest(new TestSuite(GridH2TableSelfTest.class));
+
+ // TODO: IGNITE-4994: Restore mock.
+ // suite.addTest(new TestSuite(GridH2TableSelfTest.class));
+
suite.addTest(new TestSuite(GridH2IndexingInMemSelfTest.class));
suite.addTest(new TestSuite(GridH2IndexingOffheapSelfTest.class));
@@ -171,6 +194,7 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite {
suite.addTestSuite(IgniteCacheMultipleIndexedTypesTest.class);
+ // DML.
suite.addTestSuite(IgniteCacheMergeSqlQuerySelfTest.class);
suite.addTestSuite(IgniteCacheInsertSqlQuerySelfTest.class);
suite.addTestSuite(IgniteCacheUpdateSqlQuerySelfTest.class);
@@ -185,6 +209,14 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite {
suite.addTestSuite(IgniteCacheMultipleIndexedTypesTest.class);
suite.addTestSuite(IgniteSqlQueryMinMaxTest.class);
+ // DDL.
+ suite.addTestSuite(H2DynamicIndexTransactionalReplicatedSelfTest.class);
+ suite.addTestSuite(H2DynamicIndexTransactionalPartitionedSelfTest.class);
+ suite.addTestSuite(H2DynamicIndexTransactionalPartitionedNearSelfTest.class);
+ suite.addTestSuite(H2DynamicIndexAtomicReplicatedSelfTest.class);
+ suite.addTestSuite(H2DynamicIndexAtomicPartitionedSelfTest.class);
+ suite.addTestSuite(H2DynamicIndexAtomicPartitionedNearSelfTest.class);
+
// Fields queries.
suite.addTestSuite(SqlFieldsQuerySelfTest.class);
suite.addTestSuite(IgniteCacheLocalFieldsQuerySelfTest.class);
@@ -227,7 +259,6 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite {
suite.addTestSuite(IgniteCacheNoClassQuerySelfTest.class);
// Cancellation.
-
suite.addTestSuite(IgniteCacheDistributedQueryCancelSelfTest.class);
suite.addTestSuite(IgniteCacheLocalQueryCancelOrTimeoutSelfTest.class);
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java
index 416f150..5085cd5 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java
@@ -30,6 +30,10 @@ import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheD
import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheQueryNodeFailTest;
import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheQueryNodeRestartSelfTest;
import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheQueryNodeRestartSelfTest2;
+import org.apache.ignite.internal.processors.cache.index.DynamicIndexPartitionedAtomicConcurrentSelfTest;
+import org.apache.ignite.internal.processors.cache.index.DynamicIndexPartitionedTransactionalConcurrentSelfTest;
+import org.apache.ignite.internal.processors.cache.index.DynamicIndexReplicatedAtomicConcurrentSelfTest;
+import org.apache.ignite.internal.processors.cache.index.DynamicIndexReplicatedTransactionalConcurrentSelfTest;
import org.apache.ignite.testframework.IgniteTestSuite;
/**
@@ -43,6 +47,13 @@ public class IgniteCacheQuerySelfTestSuite2 extends TestSuite {
public static TestSuite suite() throws Exception {
TestSuite suite = new IgniteTestSuite("Ignite Cache Queries Test Suite 2");
+ // Dynamic index create/drop tests.
+ suite.addTestSuite(DynamicIndexPartitionedAtomicConcurrentSelfTest.class);
+ suite.addTestSuite(DynamicIndexPartitionedTransactionalConcurrentSelfTest.class);
+ suite.addTestSuite(DynamicIndexReplicatedAtomicConcurrentSelfTest.class);
+ suite.addTestSuite(DynamicIndexReplicatedTransactionalConcurrentSelfTest.class);
+
+ // Other tests.
suite.addTestSuite(IgniteCacheQueryMultiThreadedSelfTest.class);
suite.addTestSuite(IgniteCacheQueryEvictsMultiThreadedSelfTest.class);
[2/8] ignite git commit: IGNITE-4565: Implemented CREATE INDEX and
DROP INDEX. This closes #1773. This closes #1804.
Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractConcurrentSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractConcurrentSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractConcurrentSelfTest.java
new file mode 100644
index 0000000..d2a2f49
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractConcurrentSelfTest.java
@@ -0,0 +1,921 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.query.SqlQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.internal.processors.query.QueryIndexDescriptorImpl;
+import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitor;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.jetbrains.annotations.Nullable;
+
+import javax.cache.Cache;
+import java.util.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Concurrency tests for dynamic index create/drop.
+ */
+@SuppressWarnings("unchecked")
+public abstract class DynamicIndexAbstractConcurrentSelfTest extends DynamicIndexAbstractSelfTest {
+ /** Test duration. */
+ private static final long TEST_DUR = 10_000L;
+
+ /** Large cache size. */
+ private static final int LARGE_CACHE_SIZE = 100_000;
+
+ /** Latches to block certain index operations. */
+ private static final ConcurrentHashMap<UUID, T2<CountDownLatch, AtomicBoolean>> BLOCKS = new ConcurrentHashMap<>();
+
+ /** Cache mode. */
+ private final CacheMode cacheMode;
+
+ /** Atomicity mode. */
+ private final CacheAtomicityMode atomicityMode;
+
+ /**
+ * Constructor.
+ *
+ * @param cacheMode Cache mode.
+ * @param atomicityMode Atomicity mode.
+ */
+ protected DynamicIndexAbstractConcurrentSelfTest(CacheMode cacheMode, CacheAtomicityMode atomicityMode) {
+ this.cacheMode = cacheMode;
+ this.atomicityMode = atomicityMode;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ GridQueryProcessor.idxCls = BlockingIndexing.class;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ GridQueryProcessor.idxCls = null;
+
+ for (T2<CountDownLatch, AtomicBoolean> block : BLOCKS.values())
+ block.get1().countDown();
+
+ BLOCKS.clear();
+
+ stopAllGrids();
+
+ super.afterTest();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected long getTestTimeout() {
+ return 5 * 60 * 1000L;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected CacheConfiguration<KeyClass, ValueClass> cacheConfiguration() {
+ CacheConfiguration<KeyClass, ValueClass> ccfg = super.cacheConfiguration();
+
+ return ccfg.setCacheMode(cacheMode).setAtomicityMode(atomicityMode);
+ }
+
+ /**
+ * Make sure that coordinator migrates correctly between nodes.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCoordinatorChange() throws Exception {
+ // Start servers.
+ Ignite srv1 = Ignition.start(serverConfiguration(1));
+ Ignite srv2 = Ignition.start(serverConfiguration(2));
+ Ignition.start(serverConfiguration(3, true));
+ Ignition.start(serverConfiguration(4));
+
+ UUID srv1Id = srv1.cluster().localNode().id();
+ UUID srv2Id = srv2.cluster().localNode().id();
+
+ // Start client which will execute operations.
+ Ignite cli = Ignition.start(clientConfiguration(5));
+
+ cli.getOrCreateCache(cacheConfiguration());
+
+ put(srv1, 0, KEY_AFTER);
+
+ // Test migration between normal servers.
+ blockIndexing(srv1Id);
+
+ QueryIndex idx1 = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ IgniteInternalFuture<?> idxFut1 = queryProcessor(cli).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx1, false);
+
+ Thread.sleep(100);
+
+ //srv1.close();
+ Ignition.stop(srv1.name(), true);
+
+ unblockIndexing(srv1Id);
+
+ idxFut1.get();
+
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(FIELD_NAME_1));
+ assertIndexUsed(IDX_NAME_1, SQL_SIMPLE_FIELD_1, SQL_ARG_1);
+ assertSqlSimpleData(SQL_SIMPLE_FIELD_1, KEY_AFTER - SQL_ARG_1);
+
+ // Test migration from normal server to non-affinity server.
+ blockIndexing(srv2Id);
+
+ QueryIndex idx2 = index(IDX_NAME_2, field(alias(FIELD_NAME_2)));
+
+ IgniteInternalFuture<?> idxFut2 = queryProcessor(cli).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx2, false);
+
+ Thread.sleep(100);
+
+ //srv2.close();
+ Ignition.stop(srv2.name(), true);
+
+ unblockIndexing(srv2Id);
+
+ idxFut2.get();
+
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_2, field(alias(FIELD_NAME_2)));
+ assertIndexUsed(IDX_NAME_2, SQL_SIMPLE_FIELD_2, SQL_ARG_1);
+ assertSqlSimpleData(SQL_SIMPLE_FIELD_2, KEY_AFTER - SQL_ARG_1);
+ }
+
+ /**
+ * Test operations join.
+ *
+ * @throws Exception If failed.
+ */
+ public void testOperationChaining() throws Exception {
+ Ignite srv1 = Ignition.start(serverConfiguration(1));
+
+ Ignition.start(serverConfiguration(2));
+ Ignition.start(serverConfiguration(3, true));
+ Ignition.start(clientConfiguration(4));
+
+ srv1.getOrCreateCache(cacheConfiguration());
+
+ blockIndexing(srv1);
+
+ QueryIndex idx1 = index(IDX_NAME_1, field(FIELD_NAME_1));
+ QueryIndex idx2 = index(IDX_NAME_2, field(alias(FIELD_NAME_2)));
+
+ IgniteInternalFuture<?> idxFut1 = queryProcessor(srv1).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx1, false);
+ IgniteInternalFuture<?> idxFut2 = queryProcessor(srv1).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx2, false);
+
+ // Start even more nodes of different flavors
+ Ignition.start(serverConfiguration(5));
+ Ignition.start(serverConfiguration(6, true));
+ Ignition.start(clientConfiguration(7));
+
+ assert !idxFut1.isDone();
+ assert !idxFut2.isDone();
+
+ unblockIndexing(srv1);
+
+ idxFut1.get();
+ idxFut2.get();
+
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(FIELD_NAME_1));
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_2, field(alias(FIELD_NAME_2)));
+
+ Thread.sleep(100);
+
+ put(srv1, 0, KEY_AFTER);
+
+ assertIndexUsed(IDX_NAME_1, SQL_SIMPLE_FIELD_1, SQL_ARG_1);
+ assertIndexUsed(IDX_NAME_2, SQL_SIMPLE_FIELD_2, SQL_ARG_1);
+
+ assertSqlSimpleData(SQL_SIMPLE_FIELD_1, KEY_AFTER - SQL_ARG_1);
+ assertSqlSimpleData(SQL_SIMPLE_FIELD_2, KEY_AFTER - SQL_ARG_1);
+ }
+
+ /**
+ * Test node join on pending operation.
+ *
+ * @throws Exception If failed.
+ */
+ public void testNodeJoinOnPendingOperation() throws Exception {
+ Ignite srv1 = Ignition.start(serverConfiguration(1));
+
+ srv1.getOrCreateCache(cacheConfiguration());
+
+ blockIndexing(srv1);
+
+ QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ IgniteInternalFuture<?> idxFut = queryProcessor(srv1).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, false);
+
+ Ignition.start(serverConfiguration(2));
+ Ignition.start(serverConfiguration(3, true));
+ Ignition.start(clientConfiguration(4));
+
+ assert !idxFut.isDone();
+
+ unblockIndexing(srv1);
+
+ idxFut.get();
+
+ Thread.sleep(100L);
+
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(FIELD_NAME_1));
+
+ put(srv1, 0, KEY_AFTER);
+
+ assertIndexUsed(IDX_NAME_1, SQL_SIMPLE_FIELD_1, SQL_ARG_1);
+ assertSqlSimpleData(SQL_SIMPLE_FIELD_1, KEY_AFTER - SQL_ARG_1);
+ }
+
+ /**
+ * PUT/REMOVE data from cache and build index concurrently.
+ *
+ * @throws Exception If failed,
+ */
+ public void testConcurrentPutRemove() throws Exception {
+ // Start several nodes.
+ Ignite srv1 = Ignition.start(serverConfiguration(1));
+ Ignition.start(serverConfiguration(2));
+ Ignition.start(serverConfiguration(3));
+ Ignition.start(serverConfiguration(4));
+
+ awaitPartitionMapExchange();
+
+ IgniteCache<BinaryObject, BinaryObject> cache = srv1.createCache(cacheConfiguration()).withKeepBinary();
+
+ // Start data change operations from several threads.
+ final AtomicBoolean stopped = new AtomicBoolean();
+
+ IgniteInternalFuture updateFut = multithreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ while (!stopped.get()) {
+ Ignite node = grid(ThreadLocalRandom.current().nextInt(1, 5));
+
+ int key = ThreadLocalRandom.current().nextInt(0, LARGE_CACHE_SIZE);
+ int val = ThreadLocalRandom.current().nextInt();
+
+ BinaryObject keyObj = key(node, key);
+
+ if (ThreadLocalRandom.current().nextBoolean()) {
+ BinaryObject valObj = value(node, val);
+
+ node.cache(CACHE_NAME).put(keyObj, valObj);
+ }
+ else
+ node.cache(CACHE_NAME).remove(keyObj);
+ }
+
+ return null;
+ }
+ }, 4);
+
+ // Let some to arrive.
+ Thread.sleep(500L);
+
+ // Create index.
+ QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ queryProcessor(srv1).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, false).get();
+
+ // Stop updates once index is ready.
+ stopped.set(true);
+
+ updateFut.get();
+
+ // Make sure index is there.
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(FIELD_NAME_1));
+ assertIndexUsed(IDX_NAME_1, SQL_SIMPLE_FIELD_1, SQL_ARG_1);
+
+ // Get expected values.
+ Map<Long, Long> expKeys = new HashMap<>();
+
+ for (int i = 0; i < LARGE_CACHE_SIZE; i++) {
+ BinaryObject val = cache.get(key(srv1, i));
+
+ if (val != null) {
+ long fieldVal = val.field(FIELD_NAME_1);
+
+ if (fieldVal >= SQL_ARG_1)
+ expKeys.put((long)i, fieldVal);
+ }
+ }
+
+ // Validate query result.
+ for (Ignite node : Ignition.allGrids()) {
+ IgniteCache<BinaryObject, BinaryObject> nodeCache = node.cache(CACHE_NAME).withKeepBinary();
+
+ SqlQuery qry = new SqlQuery(tableName(ValueClass.class), SQL_SIMPLE_FIELD_1).setArgs(SQL_ARG_1);
+
+ List<Cache.Entry<BinaryObject, BinaryObject>> res = nodeCache.query(qry).getAll();
+
+ assertEquals("Cache size mismatch [exp=" + expKeys.size() + ", actual=" + res.size() + ']',
+ expKeys.size(), res.size());
+
+ for (Cache.Entry<BinaryObject, BinaryObject> entry : res) {
+ long key = entry.getKey().field(FIELD_KEY);
+ Long fieldVal = entry.getValue().field(FIELD_NAME_1);
+
+ assertTrue("Expected key is not in result set: " + key, expKeys.containsKey(key));
+
+ assertEquals("Unexpected value [key=" + key + ", expVal=" + expKeys.get(key) +
+ ", actualVal=" + fieldVal + ']', expKeys.get(key), fieldVal);
+ }
+
+ }
+ }
+
+ /**
+ * Test index consistency on re-balance.
+ *
+ * @throws Exception If failed.
+ */
+ public void testConcurrentRebalance() throws Exception {
+ // Start cache and populate it with data.
+ Ignite srv1 = Ignition.start(serverConfiguration(1));
+ Ignite srv2 = Ignition.start(serverConfiguration(2));
+
+ srv1.createCache(cacheConfiguration());
+
+ awaitPartitionMapExchange();
+
+ put(srv1, 0, LARGE_CACHE_SIZE);
+
+ // Start index operation in blocked state.
+ blockIndexing(srv1);
+ blockIndexing(srv2);
+
+ QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ final IgniteInternalFuture<?> idxFut =
+ queryProcessor(srv1).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, false);
+
+ Thread.sleep(100);
+
+ // Start two more nodes and unblock index operation in the middle.
+ Ignition.start(serverConfiguration(3));
+
+ unblockIndexing(srv1);
+ unblockIndexing(srv2);
+
+ Ignition.start(serverConfiguration(4));
+
+ awaitPartitionMapExchange();
+
+ // Validate index state.
+ idxFut.get();
+
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(FIELD_NAME_1));
+
+ assertIndexUsed(IDX_NAME_1, SQL_SIMPLE_FIELD_1, SQL_ARG_1);
+ assertSqlSimpleData(SQL_SIMPLE_FIELD_1, LARGE_CACHE_SIZE - SQL_ARG_1);
+ }
+
+ /**
+ * Check what happen in case cache is destroyed before operation is started.
+ *
+ * @throws Exception If failed.
+ */
+ public void testConcurrentCacheDestroy() throws Exception {
+ // Start complex topology.
+ Ignite srv1 = Ignition.start(serverConfiguration(1));
+
+ Ignition.start(serverConfiguration(2));
+ Ignition.start(serverConfiguration(3, true));
+
+ Ignite cli = Ignition.start(clientConfiguration(4));
+
+ // Start cache and populate it with data.
+ IgniteCache cache = cli.getOrCreateCache(cacheConfiguration());
+
+ put(cli, KEY_AFTER);
+
+ // Start index operation and block it on coordinator.
+ blockIndexing(srv1);
+
+ QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ final IgniteInternalFuture<?> idxFut =
+ queryProcessor(srv1).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, false);
+
+ Thread.sleep(100);
+
+ // Destroy cache.
+ cache.destroy();
+
+ // Unblock indexing and see what happens.
+ unblockIndexing(srv1);
+
+ try {
+ idxFut.get();
+
+ fail("Exception has not been thrown.");
+ }
+ catch (SchemaOperationException e) {
+ // No-op.
+ }
+ }
+
+ /**
+ * Make sure that contended operations on the same index from different nodes do not hang.
+ *
+ * @throws Exception If failed.
+ */
+ public void testConcurrentOperationsMultithreaded() throws Exception {
+ // Start complex topology.
+ Ignition.start(serverConfiguration(1));
+ Ignition.start(serverConfiguration(2));
+ Ignition.start(serverConfiguration(3, true));
+
+ Ignite cli = Ignition.start(clientConfiguration(4));
+
+ cli.createCache(cacheConfiguration());
+
+ final AtomicBoolean stopped = new AtomicBoolean();
+
+ // Start several threads which will mess around indexes.
+ final QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ IgniteInternalFuture idxFut = multithreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ boolean exists = false;
+
+ while (!stopped.get()) {
+ Ignite node = grid(ThreadLocalRandom.current().nextInt(1, 5));
+
+ IgniteInternalFuture fut;
+
+ if (exists) {
+ fut = queryProcessor(node).dynamicIndexDrop(CACHE_NAME, IDX_NAME_1, true);
+
+ exists = false;
+ }
+ else {
+ fut = queryProcessor(node).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, true);
+
+ exists = true;
+ }
+
+ try {
+ fut.get();
+ }
+ catch (SchemaOperationException e) {
+ // No-op.
+ }
+ catch (Exception e) {
+ fail("Unexpected exception: " + e);
+ }
+ }
+
+ return null;
+ }
+ }, 8);
+
+ Thread.sleep(TEST_DUR);
+
+ stopped.set(true);
+
+ // Make sure nothing hanged.
+ idxFut.get();
+
+ queryProcessor(cli).dynamicIndexDrop(CACHE_NAME, IDX_NAME_1, true).get();
+ queryProcessor(cli).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, true).get();
+
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(FIELD_NAME_1));
+
+ put(cli, 0, KEY_AFTER);
+
+ assertIndexUsed(IDX_NAME_1, SQL_SIMPLE_FIELD_1, SQL_ARG_1);
+ assertSqlSimpleData(SQL_SIMPLE_FIELD_1, KEY_AFTER - SQL_ARG_1);
+ }
+
+ /**
+ * Make sure that contended operations on the same index from different nodes do not hang when we issue both
+ * CREATE/DROP and SELECT statements.
+ *
+ * @throws Exception If failed.
+ */
+ public void testQueryConsistencyMultithreaded() throws Exception {
+ // Start complex topology.
+ Ignition.start(serverConfiguration(1));
+ Ignition.start(serverConfiguration(2));
+ Ignition.start(serverConfiguration(3, true));
+
+ Ignite cli = Ignition.start(clientConfiguration(4));
+
+ cli.createCache(cacheConfiguration());
+
+ put(cli, 0, KEY_AFTER);
+
+ final AtomicBoolean stopped = new AtomicBoolean();
+
+ // Thread which will mess around indexes.
+ final QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ IgniteInternalFuture idxFut = multithreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ boolean exists = false;
+
+ while (!stopped.get()) {
+ Ignite node = grid(ThreadLocalRandom.current().nextInt(1, 5));
+
+ IgniteInternalFuture fut;
+
+ if (exists) {
+ fut = queryProcessor(node).dynamicIndexDrop(CACHE_NAME, IDX_NAME_1, true);
+
+ exists = false;
+ }
+ else {
+ fut = queryProcessor(node).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, true);
+
+ exists = true;
+ }
+
+ try {
+ fut.get();
+ }
+ catch (SchemaOperationException e) {
+ // No-op.
+ }
+ catch (Exception e) {
+ fail("Unexpected exception: " + e);
+ }
+ }
+
+ return null;
+ }
+ }, 1);
+
+ IgniteInternalFuture qryFut = multithreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ while (!stopped.get()) {
+ Ignite node = grid(ThreadLocalRandom.current().nextInt(1, 5));
+
+ assertSqlSimpleData(node, SQL_SIMPLE_FIELD_1, KEY_AFTER - SQL_ARG_1);
+ }
+
+ return null;
+ }
+ }, 8);
+
+ Thread.sleep(TEST_DUR);
+
+ stopped.set(true);
+
+ // Make sure nothing hanged.
+ idxFut.get();
+ qryFut.get();
+ }
+
+ /**
+ * Test concurrent node start/stop along with index operations. Nothing should hang.
+ *
+ * @throws Exception If failed.
+ */
+ public void testConcurrentOperationsAndNodeStartStopMultithreaded() throws Exception {
+ // Start several stable nodes.
+ Ignition.start(serverConfiguration(1));
+ Ignition.start(serverConfiguration(2));
+ Ignition.start(serverConfiguration(3, true));
+
+ final Ignite cli = Ignition.start(clientConfiguration(4));
+
+ cli.createCache(cacheConfiguration());
+
+ final AtomicBoolean stopped = new AtomicBoolean();
+
+ // Start node start/stop worker.
+ final AtomicInteger nodeIdx = new AtomicInteger(4);
+
+ IgniteInternalFuture startStopFut = multithreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ boolean exists = false;
+
+ int lastIdx = 0;
+
+ while (!stopped.get()) {
+ if (exists) {
+ stopGrid(lastIdx);
+
+ exists = false;
+ }
+ else {
+ lastIdx = nodeIdx.incrementAndGet();
+
+ IgniteConfiguration cfg;
+
+ switch (ThreadLocalRandom.current().nextInt(0, 3)) {
+ case 1:
+ cfg = serverConfiguration(lastIdx, false);
+
+ break;
+
+ case 2:
+
+ cfg = serverConfiguration(lastIdx, true);
+
+ break;
+
+ default:
+ cfg = clientConfiguration(lastIdx);
+ }
+
+ Ignition.start(cfg);
+
+ exists = true;
+ }
+
+ Thread.sleep(ThreadLocalRandom.current().nextLong(500L, 1500L));
+ }
+
+ return null;
+ }
+ }, 1);
+
+ // Start several threads which will mess around indexes.
+ final QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ IgniteInternalFuture idxFut = multithreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ boolean exists = false;
+
+ while (!stopped.get()) {
+ Ignite node = grid(ThreadLocalRandom.current().nextInt(1, 5));
+
+ IgniteInternalFuture fut;
+
+ if (exists) {
+ fut = queryProcessor(node).dynamicIndexDrop(CACHE_NAME, IDX_NAME_1, true);
+
+ exists = false;
+ }
+ else {
+ fut = queryProcessor(node).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, true);
+
+ exists = true;
+ }
+
+ try {
+ fut.get();
+ }
+ catch (SchemaOperationException e) {
+ // No-op.
+ }
+ catch (Exception e) {
+ fail("Unexpected exception: " + e);
+ }
+ }
+
+ return null;
+ }
+ }, 1);
+
+ Thread.sleep(TEST_DUR);
+
+ stopped.set(true);
+
+ // Make sure nothing hanged.
+ startStopFut.get();
+ idxFut.get();
+
+ // Make sure cache is operational at this point.
+ cli.getOrCreateCache(cacheConfiguration());
+
+ queryProcessor(cli).dynamicIndexDrop(CACHE_NAME, IDX_NAME_1, true).get();
+ queryProcessor(cli).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, true).get();
+
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(FIELD_NAME_1));
+
+ put(cli, 0, KEY_AFTER);
+
+ assertIndexUsed(IDX_NAME_1, SQL_SIMPLE_FIELD_1, SQL_ARG_1);
+ assertSqlSimpleData(SQL_SIMPLE_FIELD_1, KEY_AFTER - SQL_ARG_1);
+ }
+
+ /**
+ * Multithreaded cache start/stop along with index operations. Nothing should hang.
+ *
+ * @throws Exception If failed.
+ */
+ public void testConcurrentOperationsAndCacheStartStopMultithreaded() throws Exception {
+ // Start complex topology.
+ Ignition.start(serverConfiguration(1));
+ Ignition.start(serverConfiguration(2));
+ Ignition.start(serverConfiguration(3, true));
+
+ Ignite cli = Ignition.start(clientConfiguration(4));
+
+ final AtomicBoolean stopped = new AtomicBoolean();
+
+ // Start cache create/destroy worker.
+ IgniteInternalFuture startStopFut = multithreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ boolean exists = false;
+
+ while (!stopped.get()) {
+ Ignite node = grid(ThreadLocalRandom.current().nextInt(1, 5));
+
+ if (exists) {
+ node.destroyCache(CACHE_NAME);
+
+ exists = false;
+ }
+ else {
+ node.createCache(cacheConfiguration());
+
+ exists = true;
+ }
+
+ Thread.sleep(ThreadLocalRandom.current().nextLong(200L, 400L));
+ }
+
+ return null;
+ }
+ }, 1);
+
+ // Start several threads which will mess around indexes.
+ final QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ IgniteInternalFuture idxFut = multithreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ boolean exists = false;
+
+ while (!stopped.get()) {
+ Ignite node = grid(ThreadLocalRandom.current().nextInt(1, 5));
+
+ IgniteInternalFuture fut;
+
+ if (exists) {
+ fut = queryProcessor(node).dynamicIndexDrop(CACHE_NAME, IDX_NAME_1, true);
+
+ exists = false;
+ }
+ else {
+ fut = queryProcessor(node).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, true);
+
+ exists = true;
+ }
+
+ try {
+ fut.get();
+ }
+ catch (SchemaOperationException e) {
+ // No-op.
+ }
+ catch (Exception e) {
+ fail("Unexpected exception: " + e);
+ }
+ }
+
+ return null;
+ }
+ }, 8);
+
+ Thread.sleep(TEST_DUR);
+
+ stopped.set(true);
+
+ // Make sure nothing hanged.
+ startStopFut.get();
+ idxFut.get();
+
+ // Make sure cache is operational at this point.
+ cli.getOrCreateCache(cacheConfiguration());
+
+ queryProcessor(cli).dynamicIndexDrop(CACHE_NAME, IDX_NAME_1, true).get();
+ queryProcessor(cli).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, true).get();
+
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(FIELD_NAME_1));
+
+ put(cli, 0, KEY_AFTER);
+
+ assertIndexUsed(IDX_NAME_1, SQL_SIMPLE_FIELD_1, SQL_ARG_1);
+ assertSqlSimpleData(SQL_SIMPLE_FIELD_1, KEY_AFTER - SQL_ARG_1);
+ }
+
+ /**
+ * Block indexing.
+ *
+ * @param node Node.
+ */
+ @SuppressWarnings("SuspiciousMethodCalls")
+ private static void blockIndexing(Ignite node) {
+ UUID nodeId = ((IgniteEx)node).localNode().id();
+
+ blockIndexing(nodeId);
+ }
+
+ /**
+ * Block indexing.
+ *
+ * @param nodeId Node.
+ */
+ @SuppressWarnings("SuspiciousMethodCalls")
+ private static void blockIndexing(UUID nodeId) {
+ assertFalse(BLOCKS.contains(nodeId));
+
+ BLOCKS.put(nodeId, new T2<>(new CountDownLatch(1), new AtomicBoolean()));
+ }
+
+ /**
+ * Unblock indexing.
+ *
+ * @param node Node.
+ */
+ private static void unblockIndexing(Ignite node) {
+ UUID nodeId = ((IgniteEx)node).localNode().id();
+
+ unblockIndexing(nodeId);
+ }
+
+ /**
+ * Unblock indexing.
+ *
+ * @param nodeId Node ID.
+ */
+ private static void unblockIndexing(UUID nodeId) {
+ T2<CountDownLatch, AtomicBoolean> blocker = BLOCKS.remove(nodeId);
+
+ assertNotNull(blocker);
+
+ blocker.get1().countDown();
+ }
+
+ /**
+ * Await indexing.
+ *
+ * @param nodeId Node ID.
+ */
+ private static void awaitIndexing(UUID nodeId) {
+ T2<CountDownLatch, AtomicBoolean> blocker = BLOCKS.get(nodeId);
+
+ if (blocker != null) {
+ assertTrue(blocker.get2().compareAndSet(false, true));
+
+ while (true) {
+ try {
+ blocker.get1().await();
+
+ break;
+ }
+ catch (InterruptedException e) {
+ // No-op.
+ }
+ }
+ }
+ }
+
+ /**
+ * Blocking indexing processor.
+ */
+ private static class BlockingIndexing extends IgniteH2Indexing {
+ /** {@inheritDoc} */
+ @Override public void dynamicIndexCreate(@Nullable String spaceName, String tblName,
+ QueryIndexDescriptorImpl idxDesc, boolean ifNotExists, SchemaIndexCacheVisitor cacheVisitor)
+ throws IgniteCheckedException {
+ awaitIndexing(ctx.localNodeId());
+
+ super.dynamicIndexCreate(spaceName, tblName, idxDesc, ifNotExists, cacheVisitor);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void dynamicIndexDrop(@Nullable String spaceName, String idxName, boolean ifExists)
+ throws IgniteCheckedException{
+ awaitIndexing(ctx.localNodeId());
+
+ super.dynamicIndexDrop(spaceName, idxName, ifExists);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractSelfTest.java
new file mode 100644
index 0000000..e52e0d3
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractSelfTest.java
@@ -0,0 +1,467 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.SqlQuery;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.binary.BinaryMarshaller;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+
+import javax.cache.Cache;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Tests for dynamic index creation.
+ */
+@SuppressWarnings({"unchecked", "ThrowableResultOfMethodCallIgnored"})
+public abstract class DynamicIndexAbstractSelfTest extends AbstractSchemaSelfTest {
+ /** Attribute to filter node out of cache data nodes. */
+ protected static final String ATTR_FILTERED = "FILTERED";
+
+ /** Key range limit for "before" step. */
+ protected static final int KEY_BEFORE = 100;
+
+ /** Key range limit for "after" step. */
+ protected static final int KEY_AFTER = 200;
+
+ /** SQL to check index on the field 1. */
+ protected static final String SQL_SIMPLE_FIELD_1 = "SELECT * FROM " + TBL_NAME + " WHERE " + FIELD_NAME_1 + " >= ?";
+
+ /** SQL to check composite index */
+ protected static final String SQL_COMPOSITE = "SELECT * FROM " + TBL_NAME + " WHERE " + FIELD_NAME_1 +
+ " >= ? AND " + alias(FIELD_NAME_2) + " >= ?";
+
+ /** SQL to check index on the field 2. */
+ protected static final String SQL_SIMPLE_FIELD_2 =
+ "SELECT * FROM " + TBL_NAME + " WHERE " + alias(FIELD_NAME_2) + " >= ?";
+
+ /** Argument for simple SQL (1). */
+ protected static final int SQL_ARG_1 = 40;
+
+ /** Argument for simple SQL (2). */
+ protected static final int SQL_ARG_2 = 80;
+
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ stopAllGrids();
+
+ super.afterTestsStopped();
+ }
+
+ /**
+ * Create server configuration.
+ *
+ * @param idx Index.
+ * @return Configuration.
+ * @throws Exception If failed.
+ */
+ protected IgniteConfiguration serverConfiguration(int idx) throws Exception {
+ return serverConfiguration(idx, false);
+ }
+
+ /**
+ * Create server configuration.
+ *
+ * @param idx Index.
+ * @param filter Whether to filter the node out of cache.
+ * @return Configuration.
+ * @throws Exception If failed.
+ */
+ protected IgniteConfiguration serverConfiguration(int idx, boolean filter) throws Exception {
+ IgniteConfiguration cfg = commonConfiguration(idx);
+
+ if (filter)
+ cfg.setUserAttributes(Collections.singletonMap(ATTR_FILTERED, true));
+
+ return cfg;
+ }
+
+ /**
+ * Create client configuration.
+ *
+ * @param idx Index.
+ * @return Configuration.
+ * @throws Exception If failed.
+ */
+ protected IgniteConfiguration clientConfiguration(int idx) throws Exception {
+ return commonConfiguration(idx).setClientMode(true);
+ }
+
+ /**
+ * Create common node configuration.
+ *
+ * @param idx Index.
+ * @return Configuration.
+ * @throws Exception If failed.
+ */
+ protected IgniteConfiguration commonConfiguration(int idx) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(getTestIgniteInstanceName(idx));
+
+ cfg.setDiscoverySpi(new TcpDiscoverySpi());
+
+ cfg.setMarshaller(new BinaryMarshaller());
+
+ return optimize(cfg);
+ }
+
+ /**
+ * @return Default cache configuration.
+ */
+ protected CacheConfiguration<KeyClass, ValueClass> cacheConfiguration() {
+ CacheConfiguration ccfg = new CacheConfiguration().setName(CACHE_NAME);
+
+ QueryEntity entity = new QueryEntity();
+
+ entity.setKeyType(KeyClass.class.getName());
+ entity.setValueType(ValueClass.class.getName());
+
+ entity.addQueryField(FIELD_KEY, Long.class.getName(), null);
+ entity.addQueryField(FIELD_NAME_1, Long.class.getName(), null);
+ entity.addQueryField(FIELD_NAME_2, Long.class.getName(), null);
+
+ entity.setKeyFields(Collections.singleton(FIELD_KEY));
+
+ entity.setAliases(Collections.singletonMap(FIELD_NAME_2, alias(FIELD_NAME_2)));
+
+ ccfg.setQueryEntities(Collections.singletonList(entity));
+
+ ccfg.setNodeFilter(new NodeFilter());
+
+ ccfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
+ ccfg.setBackups(1);
+
+ return ccfg;
+ }
+
+ /**
+ * Ensure that schema exception is thrown.
+ *
+ * @param r Runnable.
+ * @param expCode Error code.
+ */
+ protected static void assertSchemaException(RunnableX r, int expCode) {
+ try {
+ r.run();
+ }
+ catch (SchemaOperationException e) {
+ assertEquals("Unexpected error code [expected=" + expCode + ", actual=" + e.code() + ']',
+ expCode, e.code());
+
+ return;
+ }
+ catch (Exception e) {
+ fail("Unexpected exception: " + e);
+ }
+
+ fail(SchemaOperationException.class.getSimpleName() + " is not thrown.");
+ }
+
+ /**
+ * Ensure index is used in plan.
+ *
+ * @param idxName Index name.
+ * @param sql SQL.
+ * @param args Arguments.
+ */
+ protected static void assertIndexUsed(String idxName, String sql, Object... args) {
+ for (Ignite node : Ignition.allGrids())
+ assertIndexUsed((IgniteEx)node, idxName, sql, args);
+ }
+
+ /**
+ * Ensure index is used in plan.
+ *
+ * @param node Node.
+ * @param idxName Index name.
+ * @param sql SQL.
+ * @param args Arguments.
+ */
+ protected static void assertIndexUsed(IgniteEx node, String idxName, String sql, Object... args) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("EXPLAIN " + sql);
+
+ if (args != null && args.length > 0)
+ qry.setArgs(args);
+
+ String plan = (String)node.cache(CACHE_NAME).query(qry).getAll().get(0).get(0);
+
+ assertTrue("Index is not used: " + plan, plan.toLowerCase().contains(idxName.toLowerCase()));
+ }
+
+ /**
+ * Ensure index is not used in plan.
+ *
+ * @param idxName Index name.
+ * @param sql SQL.
+ * @param args Arguments.
+ */
+ protected static void assertIndexNotUsed(String idxName, String sql, Object... args) {
+ for (Ignite node : Ignition.allGrids())
+ assertIndexNotUsed((IgniteEx)node, idxName, sql, args);
+ }
+
+ /**
+ * Ensure index is not used in plan.
+ *
+ * @param node Node.
+ * @param idxName Index name.
+ * @param sql SQL.
+ * @param args Arguments.
+ */
+ protected static void assertIndexNotUsed(IgniteEx node, String idxName, String sql, Object... args) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("EXPLAIN " + sql);
+
+ if (args != null && args.length > 0)
+ qry.setArgs(args);
+
+ String plan = (String)node.cache(CACHE_NAME).query(qry).getAll().get(0).get(0);
+
+ assertFalse("Index is used: " + plan, plan.contains(idxName));
+ }
+
+ /**
+ * Create key object.
+ *
+ * @param ignite Ignite instance.
+ * @param id ID.
+ * @return Key object.
+ */
+ protected static BinaryObject key(Ignite ignite, long id) {
+ return ignite.binary().builder(KeyClass.class.getName()).setField(FIELD_KEY, id).build();
+ }
+
+ /**
+ * Create value object.
+ *
+ * @param ignite Ignite instance.
+ * @param id ID.
+ * @return Value object.
+ */
+ protected static BinaryObject value(Ignite ignite, long id) {
+ return ignite.binary().builder(ValueClass.class.getName())
+ .setField(FIELD_NAME_1, id)
+ .setField(FIELD_NAME_2, id)
+ .build();
+ }
+
+ /**
+ * Create key/value entry for the given key.
+ *
+ * @param ignite Ignite instance.
+ * @param id ID.
+ * @return Entry.
+ */
+ protected static T2<BinaryObject, BinaryObject> entry(Ignite ignite, long id) {
+ return new T2<>(key(ignite, id), value(ignite, id));
+ }
+
+ /**
+ * Get common cache.
+ *
+ * @param node Node.
+ * @return Cache.
+ */
+ protected static IgniteCache<BinaryObject, BinaryObject> cache(Ignite node) {
+ return node.cache(CACHE_NAME).withKeepBinary();
+ }
+
+ /**
+ * Get key.
+ *
+ * @param node Node.
+ * @param id ID.
+ */
+ protected static BinaryObject get(Ignite node, int id) {
+ BinaryObject key = key(node, id);
+
+ return cache(node).get(key);
+ }
+
+ /**
+ * Put key range.
+ *
+ * @param node Node.
+ * @param from From key.
+ * @param to To key.
+ */
+ protected static void put(Ignite node, int from, int to) {
+ try (IgniteDataStreamer streamer = node.dataStreamer(CACHE_NAME)) {
+ streamer.allowOverwrite(true);
+ streamer.keepBinary(true);
+
+ for (int i = from; i < to; i++) {
+ BinaryObject key = key(node, i);
+ BinaryObject val = value(node, i);
+
+ streamer.addData(key, val);
+ }
+
+ streamer.flush();
+ }
+ }
+
+ /**
+ * Put key to cache.
+ *
+ * @param node Node.
+ * @param id ID.
+ */
+ protected static void put(Ignite node, long id) {
+ BinaryObject key = key(node, id);
+ BinaryObject val = value(node, id);
+
+ cache(node).put(key, val);
+ }
+
+ /**
+ * Remove key range.
+ *
+ * @param node Node.
+ * @param from From key.
+ * @param to To key.
+ */
+ protected static void remove(Ignite node, int from, int to) {
+ for (int i = from; i < to; i++)
+ remove(node, i);
+ }
+
+ /**
+ * Remove key form cache.
+ *
+ * @param node Node.
+ * @param id ID.
+ */
+ protected static void remove(Ignite node, long id) {
+ BinaryObject key = key(node, id);
+
+ cache(node).remove(key);
+ }
+
+ /**
+ * @return Random string.
+ */
+ protected static String randomString() {
+ return UUID.randomUUID().toString();
+ }
+
+ /**
+ * Assert SQL simple data state.
+ *
+ * @param sql SQL query.
+ * @param expSize Expected size.
+ */
+ protected static void assertSqlSimpleData(String sql, int expSize) {
+ for (Ignite node : Ignition.allGrids())
+ assertSqlSimpleData(node, sql, expSize);
+ }
+
+ /**
+ * Assert SQL simple data state.
+ *
+ * @param node Node.
+ * @param sql SQL query.
+ * @param expSize Expected size.
+ */
+ protected static void assertSqlSimpleData(Ignite node, String sql, int expSize) {
+ SqlQuery qry = new SqlQuery(tableName(ValueClass.class), sql).setArgs(SQL_ARG_1);
+
+ List<Cache.Entry<BinaryObject, BinaryObject>> res = node.cache(CACHE_NAME).withKeepBinary().query(qry).getAll();
+
+ Set<Long> ids = new HashSet<>();
+
+ for (Cache.Entry<BinaryObject, BinaryObject> entry : res) {
+ long id = entry.getKey().field(FIELD_KEY);
+
+ long field1 = entry.getValue().field(FIELD_NAME_1);
+ long field2 = entry.getValue().field(FIELD_NAME_2);
+
+ assertTrue(field1 >= SQL_ARG_1);
+
+ assertEquals(id, field1);
+ assertEquals(id, field2);
+
+ assertTrue(ids.add(id));
+ }
+
+ assertEquals("Size mismatch [node=" + node.name() + ", exp=" + expSize + ", actual=" + res.size() +
+ ", ids=" + ids + ']', expSize, res.size());
+ }
+
+ /**
+ * Assert SQL simple data state.
+ *
+ * @param node Node.
+ * @param sql SQL query.
+ * @param expSize Expected size.
+ */
+ protected static void assertSqlCompositeData(Ignite node, String sql, int expSize) {
+ SqlQuery qry = new SqlQuery(tableName(ValueClass.class), sql).setArgs(SQL_ARG_1, SQL_ARG_2);
+
+ List<Cache.Entry<BinaryObject, BinaryObject>> res = node.cache(CACHE_NAME).withKeepBinary().query(qry).getAll();
+
+ Set<Long> ids = new HashSet<>();
+
+ for (Cache.Entry<BinaryObject, BinaryObject> entry : res) {
+ long id = entry.getKey().field(FIELD_KEY);
+
+ long field1 = entry.getValue().field(FIELD_NAME_1);
+ long field2 = entry.getValue().field(FIELD_NAME_2);
+
+ assertTrue(field1 >= SQL_ARG_2);
+
+ assertEquals(id, field1);
+ assertEquals(id, field2);
+
+ assertTrue(ids.add(id));
+ }
+
+ assertEquals("Size mismatch [exp=" + expSize + ", actual=" + res.size() + ", ids=" + ids + ']',
+ expSize, res.size());
+ }
+
+ /**
+ * Node filter.
+ */
+ protected static class NodeFilter implements IgnitePredicate<ClusterNode>, Serializable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** {@inheritDoc} */
+ @Override public boolean apply(ClusterNode node) {
+ return node.attribute(ATTR_FILTERED) == null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexClientBasicSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexClientBasicSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexClientBasicSelfTest.java
new file mode 100644
index 0000000..10f4f85
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexClientBasicSelfTest.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+/**
+ * Test dynamic schema operations from client node.
+ */
+public class DynamicIndexClientBasicSelfTest extends DynamicIndexAbstractBasicSelfTest {
+ /** {@inheritDoc} */
+ @Override protected int nodeIndex() {
+ return IDX_CLI;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexPartitionedAtomicConcurrentSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexPartitionedAtomicConcurrentSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexPartitionedAtomicConcurrentSelfTest.java
new file mode 100644
index 0000000..497ec39
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexPartitionedAtomicConcurrentSelfTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+
+/**
+ * Concurrency tests for dynamic index create/drop for PARTITIONED/ATOMIC cache.
+ */
+public class DynamicIndexPartitionedAtomicConcurrentSelfTest extends DynamicIndexAbstractConcurrentSelfTest {
+ /**
+ * Constructor.
+ */
+ public DynamicIndexPartitionedAtomicConcurrentSelfTest() {
+ super(CacheMode.PARTITIONED, CacheAtomicityMode.ATOMIC);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexPartitionedTransactionalConcurrentSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexPartitionedTransactionalConcurrentSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexPartitionedTransactionalConcurrentSelfTest.java
new file mode 100644
index 0000000..fed0149
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexPartitionedTransactionalConcurrentSelfTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+
+/**
+ * Concurrency tests for dynamic index create/drop for PARTITIONED/TRANSACTIONAL cache.
+ */
+public class DynamicIndexPartitionedTransactionalConcurrentSelfTest extends DynamicIndexAbstractConcurrentSelfTest {
+ /**
+ * Constructor.
+ */
+ public DynamicIndexPartitionedTransactionalConcurrentSelfTest() {
+ super(CacheMode.PARTITIONED, CacheAtomicityMode.TRANSACTIONAL);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexReplicatedAtomicConcurrentSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexReplicatedAtomicConcurrentSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexReplicatedAtomicConcurrentSelfTest.java
new file mode 100644
index 0000000..2c6c9a9
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexReplicatedAtomicConcurrentSelfTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+
+/**
+ * Concurrency tests for dynamic index create/drop for REPLICATED/ATOMIC cache.
+ */
+public class DynamicIndexReplicatedAtomicConcurrentSelfTest extends DynamicIndexAbstractConcurrentSelfTest {
+ /**
+ * Constructor.
+ */
+ public DynamicIndexReplicatedAtomicConcurrentSelfTest() {
+ super(CacheMode.REPLICATED, CacheAtomicityMode.ATOMIC);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexReplicatedTransactionalConcurrentSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexReplicatedTransactionalConcurrentSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexReplicatedTransactionalConcurrentSelfTest.java
new file mode 100644
index 0000000..9dc92a4
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexReplicatedTransactionalConcurrentSelfTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+
+/**
+ * Concurrency tests for dynamic index create/drop for REPLICATED/TRANSACTIONAL cache.
+ */
+public class DynamicIndexReplicatedTransactionalConcurrentSelfTest extends DynamicIndexAbstractConcurrentSelfTest {
+ /**
+ * Constructor.
+ */
+ public DynamicIndexReplicatedTransactionalConcurrentSelfTest() {
+ super(CacheMode.REPLICATED, CacheAtomicityMode.TRANSACTIONAL);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerBasicSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerBasicSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerBasicSelfTest.java
new file mode 100644
index 0000000..c014229
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerBasicSelfTest.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+/**
+ * Test dynamic schema operations from non-coordinator node.
+ */
+public class DynamicIndexServerBasicSelfTest extends DynamicIndexAbstractBasicSelfTest {
+ /** {@inheritDoc} */
+ @Override protected int nodeIndex() {
+ return IDX_SRV_NON_CRD;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerCoordinatorBasicSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerCoordinatorBasicSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerCoordinatorBasicSelfTest.java
new file mode 100644
index 0000000..7427a4c
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerCoordinatorBasicSelfTest.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+/**
+ * Test dynamic schema operations from coordinator node.
+ */
+public class DynamicIndexServerCoordinatorBasicSelfTest extends DynamicIndexAbstractBasicSelfTest {
+ /** {@inheritDoc} */
+ @Override protected int nodeIndex() {
+ return IDX_SRV_CRD;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerNodeFIlterBasicSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerNodeFIlterBasicSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerNodeFIlterBasicSelfTest.java
new file mode 100644
index 0000000..b8acd1d
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerNodeFIlterBasicSelfTest.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+/**
+ * Test dynamic schema operations from server node which do not pass node filter.
+ */
+public class DynamicIndexServerNodeFIlterBasicSelfTest extends DynamicIndexAbstractBasicSelfTest {
+ /** {@inheritDoc} */
+ @Override protected int nodeIndex() {
+ return IDX_SRV_FILTERED;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerNodeFilterCoordinatorBasicSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerNodeFilterCoordinatorBasicSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerNodeFilterCoordinatorBasicSelfTest.java
new file mode 100644
index 0000000..e297fe1
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexServerNodeFilterCoordinatorBasicSelfTest.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.configuration.IgniteConfiguration;
+
+/**
+ * Test dynamic schema operations from server node which do not pass node filter and which is coordinator.
+ */
+public class DynamicIndexServerNodeFilterCoordinatorBasicSelfTest extends DynamicIndexServerCoordinatorBasicSelfTest {
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration serverCoordinatorConfiguration(int idx) throws Exception {
+ return serverConfiguration(idx, true);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAbstractSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAbstractSelfTest.java
new file mode 100644
index 0000000..cf563cc
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAbstractSelfTest.java
@@ -0,0 +1,400 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import javax.cache.CacheException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.NearCacheConfiguration;
+import org.apache.ignite.internal.binary.BinaryMarshaller;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.util.typedef.F;
+
+/**
+ * Test that checks indexes handling on H2 side.
+ */
+public abstract class H2DynamicIndexAbstractSelfTest extends AbstractSchemaSelfTest {
+ /** Client node index. */
+ private final static int CLIENT = 2;
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ super.beforeTestsStarted();
+
+ for (IgniteConfiguration cfg : configurations())
+ Ignition.start(cfg);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ stopAllGrids();
+
+ super.afterTestsStopped();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ client().getOrCreateCache(cacheConfiguration());
+
+ assertNoIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1);
+
+ IgniteCache<KeyClass, ValueClass> cache = client().cache(CACHE_NAME);
+
+ cache.put(new KeyClass(1), new ValueClass("val1"));
+ cache.put(new KeyClass(2), new ValueClass("val2"));
+ cache.put(new KeyClass(3), new ValueClass("val3"));
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ client().destroyCache(CACHE_NAME);
+
+ super.afterTest();
+ }
+
+ /**
+ * Test that after index creation index is used by queries.
+ */
+ public void testCreateIndex() throws Exception {
+ IgniteCache<KeyClass, ValueClass> cache = cache();
+
+ assertSize(3);
+
+ cache.query(new SqlFieldsQuery("CREATE INDEX \"" + IDX_NAME_1 + "\" ON \"" + TBL_NAME + "\"(\""
+ + FIELD_NAME_1 + "\" ASC)")).getAll();
+
+ // Test that local queries on all nodes use new index.
+ for (int i = 0 ; i < 4; i++) {
+ List<List<?>> locRes = ignite(i).cache("cache").query(new SqlFieldsQuery("explain select \"id\" from " +
+ "\"cache\".\"ValueClass\" where \"field1\" = 'A'").setLocal(true)).getAll();
+
+ assertEquals(F.asList(
+ Collections.singletonList("SELECT\n" +
+ " \"id\"\n" +
+ "FROM \"cache\".\"ValueClass\"\n" +
+ " /* \"cache\".\"idx_1\": \"field1\" = 'A' */\n" +
+ "WHERE \"field1\" = 'A'")
+ ), locRes);
+ }
+
+ assertSize(3);
+
+ cache.remove(new KeyClass(2));
+
+ assertSize(2);
+
+ cache.put(new KeyClass(4), new ValueClass("someVal"));
+
+ assertSize(3);
+ }
+
+ /**
+ * Test that creating an index with duplicate name yields an error.
+ */
+ public void testCreateIndexWithDuplicateName() {
+ final IgniteCache<KeyClass, ValueClass> cache = cache();
+
+ cache.query(new SqlFieldsQuery("CREATE INDEX \"" + IDX_NAME_1 + "\" ON \"" + TBL_NAME + "\"(\""
+ + FIELD_NAME_1 + "\" ASC)"));
+
+ assertSqlException(new RunnableX() {
+ @Override public void run() throws Exception {
+ cache.query(new SqlFieldsQuery("CREATE INDEX \"" + IDX_NAME_1 + "\" ON \"" + TBL_NAME + "\"(\"id\" ASC)"));
+ }
+ }, IgniteQueryErrorCode.INDEX_ALREADY_EXISTS);
+ }
+
+ /**
+ * Test that creating an index with duplicate name does not yield an error with {@code IF NOT EXISTS}.
+ */
+ public void testCreateIndexIfNotExists() {
+ final IgniteCache<KeyClass, ValueClass> cache = cache();
+
+ cache.query(new SqlFieldsQuery("CREATE INDEX \"" + IDX_NAME_1 + "\" ON \"" + TBL_NAME + "\"(\""
+ + FIELD_NAME_1 + "\" ASC)"));
+
+ cache.query(new SqlFieldsQuery("CREATE INDEX IF NOT EXISTS \"" + IDX_NAME_1 + "\" ON \"" + TBL_NAME +
+ "\"(\"id\" ASC)"));
+ }
+
+ /**
+ * Test that after index drop there are no attempts to use it, and data state remains intact.
+ */
+ public void testDropIndex() {
+ IgniteCache<KeyClass, ValueClass> cache = cache();
+
+ assertSize(3);
+
+ cache.query(new SqlFieldsQuery("CREATE INDEX \"" + IDX_NAME_1 + "\" ON \"" + TBL_NAME + "\"(\""
+ + FIELD_NAME_1 + "\" ASC)"));
+
+ assertSize(3);
+
+ cache.query(new SqlFieldsQuery("DROP INDEX \"" + IDX_NAME_1 + "\""));
+
+ // Test that no local queries on all nodes use new index.
+ for (int i = 0 ; i < 4; i++) {
+ List<List<?>> locRes = ignite(i).cache("cache").query(new SqlFieldsQuery("explain select \"id\" from " +
+ "\"cache\".\"ValueClass\" where \"field1\" = 'A'").setLocal(true)).getAll();
+
+ assertEquals(F.asList(
+ Collections.singletonList("SELECT\n" +
+ " \"id\"\n" +
+ "FROM \"cache\".\"ValueClass\"\n" +
+ " /* \"cache\".\"ValueClass\".__SCAN_ */\n" +
+ "WHERE \"field1\" = 'A'")
+ ), locRes);
+ }
+
+ assertSize(3);
+ }
+
+ /**
+ * Test that dropping a non-existent index yields an error.
+ */
+ public void testDropMissingIndex() {
+ final IgniteCache<KeyClass, ValueClass> cache = cache();
+
+ assertSqlException(new RunnableX() {
+ @Override public void run() throws Exception {
+ cache.query(new SqlFieldsQuery("DROP INDEX \"" + IDX_NAME_1 + "\""));
+ }
+ }, IgniteQueryErrorCode.INDEX_NOT_FOUND);
+ }
+
+ /**
+ * Test that dropping a non-existent index does not yield an error with {@code IF EXISTS}.
+ */
+ public void testDropMissingIndexIfExists() {
+ final IgniteCache<KeyClass, ValueClass> cache = cache();
+
+ cache.query(new SqlFieldsQuery("DROP INDEX IF EXISTS \"" + IDX_NAME_1 + "\""));
+ }
+
+ /**
+ * Test that changes in cache affect index, and vice versa.
+ */
+ public void testIndexState() {
+ IgniteCache<KeyClass, ValueClass> cache = cache();
+
+ assertColumnValues("val1", "val2", "val3");
+
+ cache.query(new SqlFieldsQuery("CREATE INDEX \"" + IDX_NAME_1 + "\" ON \"" + TBL_NAME + "\"(\""
+ + FIELD_NAME_1 + "\" ASC)"));
+
+ assertColumnValues("val1", "val2", "val3");
+
+ cache.remove(new KeyClass(2));
+
+ assertColumnValues("val1", "val3");
+
+ cache.put(new KeyClass(0), new ValueClass("someVal"));
+
+ assertColumnValues("someVal", "val1", "val3");
+
+ cache.query(new SqlFieldsQuery("DROP INDEX \"" + IDX_NAME_1 + "\""));
+
+ assertColumnValues("someVal", "val1", "val3");
+ }
+
+ /**
+ * Check that values of {@code field1} match what we expect.
+ * @param vals Expected values.
+ */
+ private void assertColumnValues(String... vals) {
+ List<List<?>> expRes = new ArrayList<>(vals.length);
+
+ for (String v : vals)
+ expRes.add(Collections.singletonList(v));
+
+ assertEquals(expRes, cache().query(new SqlFieldsQuery("SELECT \"" + FIELD_NAME_1 + "\" FROM \"" + TBL_NAME +
+ "\" ORDER BY \"id\""))
+ .getAll());
+ }
+
+ /**
+ * Do a {@code SELECT COUNT(*)} query to check index state correctness.
+ * @param expSize Expected number of items in table.
+ */
+ private void assertSize(long expSize) {
+ assertEquals(expSize, cache().size());
+
+ assertEquals(expSize, cache().query(new SqlFieldsQuery("SELECT COUNT(*) from \"ValueClass\""))
+ .getAll().get(0).get(0));
+ }
+
+ /**
+ * Get configurations to be used in test.
+ *
+ * @return Configurations.
+ * @throws Exception If failed.
+ */
+ private List<IgniteConfiguration> configurations() throws Exception {
+ return Arrays.asList(
+ serverConfiguration(0),
+ serverConfiguration(1),
+ clientConfiguration(2),
+ serverConfiguration(3)
+ );
+ }
+
+ /**
+ * @return Client node.
+ */
+ private Ignite client() {
+ return ignite(CLIENT);
+ }
+
+ /**
+ * @return Cache.
+ */
+ private IgniteCache<KeyClass, ValueClass> cache() {
+ return client().cache(CACHE_NAME);
+ }
+
+ /**
+ * Create server configuration.
+ *
+ * @param idx Index.
+ * @return Configuration.
+ * @throws Exception If failed.
+ */
+ private IgniteConfiguration serverConfiguration(int idx) throws Exception {
+ return commonConfiguration(idx);
+ }
+
+ /**
+ * Create client configuration.
+ *
+ * @param idx Index.
+ * @return Configuration.
+ * @throws Exception If failed.
+ */
+ private IgniteConfiguration clientConfiguration(int idx) throws Exception {
+ return commonConfiguration(idx).setClientMode(true);
+ }
+
+ /**
+ * Create common node configuration.
+ *
+ * @param idx Index.
+ * @return Configuration.
+ * @throws Exception If failed.
+ */
+ private IgniteConfiguration commonConfiguration(int idx) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(getTestIgniteInstanceName(idx));
+
+ cfg.setMarshaller(new BinaryMarshaller());
+
+ return optimize(cfg);
+ }
+
+ /**
+ * @return Default cache configuration.
+ */
+ private CacheConfiguration cacheConfiguration() {
+ CacheConfiguration<KeyClass, ValueClass> ccfg = new CacheConfiguration<KeyClass, ValueClass>()
+ .setName(CACHE_NAME);
+
+ QueryEntity entity = new QueryEntity();
+
+ entity.setKeyType(KeyClass.class.getName());
+ entity.setValueType(ValueClass.class.getName());
+
+ entity.addQueryField("id", Long.class.getName(), null);
+ entity.addQueryField(FIELD_NAME_1, String.class.getName(), null);
+ entity.addQueryField(FIELD_NAME_2, String.class.getName(), null);
+
+ entity.setKeyFields(Collections.singleton("id"));
+
+ entity.setAliases(Collections.singletonMap(FIELD_NAME_2, alias(FIELD_NAME_2)));
+
+ ccfg.setQueryEntities(Collections.singletonList(entity));
+
+ ccfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
+ ccfg.setSqlEscapeAll(true);
+ ccfg.setAtomicityMode(atomicityMode());
+ ccfg.setCacheMode(cacheMode());
+
+ if (nearCache())
+ ccfg.setNearConfiguration(new NearCacheConfiguration<KeyClass, ValueClass>());
+
+ return ccfg;
+ }
+
+ /**
+ * @return Cache mode to use.
+ */
+ protected abstract CacheMode cacheMode();
+
+ /**
+ * @return Cache atomicity mode to use.
+ */
+ protected abstract CacheAtomicityMode atomicityMode();
+
+ /**
+ * @return Whether to use near cache.
+ */
+ protected abstract boolean nearCache();
+
+ /**
+ * Ensure that SQL exception is thrown.
+ *
+ * @param r Runnable.
+ * @param expCode Error code.
+ */
+ private static void assertSqlException(DynamicIndexAbstractBasicSelfTest.RunnableX r, int expCode) {
+ try {
+ try {
+ r.run();
+ }
+ catch (CacheException e) {
+ if (e.getCause() != null)
+ throw (Exception)e.getCause();
+ else
+ throw e;
+ }
+ }
+ catch (IgniteSQLException e) {
+ assertEquals("Unexpected error code [expected=" + expCode + ", actual=" + e.statusCode() + ']',
+ expCode, e.statusCode());
+
+ return;
+ }
+ catch (Exception e) {
+ fail("Unexpected exception: " + e);
+ }
+
+ fail(IgniteSQLException.class.getSimpleName() + " is not thrown.");
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAtomicPartitionedNearSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAtomicPartitionedNearSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAtomicPartitionedNearSelfTest.java
new file mode 100644
index 0000000..96a7c14
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAtomicPartitionedNearSelfTest.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+/** */
+public class H2DynamicIndexAtomicPartitionedNearSelfTest extends H2DynamicIndexAtomicPartitionedSelfTest {
+ /** {@inheritDoc} */
+ @Override protected boolean nearCache() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAtomicPartitionedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAtomicPartitionedSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAtomicPartitionedSelfTest.java
new file mode 100644
index 0000000..0a4c48c
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAtomicPartitionedSelfTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+
+/** */
+public class H2DynamicIndexAtomicPartitionedSelfTest extends H2DynamicIndexAbstractSelfTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.PARTITIONED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected CacheAtomicityMode atomicityMode() {
+ return CacheAtomicityMode.ATOMIC;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean nearCache() {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAtomicReplicatedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAtomicReplicatedSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAtomicReplicatedSelfTest.java
new file mode 100644
index 0000000..fc9f9e7
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAtomicReplicatedSelfTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+
+/** */
+public class H2DynamicIndexAtomicReplicatedSelfTest extends H2DynamicIndexAbstractSelfTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.REPLICATED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected CacheAtomicityMode atomicityMode() {
+ return CacheAtomicityMode.ATOMIC;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean nearCache() {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexTransactionalPartitionedNearSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexTransactionalPartitionedNearSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexTransactionalPartitionedNearSelfTest.java
new file mode 100644
index 0000000..e8c4fb2
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexTransactionalPartitionedNearSelfTest.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+/** */
+public class H2DynamicIndexTransactionalPartitionedNearSelfTest extends H2DynamicIndexTransactionalPartitionedSelfTest {
+ /** {@inheritDoc} */
+ @Override protected boolean nearCache() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexTransactionalPartitionedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexTransactionalPartitionedSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexTransactionalPartitionedSelfTest.java
new file mode 100644
index 0000000..ad61412
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexTransactionalPartitionedSelfTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+
+/** */
+public class H2DynamicIndexTransactionalPartitionedSelfTest extends H2DynamicIndexAbstractSelfTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.PARTITIONED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected CacheAtomicityMode atomicityMode() {
+ return CacheAtomicityMode.TRANSACTIONAL;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean nearCache() {
+ return false;
+ }
+}
[5/8] ignite git commit: IGNITE-4565: Implemented CREATE INDEX and
DROP INDEX. This closes #1773. This closes #1804.
Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java
index 7677d0d..119a389 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java
@@ -23,7 +23,10 @@ import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.internal.A;
import org.apache.ignite.internal.util.typedef.internal.S;
+import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -33,6 +36,9 @@ import java.util.Map;
* Descriptor of type.
*/
public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
+ /** Space. */
+ private final String space;
+
/** */
private String name;
@@ -50,9 +56,15 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
/** Map with upper cased property names to help find properties based on SQL INSERT and MERGE queries. */
private final Map<String, GridQueryProperty> uppercaseProps = new HashMap<>();
+ /** Mutex for operations on indexes. */
+ private final Object idxMux = new Object();
+
/** */
@GridToStringInclude
- private final Map<String, QueryIndexDescriptorImpl> indexes = new HashMap<>();
+ private final Map<String, QueryIndexDescriptorImpl> idxs = new HashMap<>();
+
+ /** Aliases. */
+ private Map<String, String> aliases;
/** */
private QueryIndexDescriptorImpl fullTextIdx;
@@ -78,6 +90,25 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
/** */
private String affKey;
+ /** Obsolete. */
+ private volatile boolean obsolete;
+
+ /**
+ * Constructor.
+ *
+ * @param space Cache name.
+ */
+ public QueryTypeDescriptorImpl(String space) {
+ this.space = space;
+ }
+
+ /**
+ * @return Space.
+ */
+ public String space() {
+ return space;
+ }
+
/** {@inheritDoc} */
@Override public String name() {
return name;
@@ -97,7 +128,7 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
* @return Table name.
*/
@Override public String tableName() {
- return tblName;
+ return tblName != null ? tblName : name;
}
/**
@@ -160,7 +191,9 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
/** {@inheritDoc} */
@Override public Map<String, GridQueryIndexDescriptor> indexes() {
- return Collections.<String, GridQueryIndexDescriptor>unmodifiableMap(indexes);
+ synchronized (idxMux) {
+ return Collections.<String, GridQueryIndexDescriptor>unmodifiableMap(idxs);
+ }
}
/** {@inheritDoc} */
@@ -176,59 +209,74 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
}
/**
- * Adds index.
+ * Get index by name.
*
- * @param idxName Index name.
- * @param type Index type.
- * @param inlineSize Inline size.
- * @return Index descriptor.
- * @throws IgniteCheckedException In case of error.
+ * @param name Name.
+ * @return Index.
*/
- public QueryIndexDescriptorImpl addIndex(String idxName, QueryIndexType type, int inlineSize) throws IgniteCheckedException {
- QueryIndexDescriptorImpl idx = new QueryIndexDescriptorImpl(type, inlineSize);
+ @Nullable public QueryIndexDescriptorImpl index(String name) {
+ synchronized (idxMux) {
+ return idxs.get(name);
+ }
+ }
- if (indexes.put(idxName, idx) != null)
- throw new IgniteCheckedException("Index with name '" + idxName + "' already exists.");
+ /**
+ * @return Raw index descriptors.
+ */
+ public Collection<QueryIndexDescriptorImpl> indexes0() {
+ synchronized (idxMux) {
+ return new ArrayList<>(idxs.values());
+ }
+ }
- return idx;
+ /** {@inheritDoc} */
+ @Override public GridQueryIndexDescriptor textIndex() {
+ return fullTextIdx;
}
/**
- * Adds field to index.
+ * Add index.
*
- * @param idxName Index name.
- * @param field Field name.
- * @param orderNum Fields order number in index.
- * @param inlineSize Inline size.
- * @param descending Sorting order.
+ * @param idx Index.
* @throws IgniteCheckedException If failed.
*/
- public void addFieldToIndex(
- String idxName,
- String field,
- int orderNum,
- int inlineSize,
- boolean descending
- ) throws IgniteCheckedException {
- QueryIndexDescriptorImpl desc = indexes.get(idxName);
+ public void addIndex(QueryIndexDescriptorImpl idx) throws IgniteCheckedException {
+ synchronized (idxMux) {
+ if (idxs.put(idx.name(), idx) != null)
+ throw new IgniteCheckedException("Index with name '" + idx.name() + "' already exists.");
+ }
+ }
- if (desc == null)
- desc = addIndex(idxName, QueryIndexType.SORTED, inlineSize);
+ /**
+ * Drop index.
+ *
+ * @param idxName Index name.
+ */
+ public void dropIndex(String idxName) {
+ synchronized (idxMux) {
+ idxs.remove(idxName);
+ }
+ }
- desc.addField(field, orderNum, descending);
+ /**
+ * Chedk if particular field exists.
+ *
+ * @param field Field.
+ * @return {@code True} if exists.
+ */
+ public boolean hasField(String field) {
+ return props.containsKey(field) || QueryUtils._VAL.equalsIgnoreCase(field);
}
/**
* Adds field to text index.
*
* @param field Field name.
+ * @throws IgniteCheckedException If failed.
*/
- public void addFieldToTextIndex(String field) {
- if (fullTextIdx == null) {
- fullTextIdx = new QueryIndexDescriptorImpl(QueryIndexType.FULLTEXT, 0);
-
- indexes.put(null, fullTextIdx);
- }
+ public void addFieldToTextIndex(String field) throws IgniteCheckedException {
+ if (fullTextIdx == null)
+ fullTextIdx = new QueryIndexDescriptorImpl(this, null, QueryIndexType.FULLTEXT, 0);
fullTextIdx.addField(field, 0, false);
}
@@ -335,6 +383,34 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
this.affKey = affKey;
}
+ /**
+ * @return Aliases.
+ */
+ public Map<String, String> aliases() {
+ return aliases != null ? aliases : Collections.<String, String>emptyMap();
+ }
+
+ /**
+ * @param aliases Aliases.
+ */
+ public void aliases(Map<String, String> aliases) {
+ this.aliases = aliases;
+ }
+
+ /**
+ * @return {@code True} if obsolete.
+ */
+ public boolean obsolete() {
+ return obsolete;
+ }
+
+ /**
+ * Mark index as obsolete.
+ */
+ public void markObsolete() {
+ obsolete = true;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(QueryTypeDescriptorImpl.class, this);
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
index f00cbd6..3a7437b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
@@ -29,6 +29,7 @@ import org.apache.ignite.internal.processors.cache.CacheObjectContext;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheDefaultAffinityKeyMapper;
import org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
import org.apache.ignite.internal.processors.query.property.QueryBinaryProperty;
import org.apache.ignite.internal.processors.query.property.QueryClassProperty;
import org.apache.ignite.internal.processors.query.property.QueryFieldAccessor;
@@ -44,7 +45,6 @@ import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Time;
import java.sql.Timestamp;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -52,6 +52,9 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_INDEXING_DISCOVERY_HISTORY_SIZE;
+import static org.apache.ignite.IgniteSystemProperties.getInteger;
+
/**
* Utility methods for queries.
*/
@@ -59,6 +62,9 @@ public class QueryUtils {
/** */
public static final String _VAL = "_val";
+ /** Discovery history size. */
+ private static final int DISCO_HIST_SIZE = getInteger(IGNITE_INDEXING_DISCOVERY_HISTORY_SIZE, 1000);
+
/** */
private static final Class<?> GEOMETRY_CLASS = U.classForName("com.vividsolutions.jts.geom.Geometry", null);
@@ -82,6 +88,69 @@ public class QueryUtils {
));
/**
+ * Get table name for entity.
+ *
+ * @param entity Entity.
+ * @return Table name.
+ */
+ public static String tableName(QueryEntity entity) {
+ String res = entity.getTableName();
+
+ if (res == null)
+ res = typeName(entity.getValueType());
+
+ return res;
+ }
+
+ /**
+ * Get index name.
+ *
+ * @param entity Query entity.
+ * @param idx Index.
+ * @return Index name.
+ */
+ public static String indexName(QueryEntity entity, QueryIndex idx) {
+ return indexName(tableName(entity), idx);
+ }
+
+ /**
+ * Get index name.
+ *
+ * @param tblName Table name.
+ * @param idx Index.
+ * @return Index name.
+ */
+ public static String indexName(String tblName, QueryIndex idx) {
+ String res = idx.getName();
+
+ if (res == null) {
+ StringBuilder idxName = new StringBuilder(tblName + "_");
+
+ for (Map.Entry<String, Boolean> field : idx.getFields().entrySet()) {
+ idxName.append(field.getKey());
+
+ idxName.append('_');
+ idxName.append(field.getValue() ? "asc_" : "desc_");
+ }
+
+ for (int i = 0; i < idxName.length(); i++) {
+ char ch = idxName.charAt(i);
+
+ if (Character.isWhitespace(ch))
+ idxName.setCharAt(i, '_');
+ else
+ idxName.setCharAt(i, Character.toLowerCase(ch));
+ }
+
+ idxName.append("idx");
+
+ return idxName.toString();
+ }
+
+ return res;
+ }
+
+ /**
* Create type candidate for query entity.
*
* @param space Space.
@@ -103,7 +172,9 @@ public class QueryUtils {
CacheObjectContext coCtx = binaryEnabled ? ctx.cacheObjects().contextForCache(ccfg) : null;
- QueryTypeDescriptorImpl desc = new QueryTypeDescriptorImpl();
+ QueryTypeDescriptorImpl desc = new QueryTypeDescriptorImpl(space);
+
+ desc.aliases(qryEntity.getAliases());
// Key and value classes still can be available if they are primitive or JDK part.
// We need that to set correct types for _key and _val columns.
@@ -206,11 +277,6 @@ public class QueryUtils {
*/
public static void processBinaryMeta(GridKernalContext ctx, QueryEntity qryEntity, QueryTypeDescriptorImpl d)
throws IgniteCheckedException {
- Map<String,String> aliases = qryEntity.getAliases();
-
- if (aliases == null)
- aliases = Collections.emptyMap();
-
Set<String> keyFields = qryEntity.getKeyFields();
// We have to distinguish between empty and null keyFields when the key is not of SQL type -
@@ -239,7 +305,7 @@ public class QueryUtils {
isKeyField = (hasKeyFields ? keyFields.contains(entry.getKey()) : null);
QueryBinaryProperty prop = buildBinaryProperty(ctx, entry.getKey(),
- U.classForName(entry.getValue(), Object.class, true), aliases, isKeyField);
+ U.classForName(entry.getValue(), Object.class, true), d.aliases(), isKeyField);
d.addProperty(prop, false);
}
@@ -256,18 +322,13 @@ public class QueryUtils {
*/
public static void processClassMeta(QueryEntity qryEntity, QueryTypeDescriptorImpl d, CacheObjectContext coCtx)
throws IgniteCheckedException {
- Map<String,String> aliases = qryEntity.getAliases();
-
- if (aliases == null)
- aliases = Collections.emptyMap();
-
for (Map.Entry<String, String> entry : qryEntity.getFields().entrySet()) {
QueryClassProperty prop = buildClassProperty(
d.keyClass(),
d.valueClass(),
entry.getKey(),
U.classForName(entry.getValue(), Object.class),
- aliases,
+ d.aliases(),
coCtx);
d.addProperty(prop, false);
@@ -275,7 +336,7 @@ public class QueryUtils {
processIndexes(qryEntity, d);
}
-
+
/**
* Processes indexes based on query entity.
*
@@ -285,53 +346,90 @@ public class QueryUtils {
*/
private static void processIndexes(QueryEntity qryEntity, QueryTypeDescriptorImpl d) throws IgniteCheckedException {
if (!F.isEmpty(qryEntity.getIndexes())) {
- Map<String, String> aliases = qryEntity.getAliases();
+ for (QueryIndex idx : qryEntity.getIndexes())
+ processIndex(idx, d);
+ }
+ }
- if (aliases == null)
- aliases = Collections.emptyMap();
+ /**
+ * Process dynamic index change.
+ *
+ * @param idx Index.
+ * @param d Type descriptor to populate.
+ * @throws IgniteCheckedException If failed to build index information.
+ */
+ public static void processDynamicIndexChange(String idxName, @Nullable QueryIndex idx, QueryTypeDescriptorImpl d)
+ throws IgniteCheckedException {
+ d.dropIndex(idxName);
- for (QueryIndex idx : qryEntity.getIndexes()) {
- String idxName = idx.getName();
+ if (idx != null)
+ processIndex(idx, d);
+ }
- if (idxName == null)
- idxName = QueryEntity.defaultIndexName(idx);
+ /**
+ * Create index descriptor.
+ *
+ * @param typeDesc Type descriptor.
+ * @param idx Index.
+ * @return Index descriptor.
+ * @throws IgniteCheckedException If failed.
+ */
+ public static QueryIndexDescriptorImpl createIndexDescriptor(QueryTypeDescriptorImpl typeDesc, QueryIndex idx)
+ throws IgniteCheckedException {
+ String idxName = indexName(typeDesc.tableName(), idx);
+ QueryIndexType idxTyp = idx.getIndexType();
- QueryIndexType idxTyp = idx.getIndexType();
+ assert idxTyp == QueryIndexType.SORTED || idxTyp == QueryIndexType.GEOSPATIAL;
- if (idxTyp == QueryIndexType.SORTED || idxTyp == QueryIndexType.GEOSPATIAL) {
- d.addIndex(idxName, idxTyp, idx.getInlineSize());
+ QueryIndexDescriptorImpl res = new QueryIndexDescriptorImpl(typeDesc, idxName, idxTyp, idx.getInlineSize());
- int i = 0;
+ int i = 0;
- for (Map.Entry<String, Boolean> entry : idx.getFields().entrySet()) {
- String field = entry.getKey();
- boolean asc = entry.getValue();
+ for (Map.Entry<String, Boolean> entry : idx.getFields().entrySet()) {
+ String field = entry.getKey();
+ boolean asc = entry.getValue();
- String alias = aliases.get(field);
+ String alias = typeDesc.aliases().get(field);
- if (alias != null)
- field = alias;
+ if (alias != null)
+ field = alias;
- d.addFieldToIndex(idxName, field, i++, idx.getInlineSize(), !asc);
- }
- }
- else if (idxTyp == QueryIndexType.FULLTEXT){
- for (String field : idx.getFields().keySet()) {
- String alias = aliases.get(field);
+ res.addField(field, i++, !asc);
+ }
- if (alias != null)
- field = alias;
+ return res;
+ }
- d.addFieldToTextIndex(field);
- }
- }
- else if (idxTyp != null)
- throw new IllegalArgumentException("Unsupported index type [idx=" + idx.getName() +
- ", typ=" + idxTyp + ']');
- else
- throw new IllegalArgumentException("Index type is not set: " + idx.getName());
+ /**
+ * Process single index.
+ *
+ * @param idx Index.
+ * @param d Type descriptor to populate.
+ * @throws IgniteCheckedException If failed to build index information.
+ */
+ private static void processIndex(QueryIndex idx, QueryTypeDescriptorImpl d) throws IgniteCheckedException {
+ QueryIndexType idxTyp = idx.getIndexType();
+
+ if (idxTyp == QueryIndexType.SORTED || idxTyp == QueryIndexType.GEOSPATIAL) {
+ QueryIndexDescriptorImpl idxDesc = createIndexDescriptor(d, idx);
+
+ d.addIndex(idxDesc);
+ }
+ else if (idxTyp == QueryIndexType.FULLTEXT){
+ for (String field : idx.getFields().keySet()) {
+ String alias = d.aliases().get(field);
+
+ if (alias != null)
+ field = alias;
+
+ d.addFieldToTextIndex(field);
}
}
+ else if (idxTyp != null)
+ throw new IllegalArgumentException("Unsupported index type [idx=" + idx.getName() +
+ ", typ=" + idxTyp + ']');
+ else
+ throw new IllegalArgumentException("Index type is not set: " + idx.getName());
}
/**
@@ -674,6 +772,31 @@ public class QueryUtils {
}
/**
+ * Discovery history size.
+ *
+ * @return Discovery history size.
+ */
+ public static int discoveryHistorySize() {
+ return DISCO_HIST_SIZE;
+ }
+
+ /**
+ * Wrap schema exception if needed.
+ *
+ * @param e Original exception.
+ * @return Schema exception.
+ */
+ @Nullable public static SchemaOperationException wrapIfNeeded(@Nullable Exception e) {
+ if (e == null)
+ return null;
+
+ if (e instanceof SchemaOperationException)
+ return (SchemaOperationException)e;
+
+ return new SchemaOperationException("Unexpected exception.", e);
+ }
+
+ /**
* Private constructor.
*/
private QueryUtils() {
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaExchangeWorkerTask.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaExchangeWorkerTask.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaExchangeWorkerTask.java
new file mode 100644
index 0000000..f97f931
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaExchangeWorkerTask.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema;
+
+import org.apache.ignite.internal.processors.cache.CachePartitionExchangeWorkerTask;
+import org.apache.ignite.internal.processors.query.schema.message.SchemaAbstractDiscoveryMessage;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Cache schema change task for exchange worker.
+ */
+public class SchemaExchangeWorkerTask implements CachePartitionExchangeWorkerTask {
+ /** Message. */
+ private final SchemaAbstractDiscoveryMessage msg;
+
+ /**
+ * Constructor.
+ *
+ * @param msg Message.
+ */
+ public SchemaExchangeWorkerTask(SchemaAbstractDiscoveryMessage msg) {
+ assert msg != null;
+
+ this.msg = msg;
+ }
+
+ /**
+ * @return Message.
+ */
+ public SchemaAbstractDiscoveryMessage message() {
+ return msg;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaExchangeWorkerTask.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitor.java
new file mode 100644
index 0000000..3321e66
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitor.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema;
+
+import org.apache.ignite.IgniteCheckedException;
+
+/**
+ * Closure that internally applies given {@link SchemaIndexCacheVisitorClosure} to some set of entries.
+ */
+public interface SchemaIndexCacheVisitor {
+ /**
+ * Visit cache entries and pass them to closure.
+ *
+ * @param clo Closure.
+ * @throws IgniteCheckedException If failed.
+ */
+ public void visit(SchemaIndexCacheVisitorClosure clo) throws IgniteCheckedException;
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitorClosure.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitorClosure.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitorClosure.java
new file mode 100644
index 0000000..7f50089
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitorClosure.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+
+/**
+ * Index closure accepting current entry state.
+ */
+public interface SchemaIndexCacheVisitorClosure {
+ /**
+ * Apply closure.
+ *
+ * @param key Key.
+ * @param part Partition.
+ * @param val Value.
+ * @param ver Version.
+ * @param expiration Expiration.
+ * @param link Link.
+ * @throws IgniteCheckedException If failed.
+ */
+ public void apply(KeyCacheObject key, int part, CacheObject val, GridCacheVersion ver, long expiration, long link)
+ throws IgniteCheckedException;
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitorImpl.java
new file mode 100644
index 0000000..58c909d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitorImpl.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
+import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.database.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheAdapter;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.internal.util.lang.GridCursor;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.util.Collection;
+
+import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.EVICTED;
+import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.OWNING;
+import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.RENTING;
+
+/**
+ * Traversor operating all primary and backup partitions of given cache.
+ */
+public class SchemaIndexCacheVisitorImpl implements SchemaIndexCacheVisitor {
+ /** Query procssor. */
+ private final GridQueryProcessor qryProc;
+
+ /** Cache context. */
+ private final GridCacheContext cctx;
+
+ /** Space name. */
+ private final String spaceName;
+
+ /** Table name. */
+ private final String tblName;
+
+ /** Cancellation token. */
+ private final SchemaIndexOperationCancellationToken cancel;
+
+ /**
+ * Constructor.
+ *
+ * @param cctx Cache context.
+ * @param spaceName Space name.
+ * @param tblName Table name.
+ * @param cancel Cancellation token.
+ */
+ public SchemaIndexCacheVisitorImpl(GridQueryProcessor qryProc, GridCacheContext cctx, String spaceName,
+ String tblName, SchemaIndexOperationCancellationToken cancel) {
+ this.qryProc = qryProc;
+ this.spaceName = spaceName;
+ this.tblName = tblName;
+ this.cancel = cancel;
+
+ if (cctx.isNear())
+ cctx = ((GridNearCacheAdapter)cctx.cache()).dht().context();
+
+ this.cctx = cctx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void visit(SchemaIndexCacheVisitorClosure clo) throws IgniteCheckedException {
+ assert clo != null;
+
+ FilteringVisitorClosure filterClo = new FilteringVisitorClosure(clo);
+
+ Collection<GridDhtLocalPartition> parts = cctx.topology().localPartitions();
+
+ for (GridDhtLocalPartition part : parts)
+ processPartition(part, filterClo);
+ }
+
+ /**
+ * Process partition.
+ *
+ * @param part Partition.
+ * @param clo Index closure.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void processPartition(GridDhtLocalPartition part, FilteringVisitorClosure clo)
+ throws IgniteCheckedException {
+ checkCancelled();
+
+ boolean reserved = false;
+
+ if (part != null && part.state() != EVICTED)
+ reserved = (part.state() == OWNING || part.state() == RENTING) && part.reserve();
+
+ if (!reserved)
+ return;
+
+ try {
+ GridCursor<? extends CacheDataRow> cursor = part.dataStore().cursor();
+
+ while (cursor.next()) {
+ CacheDataRow row = cursor.get();
+
+ KeyCacheObject key = row.key();
+
+ processKey(key, row.link(), clo);
+ }
+ }
+ finally {
+ part.release();
+ }
+ }
+
+ /**
+ * Process single key.
+ *
+ * @param key Key.
+ * @param link Link.
+ * @param clo Closure.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void processKey(KeyCacheObject key, long link, FilteringVisitorClosure clo) throws IgniteCheckedException {
+ while (true) {
+ try {
+ checkCancelled();
+
+ GridCacheEntryEx entry = cctx.cache().entryEx(key);
+
+ try {
+ entry.updateIndex(clo, link);
+ }
+ finally {
+ cctx.evicts().touch(entry, AffinityTopologyVersion.NONE);
+ }
+
+ break;
+ }
+ catch (GridCacheEntryRemovedException ignored) {
+ // No-op.
+ }
+ }
+ }
+
+ /**
+ * Check if visit process is not cancelled.
+ *
+ * @throws IgniteCheckedException If cancelled.
+ */
+ private void checkCancelled() throws IgniteCheckedException {
+ if (cancel.isCancelled())
+ throw new IgniteCheckedException("Index creation was cancelled.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaIndexCacheVisitorImpl.class, this);
+ }
+
+ /**
+ * Filtering visitor closure.
+ */
+ private class FilteringVisitorClosure implements SchemaIndexCacheVisitorClosure {
+
+ /** Target closure. */
+ private final SchemaIndexCacheVisitorClosure target;
+
+ /**
+ * Constructor.
+ *
+ * @param target Target.
+ */
+ public FilteringVisitorClosure(SchemaIndexCacheVisitorClosure target) {
+ this.target = target;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void apply(KeyCacheObject key, int part, CacheObject val, GridCacheVersion ver,
+ long expiration, long link) throws IgniteCheckedException {
+ if (qryProc.belongsToTable(cctx, spaceName, tblName, key, val))
+ target.apply(key, part, val, ver, expiration, link);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexOperationCancellationToken.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexOperationCancellationToken.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexOperationCancellationToken.java
new file mode 100644
index 0000000..1bc3434
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexOperationCancellationToken.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema;
+
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Index operation cancellation token.
+ */
+public class SchemaIndexOperationCancellationToken {
+ /** Cancel flag. */
+ private final AtomicBoolean flag = new AtomicBoolean();
+
+ /**
+ * Get cancel state.
+ *
+ * @return {@code True} if cancelled.
+ */
+ public boolean isCancelled() {
+ return flag.get();
+ }
+
+ /**
+ * Do cancel.
+ *
+ * @return {@code True} if cancel flag was set by this call.
+ */
+ public boolean cancel() {
+ return flag.compareAndSet(false, true);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaIndexOperationCancellationToken.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaKey.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaKey.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaKey.java
new file mode 100644
index 0000000..3f12b77
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaKey.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema;
+
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.lang.IgniteUuid;
+
+/**
+ * Schema key.
+ */
+public class SchemaKey {
+ /** Space. */
+ private final String space;
+
+ /** Deployment ID. */
+ private final IgniteUuid depId;
+
+ /**
+ * Constructor.
+ *
+ * @param space Space.
+ * @param depId Deployment ID.
+ */
+ public SchemaKey(String space, IgniteUuid depId) {
+ this.space = space;
+ this.depId = depId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return 31 * (space != null ? space.hashCode() : 0) + depId.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object obj) {
+ if (obj instanceof SchemaKey) {
+ SchemaKey other = (SchemaKey)obj;
+
+ return F.eq(space, other.space) && F.eq(depId, other.depId);
+ }
+
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaNodeLeaveExchangeWorkerTask.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaNodeLeaveExchangeWorkerTask.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaNodeLeaveExchangeWorkerTask.java
new file mode 100644
index 0000000..79fbfcd
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaNodeLeaveExchangeWorkerTask.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema;
+
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.processors.cache.CachePartitionExchangeWorkerTask;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Node leave exchange worker task.
+ */
+public class SchemaNodeLeaveExchangeWorkerTask implements CachePartitionExchangeWorkerTask {
+ /** Node. */
+ @GridToStringInclude
+ private final ClusterNode node;
+
+ /**
+ * Constructor.
+ *
+ * @param node Node.
+ */
+ public SchemaNodeLeaveExchangeWorkerTask(ClusterNode node) {
+ this.node = node;
+ }
+
+ /**
+ * @return Node.
+ */
+ public ClusterNode node() {
+ return node;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaNodeLeaveExchangeWorkerTask.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationClientFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationClientFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationClientFuture.java
new file mode 100644
index 0000000..6c47aff
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationClientFuture.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema;
+
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.util.UUID;
+
+/**
+ * Schema operation client future.
+ */
+public class SchemaOperationClientFuture extends GridFutureAdapter<Object> {
+ /** Operation ID. */
+ private final UUID opId;
+
+ /**
+ * Constructor.
+ *
+ * @param opId Operation ID.
+ */
+ public SchemaOperationClientFuture(UUID opId) {
+ this.opId = opId;
+ }
+
+ /**
+ * @return Operation ID.
+ */
+ public UUID operationId() {
+ return opId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaOperationClientFuture.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationException.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationException.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationException.java
new file mode 100644
index 0000000..f0db026
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationException.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Schema operation exception.
+ */
+public class SchemaOperationException extends IgniteCheckedException {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Code: generic error. */
+ public static final int CODE_GENERIC = 0;
+
+ /** Code: cache not found. */
+ public static final int CODE_CACHE_NOT_FOUND = 1;
+
+ /** Code: table not found. */
+ public static final int CODE_TABLE_NOT_FOUND = 2;
+
+ /** Code: table already exists. */
+ public static final int CODE_TABLE_EXISTS = 3;
+
+ /** Code: column not found. */
+ public static final int CODE_COLUMN_NOT_FOUND = 4;
+
+ /** Code: column already exists. */
+ public static final int CODE_COLUMN_EXISTS = 5;
+
+ /** Code: index not found. */
+ public static final int CODE_INDEX_NOT_FOUND = 6;
+
+ /** Code: index already exists. */
+ public static final int CODE_INDEX_EXISTS = 7;
+
+ /** Error code. */
+ private final int code;
+
+ /**
+ * Constructor for specific error type.
+ *
+ * @param code Code.
+ * @param objName Object name.
+ */
+ public SchemaOperationException(int code, String objName) {
+ super(message(code, objName));
+
+ this.code = code;
+ }
+
+ /**
+ * Constructor for generic error.
+ *
+ * @param msg Message.
+ */
+ public SchemaOperationException(String msg) {
+ this(msg, null);
+ }
+
+ /**
+ * Constructor for generic error.
+ *
+ * @param msg Message.
+ * @param cause Cause.
+ */
+ public SchemaOperationException(String msg, Throwable cause) {
+ super(msg, cause);
+
+ code = CODE_GENERIC;
+ }
+
+ /**
+ * @return Code.
+ */
+ public int code() {
+ return code;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaOperationException.class, this, "msg", getMessage());
+ }
+
+ /**
+ * Create message for specific code and object name.
+ *
+ * @param code Code.
+ * @param objName Object name.
+ * @return Message.
+ */
+ private static String message(int code, String objName) {
+ switch (code) {
+ case CODE_CACHE_NOT_FOUND:
+ return "Cache doesn't exist: " + objName;
+
+ case CODE_TABLE_NOT_FOUND:
+ return "Table doesn't exist: " + objName;
+
+ case CODE_TABLE_EXISTS:
+ return "Table already exists: " + objName;
+
+ case CODE_COLUMN_NOT_FOUND:
+ return "Column doesn't exist: " + objName;
+
+ case CODE_COLUMN_EXISTS:
+ return "Column already exists: " + objName;
+
+ case CODE_INDEX_NOT_FOUND:
+ return "Index doesn't exist: " + objName;
+
+ case CODE_INDEX_EXISTS:
+ return "Index already exists: " + objName;
+
+ default:
+ assert false;
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationManager.java
new file mode 100644
index 0000000..eb0f3cd
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationManager.java
@@ -0,0 +1,292 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema;
+
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Schema operation manager.
+ */
+@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+public class SchemaOperationManager {
+ /** Kernal context. */
+ private final GridKernalContext ctx;
+
+ /** Query processor. */
+ private final GridQueryProcessor qryProc;
+
+ /** Logger. */
+ private final IgniteLogger log;
+
+ /** Operation handler. */
+ private final SchemaOperationWorker worker;
+
+ /** Mutex for concurrency control. */
+ private final Object mux = new Object();
+
+ /** Participants. */
+ private Collection<UUID> nodeIds;
+
+ /** Node results. */
+ private Map<UUID, SchemaOperationException> nodeRess;
+
+ /** Current coordinator node. */
+ private ClusterNode crd;
+
+ /** Whether coordinator state is mapped. */
+ private boolean crdMapped;
+
+ /** Coordinator finished flag. */
+ private boolean crdFinished;
+
+ /**
+ * Constructor.
+ *
+ * @param ctx Context.
+ * @param qryProc Query processor.
+ * @param worker Operation handler.
+ * @param crd Coordinator node.
+ */
+ public SchemaOperationManager(GridKernalContext ctx, GridQueryProcessor qryProc, SchemaOperationWorker worker,
+ @Nullable ClusterNode crd) {
+ assert !ctx.clientNode() || crd == null;
+
+ this.ctx = ctx;
+
+ log = ctx.log(SchemaOperationManager.class);
+
+ this.qryProc = qryProc;
+ this.worker = worker;
+
+ synchronized (mux) {
+ this.crd = crd;
+
+ prepareCoordinator();
+ }
+ }
+
+ /**
+ * Map operation handling.
+ */
+ @SuppressWarnings("unchecked")
+ public void start() {
+ worker.start();
+
+ synchronized (mux) {
+ worker.future().listen(new IgniteInClosure<IgniteInternalFuture>() {
+ @Override public void apply(IgniteInternalFuture fut) {
+ onLocalNodeFinished(fut);
+ }
+ });
+ }
+ }
+
+ /**
+ * Handle local node finish.
+ *
+ * @param fut Future.
+ */
+ private void onLocalNodeFinished(IgniteInternalFuture fut) {
+ assert fut.isDone();
+
+ if (ctx.clientNode())
+ return;
+
+ SchemaOperationException err;
+
+ try {
+ fut.get();
+
+ err = null;
+ }
+ catch (Exception e) {
+ err = QueryUtils.wrapIfNeeded(e);
+ }
+
+ synchronized (mux) {
+ if (isLocalCoordinator())
+ onNodeFinished(ctx.localNodeId(), err);
+ else
+ qryProc.sendStatusMessage(crd.id(), operationId(), err);
+ }
+ }
+
+ /**
+ * Handle node finish.
+ *
+ * @param nodeId Node ID.
+ * @param err Error.
+ */
+ public void onNodeFinished(UUID nodeId, @Nullable SchemaOperationException err) {
+ synchronized (mux) {
+ assert isLocalCoordinator();
+
+ if (nodeRess.containsKey(nodeId)) {
+ if (log.isDebugEnabled())
+ log.debug("Received duplicate result [opId=" + operationId() + ", nodeId=" + nodeId +
+ ", err=" + err + ']');
+
+ return;
+ }
+
+ if (nodeIds.contains(nodeId)) {
+ if (log.isDebugEnabled())
+ log.debug("Received result [opId=" + operationId() + ", nodeId=" + nodeId + ", err=" + err + ']');
+
+ nodeRess.put(nodeId, err);
+ }
+ else {
+ if (log.isDebugEnabled())
+ log.debug("Received result from non-tracked node (joined after operation started, will ignore) " +
+ "[opId=" + operationId() + ", nodeId=" + nodeId + ", err=" + err + ']');
+ }
+
+ checkFinished();
+ }
+ }
+
+ /**
+ * Handle node leave event.
+ *
+ * @param nodeId ID of the node that has left the grid.
+ * @param curCrd Current coordinator node.
+ */
+ public void onNodeLeave(UUID nodeId, ClusterNode curCrd) {
+ synchronized (mux) {
+ assert crd != null;
+
+ if (F.eq(nodeId, crd.id())) {
+ // Coordinator has left!
+ crd = curCrd;
+
+ prepareCoordinator();
+ }
+ else if (isLocalCoordinator()) {
+ // Other node has left, remove it from the coordinator's wait set.
+ // Handle this as success.
+ if (nodeIds.remove(nodeId))
+ nodeRess.remove(nodeId);
+ }
+
+ IgniteInternalFuture fut = worker().future();
+
+ if (fut.isDone())
+ onLocalNodeFinished(fut);
+
+ checkFinished();
+ }
+ }
+
+ /**
+ * Check if operation finished.
+ */
+ private void checkFinished() {
+ assert Thread.holdsLock(mux);
+
+ if (isLocalCoordinator()) {
+ if (crdFinished)
+ return;
+
+ if (nodeIds.size() == nodeRess.size()) {
+ // Initiate finish request.
+ SchemaOperationException err = null;
+
+ for (Map.Entry<UUID, SchemaOperationException> nodeRes : nodeRess.entrySet()) {
+ if (nodeRes.getValue() != null) {
+ err = nodeRes.getValue();
+
+ break;
+ }
+ }
+
+ if (log.isDebugEnabled())
+ log.debug("Collected all results, about to send finish message [opId=" + operationId() +
+ ", err=" + err + ']');
+
+ crdFinished = true;
+
+ qryProc.onCoordinatorFinished(worker.operation(), err);
+ }
+ }
+ }
+
+ /**
+ * Prepare topology state in case local node is coordinator.
+ *
+ * @return {@code True} if state was changed by this call.
+ */
+ private boolean prepareCoordinator() {
+ if (isLocalCoordinator() && !crdMapped) {
+ // Initialize local structures.
+ nodeIds = new HashSet<>();
+ nodeRess = new HashMap<>();
+
+ for (ClusterNode alive : ctx.discovery().aliveServerNodes())
+ nodeIds.add(alive.id());
+
+ if (log.isDebugEnabled())
+ log.debug("Mapped participating nodes on coordinator [opId=" + operationId() +
+ ", crdNodeId=" + ctx.localNodeId() + ", nodes=" + nodeIds + ']');
+
+ crdMapped = true;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if current node is local coordinator.
+ *
+ * @return {@code True} if coordinator.
+ */
+ private boolean isLocalCoordinator() {
+ assert Thread.holdsLock(mux);
+
+ return crd != null && crd.isLocal();
+ }
+
+ /**
+ * @return Worker.
+ */
+ public SchemaOperationWorker worker() {
+ return worker;
+ }
+
+ /**
+ * @return Operation ID.
+ */
+ private UUID operationId() {
+ return worker.operation().id();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationWorker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationWorker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationWorker.java
new file mode 100644
index 0000000..06feecb
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaOperationWorker.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema;
+
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.internal.processors.query.QueryTypeDescriptorImpl;
+import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.processors.query.schema.operation.SchemaAbstractOperation;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.worker.GridWorker;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.thread.IgniteThread;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Schema operation executor.
+ */
+public class SchemaOperationWorker extends GridWorker {
+ /** Query processor */
+ private final GridQueryProcessor qryProc;
+
+ /** Deployment ID. */
+ private final IgniteUuid depId;
+
+ /** Target operation. */
+ private final SchemaAbstractOperation op;
+
+ /** No-op flag. */
+ private final boolean nop;
+
+ /** Whether cache started. */
+ private final boolean cacheRegistered;
+
+ /** Type descriptor. */
+ private final QueryTypeDescriptorImpl type;
+
+ /** Operation future. */
+ private final GridFutureAdapter fut;
+
+ /** Public operation future. */
+ private final GridFutureAdapter pubFut;
+
+ /** Start guard. */
+ private final AtomicBoolean startGuard = new AtomicBoolean();
+
+ /** Cancellation token. */
+ private final SchemaIndexOperationCancellationToken cancelToken = new SchemaIndexOperationCancellationToken();
+
+ /**
+ * Constructor.
+ *
+ * @param ctx Context.
+ * @param qryProc Query processor.
+ * @param depId Deployment ID.
+ * @param op Target operation.
+ * @param nop No-op flag.
+ * @param err Predefined error.
+ * @param cacheRegistered Whether cache is registered in indexing at this point.
+ * @param type Type descriptor (if available).
+ */
+ public SchemaOperationWorker(GridKernalContext ctx, GridQueryProcessor qryProc, IgniteUuid depId,
+ SchemaAbstractOperation op, boolean nop, @Nullable SchemaOperationException err, boolean cacheRegistered,
+ @Nullable QueryTypeDescriptorImpl type) {
+ super(ctx.igniteInstanceName(), workerName(op), ctx.log(SchemaOperationWorker.class));
+
+ this.qryProc = qryProc;
+ this.depId = depId;
+ this.op = op;
+ this.nop = nop;
+ this.cacheRegistered = cacheRegistered;
+ this.type = type;
+
+ fut = new GridFutureAdapter();
+
+ if (err != null)
+ fut.onDone(err);
+ else if (nop || !cacheRegistered)
+ fut.onDone();
+
+ pubFut = publicFuture(fut);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void body() throws InterruptedException, IgniteInterruptedCheckedException {
+ try {
+ // Execute.
+ qryProc.processIndexOperationLocal(op, type, depId, cancelToken);
+
+ fut.onDone();
+ }
+ catch (Exception e) {
+ fut.onDone(QueryUtils.wrapIfNeeded(e));
+ }
+ }
+
+ /**
+ * Perform initialization routine.
+ *
+ * @return This instance.
+ */
+ public SchemaOperationWorker start() {
+ if (startGuard.compareAndSet(false, true)) {
+ if (!fut.isDone())
+ new IgniteThread(this).start();
+ }
+
+ return this;
+ }
+
+ /**
+ * Chain the future making sure that operation is completed after local schema is updated.
+ *
+ * @param fut Current future.
+ * @return Chained future.
+ */
+ @SuppressWarnings("unchecked")
+ private GridFutureAdapter<?> publicFuture(GridFutureAdapter fut) {
+ final GridFutureAdapter<?> chainedFut = new GridFutureAdapter<>();
+
+ fut.listen(new IgniteInClosure<IgniteInternalFuture>() {
+ @Override public void apply(IgniteInternalFuture fut) {
+ Exception err = null;
+
+ try {
+ fut.get();
+
+ if (cacheRegistered && !nop)
+ qryProc.onLocalOperationFinished(op, type);
+ }
+ catch (Exception e) {
+ err = e;
+ }
+ finally {
+ chainedFut.onDone(null, err);
+ }
+ }
+ });
+
+ return chainedFut;
+ }
+
+ /**
+ * @return No-op flag.
+ */
+ public boolean nop() {
+ return nop;
+ }
+
+ /**
+ * @return Whether cache is registered.
+ */
+ public boolean cacheRegistered() {
+ return cacheRegistered;
+ }
+
+ /**
+ * Cancel operation.
+ */
+ public void cancel() {
+ if (cancelToken.cancel())
+ super.cancel();
+ }
+
+ /**
+ * @return Operation.
+ */
+ public SchemaAbstractOperation operation() {
+ return op;
+ }
+
+ /**
+ * @return Future completed when operation is ready.
+ */
+ public IgniteInternalFuture future() {
+ return pubFut;
+ }
+
+ /**
+ * @return Worker name.
+ */
+ private static String workerName(SchemaAbstractOperation op) {
+ return "schema-op-worker-" + op.id();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaAbstractDiscoveryMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaAbstractDiscoveryMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaAbstractDiscoveryMessage.java
new file mode 100644
index 0000000..9fdc6c3
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaAbstractDiscoveryMessage.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema.message;
+
+import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage;
+import org.apache.ignite.internal.processors.query.schema.operation.SchemaAbstractOperation;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteUuid;
+
+/**
+ * Abstract discovery message for schema operations.
+ */
+public abstract class SchemaAbstractDiscoveryMessage implements DiscoveryCustomMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** ID */
+ private final IgniteUuid id = IgniteUuid.randomUuid();
+
+ /** Operation. */
+ @GridToStringInclude
+ protected final SchemaAbstractOperation op;
+
+ /**
+ * Constructor.
+ *
+ * @param op Operation.
+ */
+ protected SchemaAbstractDiscoveryMessage(SchemaAbstractOperation op) {
+ this.op = op;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteUuid id() {
+ return id;
+ }
+
+ /**
+ * @return Operation.
+ */
+ public SchemaAbstractOperation operation() {
+ return op;
+ }
+
+ /**
+ * @return Whether request must be propagated to exchange thread.
+ */
+ public abstract boolean exchange();
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaAbstractDiscoveryMessage.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaFinishDiscoveryMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaFinishDiscoveryMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaFinishDiscoveryMessage.java
new file mode 100644
index 0000000..2245b24
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaFinishDiscoveryMessage.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema.message;
+
+import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
+import org.apache.ignite.internal.processors.query.schema.operation.SchemaAbstractOperation;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Schema change finish discovery message.
+ */
+public class SchemaFinishDiscoveryMessage extends SchemaAbstractDiscoveryMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Error. */
+ private final SchemaOperationException err;
+
+ /** Original propose message. */
+ private transient SchemaProposeDiscoveryMessage proposeMsg;
+
+ /**
+ * Constructor.
+ *
+ * @param op Original operation.
+ * @param err Error.
+ */
+ public SchemaFinishDiscoveryMessage(SchemaAbstractOperation op, SchemaOperationException err) {
+ super(op);
+
+ this.err = err;
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public DiscoveryCustomMessage ackMessage() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isMutable() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean exchange() {
+ return false;
+ }
+
+ /**
+ * @return {@code True} if error was reported during init.
+ */
+ public boolean hasError() {
+ return err != null;
+ }
+
+ /**
+ * @return Error message (if any).
+ */
+ @Nullable public SchemaOperationException error() {
+ return err;
+ }
+
+ /**
+ * @return Propose message.
+ */
+ public SchemaProposeDiscoveryMessage proposeMessage() {
+ return proposeMsg;
+ }
+
+ /**
+ * @param proposeMsg Propose message.
+ */
+ public void proposeMessage(SchemaProposeDiscoveryMessage proposeMsg) {
+ this.proposeMsg = proposeMsg;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaFinishDiscoveryMessage.class, this, "parent", super.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaOperationStatusMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaOperationStatusMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaOperationStatusMessage.java
new file mode 100644
index 0000000..5f75e60
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaOperationStatusMessage.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema.message;
+
+import org.apache.ignite.internal.GridDirectTransient;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+import org.jetbrains.annotations.Nullable;
+
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Schema operation status message.
+ */
+public class SchemaOperationStatusMessage implements Message {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Operation ID. */
+ private UUID opId;
+
+ /** Error bytes (if any). */
+ private byte[] errBytes;
+
+ /** Sender node ID. */
+ @GridDirectTransient
+ private UUID sndNodeId;
+
+ /**
+ * Default constructor.
+ */
+ public SchemaOperationStatusMessage() {
+ // No-op.
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param opId Operation ID.
+ * @param errBytes Error bytes.
+ */
+ public SchemaOperationStatusMessage(UUID opId, byte[] errBytes) {
+ this.opId = opId;
+ this.errBytes = errBytes;
+ }
+
+ /**
+ * @return Operation ID.
+ */
+ public UUID operationId() {
+ return opId;
+ }
+
+ /**
+ * @return Error bytes.
+ */
+ @Nullable public byte[] errorBytes() {
+ return errBytes;
+ }
+
+ /**
+ * @return Sender node ID.
+ */
+ public UUID senderNodeId() {
+ return sndNodeId;
+ }
+
+ /**
+ * @param sndNodeId Sender node ID.
+ */
+ public void senderNodeId(UUID sndNodeId) {
+ this.sndNodeId = sndNodeId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeUuid("opId", opId))
+ return false;
+
+ writer.incrementState();
+
+ case 1:
+ if (!writer.writeByteArray("errBytes", errBytes))
+ return false;
+
+ writer.incrementState();
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ opId = reader.readUuid("opId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 1:
+ errBytes = reader.readByteArray("errBytes");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+ }
+
+ return reader.afterMessageRead(SchemaOperationStatusMessage.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return -53;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaOperationStatusMessage.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaProposeDiscoveryMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaProposeDiscoveryMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaProposeDiscoveryMessage.java
new file mode 100644
index 0000000..664ee03
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/message/SchemaProposeDiscoveryMessage.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema.message;
+
+import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage;
+import org.apache.ignite.internal.processors.query.schema.SchemaKey;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
+import org.apache.ignite.internal.processors.query.schema.operation.SchemaAbstractOperation;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteUuid;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Schema change propose discovery message.
+ */
+public class SchemaProposeDiscoveryMessage extends SchemaAbstractDiscoveryMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Cache deployment ID. */
+ private IgniteUuid depId;
+
+ /** Error. */
+ private SchemaOperationException err;
+
+ /** Whether to perform exchange. */
+ private transient boolean exchange;
+
+ /**
+ * Constructor.
+ *
+ * @param op Operation.
+ */
+ public SchemaProposeDiscoveryMessage(SchemaAbstractOperation op) {
+ super(op);
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public DiscoveryCustomMessage ackMessage() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isMutable() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean exchange() {
+ return exchange;
+ }
+
+ /**
+ * @param exchange Whether to perform exchange.
+ */
+ public void exchange(boolean exchange) {
+ this.exchange = exchange;
+ }
+
+ /**
+ * @return Deployment ID.
+ */
+ @Nullable public IgniteUuid deploymentId() {
+ return depId;
+ }
+
+ /**
+ * @param depId Deployment ID.
+ */
+ public void deploymentId(IgniteUuid depId) {
+ this.depId = depId;
+ }
+
+ /**
+ *
+ * @return {@code True} if message is initialized.
+ */
+ public boolean initialized() {
+ return deploymentId() != null || hasError();
+ }
+
+ /**
+ * Set error.
+ *
+ * @param err Error.
+ */
+ public void onError(SchemaOperationException err) {
+ if (!hasError()) {
+ this.err = err;
+ }
+ }
+
+ /**
+ * @return {@code True} if error was reported during init.
+ */
+ public boolean hasError() {
+ return err != null;
+ }
+
+ /**
+ * @return Error message (if any).
+ */
+ @Nullable public SchemaOperationException error() {
+ return err;
+ }
+
+ /**
+ * @return Schema key.
+ */
+ public SchemaKey schemaKey() {
+ return new SchemaKey(operation().space(), depId);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaProposeDiscoveryMessage.class, this, "parent", super.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaAbstractOperation.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaAbstractOperation.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaAbstractOperation.java
new file mode 100644
index 0000000..8418ece
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaAbstractOperation.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema.operation;
+
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.io.Serializable;
+import java.util.UUID;
+
+/**
+ * Abstract operation on schema.
+ */
+public abstract class SchemaAbstractOperation implements Serializable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Operation ID. */
+ private final UUID opId;
+
+ /** Space. */
+ private final String space;
+
+ /**
+ * Constructor.
+ *
+ * @param opId Operation ID.
+ * @param space Space.
+ */
+ public SchemaAbstractOperation(UUID opId, String space) {
+ this.opId = opId;
+ this.space = space;
+ }
+
+ /**
+ * @return Operation id.
+ */
+ public UUID id() {
+ return opId;
+ }
+
+ /**
+ * @return Space.
+ */
+ public String space() {
+ return space;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaAbstractOperation.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaIndexAbstractOperation.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaIndexAbstractOperation.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaIndexAbstractOperation.java
new file mode 100644
index 0000000..fc4a9ff
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaIndexAbstractOperation.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema.operation;
+
+import java.util.UUID;
+
+/**
+ * Schema index abstract operation.
+ */
+public abstract class SchemaIndexAbstractOperation extends SchemaAbstractOperation {
+ /**
+ * Constructor.
+ *
+ * @param opId Operation ID.
+ * @param space Space.
+ */
+ public SchemaIndexAbstractOperation(UUID opId, String space) {
+ super(opId, space);
+ }
+
+ /**
+ * @return Index name.
+ */
+ public abstract String indexName();
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaIndexCreateOperation.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaIndexCreateOperation.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaIndexCreateOperation.java
new file mode 100644
index 0000000..9281f2a
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaIndexCreateOperation.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema.operation;
+
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.util.UUID;
+
+/**
+ * Schema index create operation.
+ */
+public class SchemaIndexCreateOperation extends SchemaIndexAbstractOperation {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Table name. */
+ private final String tblName;
+
+ /** Index. */
+ @GridToStringInclude
+ private final QueryIndex idx;
+
+ /** Ignore operation if index exists. */
+ private final boolean ifNotExists;
+
+ /**
+ * Constructor.
+ *
+ * @param opId Operation id.
+ * @param space Space.
+ * @param tblName Table name.
+ * @param idx Index params.
+ * @param ifNotExists Ignore operation if index exists.
+ */
+ public SchemaIndexCreateOperation(UUID opId, String space, String tblName, QueryIndex idx, boolean ifNotExists) {
+ super(opId, space);
+
+ this.tblName = tblName;
+ this.idx = idx;
+ this.ifNotExists = ifNotExists;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String indexName() {
+ return QueryUtils.indexName(tblName, idx);
+ }
+
+ /**
+ * @return Table name.
+ */
+ public String tableName() {
+ return tblName;
+ }
+
+ /**
+ * @return Index params.
+ */
+ public QueryIndex index() {
+ return idx;
+ }
+
+ /**
+ * @return Ignore operation if index exists.
+ */
+ public boolean ifNotExists() {
+ return ifNotExists;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaIndexCreateOperation.class, this, "parent", super.toString());
+ }
+}
[7/8] ignite git commit: IGNITE-4565: Implemented CREATE INDEX and
DROP INDEX. This closes #1773. This closes #1804.
Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
index 7841f06..da6ebc1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
@@ -60,7 +60,12 @@ import org.apache.ignite.configuration.NearCacheConfiguration;
import org.apache.ignite.configuration.TransactionConfiguration;
import org.apache.ignite.events.EventType;
import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.processors.query.QuerySchema;
import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.processors.query.schema.message.SchemaAbstractDiscoveryMessage;
+import org.apache.ignite.internal.processors.query.schema.SchemaExchangeWorkerTask;
+import org.apache.ignite.internal.processors.query.schema.SchemaNodeLeaveExchangeWorkerTask;
+import org.apache.ignite.internal.processors.query.schema.message.SchemaProposeDiscoveryMessage;
import org.apache.ignite.internal.suggestions.GridPerformanceSuggestions;
import org.apache.ignite.internal.IgniteClientDisconnectedCheckedException;
import org.apache.ignite.internal.IgniteComponentType;
@@ -343,6 +348,13 @@ public class GridCacheProcessor extends GridProcessorAdapter {
* @return Task or {@code null} if message doesn't require any special processing.
*/
public CachePartitionExchangeWorkerTask exchangeTaskForCustomDiscoveryMessage(DiscoveryCustomMessage msg) {
+ if (msg instanceof SchemaAbstractDiscoveryMessage) {
+ SchemaAbstractDiscoveryMessage msg0 = (SchemaAbstractDiscoveryMessage)msg;
+
+ if (msg0.exchange())
+ return new SchemaExchangeWorkerTask(msg0);
+ }
+
return null;
}
@@ -352,7 +364,24 @@ public class GridCacheProcessor extends GridProcessorAdapter {
* @param task Task.
*/
public void processCustomExchangeTask(CachePartitionExchangeWorkerTask task) {
- // No-op.
+ if (task instanceof SchemaExchangeWorkerTask) {
+ SchemaAbstractDiscoveryMessage msg = ((SchemaExchangeWorkerTask)task).message();
+
+ if (msg instanceof SchemaProposeDiscoveryMessage) {
+ SchemaProposeDiscoveryMessage msg0 = (SchemaProposeDiscoveryMessage)msg;
+
+ ctx.query().onSchemaPropose(msg0);
+ }
+ else
+ U.warn(log, "Unsupported schema discovery message: " + msg);
+ }
+ else if (task instanceof SchemaNodeLeaveExchangeWorkerTask) {
+ SchemaNodeLeaveExchangeWorkerTask task0 = (SchemaNodeLeaveExchangeWorkerTask)task;
+
+ ctx.query().onNodeLeave(task0.node());
+ }
+ else
+ U.warn(log, "Unsupported custom exchange task: " + task);
}
/**
@@ -719,7 +748,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
cfg,
cacheType,
template,
- IgniteUuid.randomUuid());
+ IgniteUuid.randomUuid(),
+ new QuerySchema(cfg.getQueryEntities()));
desc.locallyConfigured(true);
desc.staticallyConfigured(true);
@@ -755,7 +785,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
cfg,
cacheType,
true,
- IgniteUuid.randomUuid());
+ IgniteUuid.randomUuid(),
+ new QuerySchema(cfg.getQueryEntities()));
desc0.locallyConfigured(true);
desc0.staticallyConfigured(true);
@@ -831,6 +862,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
// Must start database before start first cache.
sharedCtx.database().onKernalStart(false);
+ ctx.query().onCacheKernalStart();
+
// Start dynamic caches received from collect discovery data.
for (DynamicCacheDescriptor desc : cacheDescriptors()) {
if (ctx.config().isDaemon())
@@ -866,7 +899,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
caches.put(maskNull(name), cache);
- startCache(cache);
+ startCache(cache, desc.schema());
jCacheProxies.put(maskNull(name), new IgniteCacheProxy(ctx, cache, null, false));
}
@@ -1177,10 +1210,11 @@ public class GridCacheProcessor extends GridProcessorAdapter {
/**
* @param cache Cache to start.
+ * @param schema Cache schema.
* @throws IgniteCheckedException If failed to start cache.
*/
@SuppressWarnings({"TypeMayBeWeakened", "unchecked"})
- private void startCache(GridCacheAdapter<?, ?> cache) throws IgniteCheckedException {
+ private void startCache(GridCacheAdapter<?, ?> cache, QuerySchema schema) throws IgniteCheckedException {
GridCacheContext<?, ?> cacheCtx = cache.context();
ctx.continuous().onCacheStart(cacheCtx);
@@ -1220,7 +1254,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
cacheCtx.cache().start();
- ctx.query().onCacheStart(cacheCtx);
+ ctx.query().onCacheStart(cacheCtx, schema);
cacheCtx.onStarted();
@@ -1779,6 +1813,11 @@ public class GridCacheProcessor extends GridProcessorAdapter {
assert req.start() : req;
assert req.cacheType() != null : req;
+ DynamicCacheDescriptor desc = cacheDescriptor(req.cacheName());
+
+ if (desc != null)
+ desc.onStart();
+
prepareCacheStart(
req.startCacheConfiguration(),
req.nearCacheConfiguration(),
@@ -1786,13 +1825,9 @@ public class GridCacheProcessor extends GridProcessorAdapter {
req.clientStartOnly(),
req.initiatingNodeId(),
req.deploymentId(),
- topVer
+ topVer,
+ desc != null ? desc.schema() : null
);
-
- DynamicCacheDescriptor desc = cacheDescriptor(req.cacheName());
-
- if (desc != null)
- desc.onStart();
}
/**
@@ -1828,7 +1863,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
false,
null,
desc.deploymentId(),
- topVer
+ topVer,
+ desc.schema()
);
}
}
@@ -1845,6 +1881,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
* @param initiatingNodeId Initiating node ID.
* @param deploymentId Deployment ID.
* @param topVer Topology version.
+ * @param schema Query schema.
* @throws IgniteCheckedException If failed.
*/
private void prepareCacheStart(
@@ -1854,7 +1891,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
boolean clientStartOnly,
UUID initiatingNodeId,
IgniteUuid deploymentId,
- AffinityTopologyVersion topVer
+ AffinityTopologyVersion topVer,
+ @Nullable QuerySchema schema
) throws IgniteCheckedException {
CacheConfiguration ccfg = new CacheConfiguration(cfg);
@@ -1890,7 +1928,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
caches.put(maskNull(cacheCtx.name()), cache);
- startCache(cache);
+ startCache(cache, schema != null ? schema : new QuerySchema());
onKernalStart(cache);
}
@@ -2137,6 +2175,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
req.cacheType(desc.cacheType());
req.deploymentId(desc.deploymentId());
req.receivedFrom(desc.receivedFrom());
+ req.schema(desc.schema());
reqs.add(req);
}
@@ -2149,6 +2188,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
null);
req.startCacheConfiguration(desc.cacheConfiguration());
+ req.schema(desc.schema());
req.template(true);
@@ -2178,6 +2218,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
req.cacheType(desc.cacheType());
req.deploymentId(desc.deploymentId());
req.receivedFrom(desc.receivedFrom());
+ req.schema(desc.schema());
reqs.add(req);
@@ -2199,7 +2240,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
onDiscoDataReceived(
data.joiningNodeId(),
data.joiningNodeId(),
- (DynamicCacheChangeBatch) joiningNodeData);
+ (DynamicCacheChangeBatch) joiningNodeData, true);
}
}
@@ -2212,7 +2253,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
if (e.getValue() != null && e.getValue() instanceof DynamicCacheChangeBatch) {
DynamicCacheChangeBatch batch = (DynamicCacheChangeBatch) e.getValue();
- onDiscoDataReceived(data.joiningNodeId(), e.getKey(), batch);
+ onDiscoDataReceived(data.joiningNodeId(), e.getKey(), batch, false);
}
}
}
@@ -2222,8 +2263,9 @@ public class GridCacheProcessor extends GridProcessorAdapter {
* @param joiningNodeId Joining node id.
* @param rmtNodeId Rmt node id.
* @param batch Batch.
+ * @param join Whether this is data from joining node.
*/
- private void onDiscoDataReceived(UUID joiningNodeId, UUID rmtNodeId, DynamicCacheChangeBatch batch) {
+ private void onDiscoDataReceived(UUID joiningNodeId, UUID rmtNodeId, DynamicCacheChangeBatch batch, boolean join) {
if (batch.clientReconnect()) {
if (ctx.clientDisconnected()) {
if (clientReconnectReqs == null)
@@ -2253,7 +2295,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
ccfg,
req.cacheType(),
true,
- req.deploymentId());
+ req.deploymentId(),
+ req.schema());
registeredTemplates.put(maskNull(req.cacheName()), desc);
}
@@ -2275,6 +2318,10 @@ public class GridCacheProcessor extends GridProcessorAdapter {
if (existing.locallyConfigured()) {
existing.addRemoteConfiguration(rmtNodeId, req.startCacheConfiguration());
+ if (!join)
+ // Overwrite existing with remote.
+ existing.schema(req.schema());
+
ctx.discovery().setCacheFilter(
req.cacheName(),
ccfg.getNodeFilter(),
@@ -2290,7 +2337,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
ccfg,
req.cacheType(),
false,
- req.deploymentId());
+ req.deploymentId(),
+ req.schema());
// Received statically configured cache.
if (req.initiatingNodeId() == null)
@@ -2791,12 +2839,10 @@ public class GridCacheProcessor extends GridProcessorAdapter {
UUID.randomUUID(), cacheName, ctx.localNodeId());
req.startCacheConfiguration(cfg);
-
req.template(cfg.getName() != null && cfg.getName().endsWith("*"));
-
req.nearCacheConfiguration(cfg.getNearConfiguration());
-
req.deploymentId(IgniteUuid.randomUuid());
+ req.schema(new QuerySchema(cfg.getQueryEntities()));
if (CU.isUtilityCache(cacheName))
req.cacheType(CacheType.UTILITY);
@@ -2923,10 +2969,13 @@ public class GridCacheProcessor extends GridProcessorAdapter {
* @param topVer Current topology version.
* @return {@code True} if minor topology version should be increased.
*/
- public boolean onCustomEvent(
- DiscoveryCustomMessage msg,
- AffinityTopologyVersion topVer
- ) {
+ public boolean onCustomEvent(DiscoveryCustomMessage msg, AffinityTopologyVersion topVer) {
+ if (msg instanceof SchemaAbstractDiscoveryMessage) {
+ ctx.query().onDiscovery((SchemaAbstractDiscoveryMessage)msg);
+
+ return false;
+ }
+
if (msg instanceof CacheAffinityChangeMessage)
return sharedCtx.affinity().onCustomEvent(((CacheAffinityChangeMessage)msg));
@@ -2964,8 +3013,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
DynamicCacheDescriptor desc = registeredTemplates.get(maskNull(req.cacheName()));
if (desc == null) {
- DynamicCacheDescriptor templateDesc =
- new DynamicCacheDescriptor(ctx, ccfg, req.cacheType(), true, req.deploymentId());
+ DynamicCacheDescriptor templateDesc = new DynamicCacheDescriptor(ctx, ccfg, req.cacheType(), true,
+ req.deploymentId(), req.schema());
DynamicCacheDescriptor old = registeredTemplates.put(maskNull(ccfg.getName()), templateDesc);
@@ -3008,8 +3057,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
assert req.cacheType() != null : req;
assert F.eq(ccfg.getName(), req.cacheName()) : req;
- DynamicCacheDescriptor startDesc =
- new DynamicCacheDescriptor(ctx, ccfg, req.cacheType(), false, req.deploymentId());
+ DynamicCacheDescriptor startDesc = new DynamicCacheDescriptor(ctx, ccfg, req.cacheType(), false,
+ req.deploymentId(), req.schema());
if (newTopVer == null) {
newTopVer = new AffinityTopologyVersion(topVer.topologyVersion(),
@@ -3605,6 +3654,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
req.template(true);
req.startCacheConfiguration(cfg);
+ req.schema(new QuerySchema(cfg.getQueryEntities()));
req.deploymentId(IgniteUuid.randomUuid());
@@ -3769,16 +3819,28 @@ public class GridCacheProcessor extends GridProcessorAdapter {
*
* @throws IgniteCheckedException In case of error.
*/
- public void createMissingCaches() throws IgniteCheckedException {
+ public void createMissingQueryCaches() throws IgniteCheckedException {
for (Map.Entry<String, DynamicCacheDescriptor> e : registeredCaches.entrySet()) {
- CacheConfiguration ccfg = e.getValue().cacheConfiguration();
+ DynamicCacheDescriptor desc = e.getValue();
- if (!caches.containsKey(maskNull(ccfg.getName())) && QueryUtils.isEnabled(ccfg))
- dynamicStartCache(null, ccfg.getName(), null, false, true, true).get();
+ if (isMissingQueryCache(desc))
+ dynamicStartCache(null, desc.cacheConfiguration().getName(), null, false, true, true).get();
}
}
/**
+ * Whether cache defined by provided descriptor is not yet started and has queries enabled.
+ *
+ * @param desc Descriptor.
+ * @return {@code True} if this is missing query cache.
+ */
+ private boolean isMissingQueryCache(DynamicCacheDescriptor desc) {
+ CacheConfiguration ccfg = desc.cacheConfiguration();
+
+ return !caches.containsKey(maskNull(ccfg.getName())) && QueryUtils.isEnabled(ccfg);
+ }
+
+ /**
* Registers MBean for cache components.
*
* @param obj Cache component.
@@ -4008,6 +4070,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
req.deploymentId(desc.deploymentId());
req.startCacheConfiguration(descCfg);
+ req.schema(desc.schema());
}
}
else {
@@ -4020,6 +4083,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
initialize(cfg, cacheObjCtx);
req.startCacheConfiguration(cfg);
+ req.schema(new QuerySchema(cfg.getQueryEntities()));
}
}
else {
@@ -4039,6 +4103,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
req.deploymentId(desc.deploymentId());
req.startCacheConfiguration(ccfg);
+ req.schema(desc.schema());
}
if (nearCfg != null)
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java
index ad9eeb1..34bb321 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java
@@ -293,6 +293,8 @@ public class GridCacheSharedContext<K, V> {
mgr.start(this);
}
+ kernalCtx.query().onCacheReconnect();
+
for (GridCacheSharedManager<?, ?> mgr : mgrs)
mgr.onKernalStart(true);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
index f6827ab..654d306 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
@@ -2077,11 +2077,6 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
}
/** {@inheritDoc} */
- @Override public boolean isExchange() {
- return true;
- }
-
- /** {@inheritDoc} */
@Override public int compareTo(GridDhtPartitionsExchangeFuture fut) {
return exchId.compareTo(fut.exchId);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
index e1ed3c5..94ff0d2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
@@ -2145,10 +2145,11 @@ public abstract class GridCacheQueryManager<K, V> extends GridCacheManagerAdapte
fields.put(type.name(), fieldsMap);
- Collection<GridCacheSqlIndexMetadata> indexesCol =
- new ArrayList<>(type.indexes().size());
+ Map<String, GridQueryIndexDescriptor> idxs = type.indexes();
- for (Map.Entry<String, GridQueryIndexDescriptor> e : type.indexes().entrySet()) {
+ Collection<GridCacheSqlIndexMetadata> indexesCol = new ArrayList<>(idxs.size());
+
+ for (Map.Entry<String, GridQueryIndexDescriptor> e : idxs.entrySet()) {
GridQueryIndexDescriptor desc = e.getValue();
// Add only SQL indexes.
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
index 93b8d47..d05c9fd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
@@ -33,7 +33,7 @@ public final class IgniteQueryErrorCode {
/** General parsing error - for the cases when there's no more specific code available. */
public final static int PARSING = 1001;
- /** Code encountered unexpected type of SQL operation - like {@code EXPLAIN MERGE}. */
+ /** Requested operation is not supported. */
public final static int UNSUPPORTED_OPERATION = 1002;
/* 2xxx - analysis errors */
@@ -58,9 +58,24 @@ public final class IgniteQueryErrorCode {
/** Statement type does not match that declared by JDBC driver. */
public final static int STMT_TYPE_MISMATCH = 3003;
- /** Statement type does not match that declared by JDBC driver. */
+ /** DROP TABLE failed. */
public final static int TABLE_DROP_FAILED = 3004;
+ /** Index already exists. */
+ public final static int INDEX_ALREADY_EXISTS = 3005;
+
+ /** Index does not exist. */
+ public final static int INDEX_NOT_FOUND = 3006;
+
+ /** Required table already exists. */
+ public final static int TABLE_ALREADY_EXISTS = 3007;
+
+ /** Required column not found. */
+ public final static int COLUMN_NOT_FOUND = 3008;
+
+ /** Required column already exists. */
+ public final static int COLUMN_ALREADY_EXISTS = 3009;
+
/* 4xxx - cache related runtime errors */
/** Attempt to INSERT a key that is already in cache. */
@@ -78,6 +93,14 @@ public final class IgniteQueryErrorCode {
/** {@link EntryProcessor} has thrown an exception during {@link IgniteCache#invokeAll}. */
public final static int ENTRY_PROCESSING = 4005;
+ /** Cache not found. */
+ public final static int CACHE_NOT_FOUND = 4006;
+
+ /** */
+ private IgniteQueryErrorCode() {
+ // No-op.
+ }
+
/**
* Create a {@link SQLException} for given code and message with null state.
*
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/pool/PoolProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/pool/PoolProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/pool/PoolProcessor.java
index f6c8b2e..37bbb54 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/pool/PoolProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/pool/PoolProcessor.java
@@ -137,6 +137,11 @@ public class PoolProcessor extends GridProcessorAdapter {
return ctx.getQueryExecutorService();
+ case GridIoPolicy.SCHEMA_POOL:
+ assert ctx.getSchemaExecutorService() != null : "Query pool is not configured.";
+
+ return ctx.getSchemaExecutorService();
+
default: {
if (plc < 0)
throw new IgniteCheckedException("Policy cannot be negative: " + plc);
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexDescriptor.java
index 7f64dd7..69130fb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexDescriptor.java
@@ -29,6 +29,11 @@ import java.util.Collection;
*/
public interface GridQueryIndexDescriptor {
/**
+ * @return Name.
+ */
+ public String name();
+
+ /**
* Gets all fields to be indexed.
*
* @return Fields to be indexed.
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
index d49ea57..7746ba5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
@@ -24,6 +24,7 @@ import java.util.List;
import javax.cache.Cache;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.cache.QueryIndex;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cache.query.SqlQuery;
@@ -36,6 +37,7 @@ import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.database.CacheDataRow;
import org.apache.ignite.internal.processors.cache.database.tree.BPlusTree;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitor;
import org.apache.ignite.internal.util.GridSpinBusyLock;
import org.apache.ignite.internal.util.lang.GridCloseableIterator;
import org.apache.ignite.lang.IgniteBiTuple;
@@ -128,13 +130,38 @@ public interface GridQueryIndexing {
*
* @param spaceName Space name.
* @param qry Text query.
- * @param type Query return type.
+ * @param typeName Type name.
* @param filter Space name and key filter.
* @return Queried rows.
* @throws IgniteCheckedException If failed.
*/
public <K, V> GridCloseableIterator<IgniteBiTuple<K, V>> queryLocalText(@Nullable String spaceName, String qry,
- GridQueryTypeDescriptor type, IndexingQueryFilter filter) throws IgniteCheckedException;
+ String typeName, IndexingQueryFilter filter) throws IgniteCheckedException;
+
+ /**
+ * Create new index locally.
+ *
+ * @param spaceName Space name.
+ * @param tblName Table name.
+ * @param idxDesc Index descriptor.
+ * @param ifNotExists Ignore operation if index exists (instead of throwing an error).
+ * @param cacheVisitor Cache visitor
+ * @throws IgniteCheckedException if failed.
+ */
+ public void dynamicIndexCreate(@Nullable String spaceName, String tblName, QueryIndexDescriptorImpl idxDesc,
+ boolean ifNotExists, SchemaIndexCacheVisitor cacheVisitor) throws IgniteCheckedException;
+
+ /**
+ * Remove index from the space.
+ *
+ * @param spaceName Space name.
+ * @param idxName Index name.
+ * @param ifExists Ignore operation if index does not exist (instead of throwing an error).
+ * @throws IgniteCheckedException If failed.
+ */
+ @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
+ public void dynamicIndexDrop(@Nullable String spaceName, String idxName, boolean ifExists)
+ throws IgniteCheckedException;
/**
* Registers cache.
@@ -169,17 +196,17 @@ public interface GridQueryIndexing {
* Unregisters type and removes all corresponding data.
*
* @param spaceName Space name.
- * @param type Type descriptor.
+ * @param typeName Type name.
* @throws IgniteCheckedException If failed.
*/
- public void unregisterType(@Nullable String spaceName, GridQueryTypeDescriptor type) throws IgniteCheckedException;
+ public void unregisterType(@Nullable String spaceName, String typeName) throws IgniteCheckedException;
/**
* Updates index. Note that key is unique for space, so if space contains multiple indexes
* the key should be removed from indexes other than one being updated.
*
* @param spaceName Space name.
- * @param type Value type.
+ * @param typeName Type name.
* @param key Key.
* @param val Value.
* @param ver Version.
@@ -187,7 +214,7 @@ public interface GridQueryIndexing {
* @throws IgniteCheckedException If failed.
*/
public void store(@Nullable String spaceName,
- GridQueryTypeDescriptor type,
+ String typeName,
KeyCacheObject key,
int partId,
CacheObject val,
@@ -266,11 +293,11 @@ public interface GridQueryIndexing {
/**
* Prepare native statement to retrieve JDBC metadata from.
*
- * @param schema Schema.
+ * @param space Schema.
* @param sql Query.
* @return {@link PreparedStatement} from underlying engine to supply metadata to Prepared - most likely H2.
*/
- public PreparedStatement prepareNativeStatement(String schema, String sql) throws SQLException;
+ public PreparedStatement prepareNativeStatement(String space, String sql) throws SQLException;
/**
* Gets space name from database schema.
[6/8] ignite git commit: IGNITE-4565: Implemented CREATE INDEX and
DROP INDEX. This closes #1773. This closes #1804.
Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index 2b957be..ceb139a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -17,34 +17,29 @@
package org.apache.ignite.internal.processors.query;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentMap;
-import javax.cache.Cache;
-import javax.cache.CacheException;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteDataStreamer;
import org.apache.ignite.IgniteException;
import org.apache.ignite.binary.Binarylizable;
+import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cache.query.SqlQuery;
+import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.events.CacheQueryExecutedEvent;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.managers.communication.GridMessageListener;
import org.apache.ignite.internal.NodeStoppingException;
import org.apache.ignite.internal.processors.GridProcessorAdapter;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.CacheObject;
import org.apache.ignite.internal.processors.cache.CacheObjectContext;
+import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
+import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
@@ -54,7 +49,23 @@ import org.apache.ignite.internal.processors.cache.query.CacheQueryType;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryType;
import org.apache.ignite.internal.processors.cache.query.QueryCursorEx;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitor;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorImpl;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexOperationCancellationToken;
+import org.apache.ignite.internal.processors.query.schema.SchemaKey;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationClientFuture;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationManager;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationWorker;
+import org.apache.ignite.internal.processors.query.schema.message.SchemaAbstractDiscoveryMessage;
+import org.apache.ignite.internal.processors.query.schema.message.SchemaFinishDiscoveryMessage;
+import org.apache.ignite.internal.processors.query.schema.message.SchemaOperationStatusMessage;
+import org.apache.ignite.internal.processors.query.schema.message.SchemaProposeDiscoveryMessage;
+import org.apache.ignite.internal.processors.query.schema.operation.SchemaAbstractOperation;
+import org.apache.ignite.internal.processors.query.schema.operation.SchemaIndexCreateOperation;
+import org.apache.ignite.internal.processors.query.schema.operation.SchemaIndexDropOperation;
import org.apache.ignite.internal.processors.timeout.GridTimeoutProcessor;
+import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashSet;
import org.apache.ignite.internal.util.GridSpinBusyLock;
import org.apache.ignite.internal.util.future.GridCompoundFuture;
import org.apache.ignite.internal.util.future.GridFinishedFuture;
@@ -63,17 +74,45 @@ import org.apache.ignite.internal.util.lang.GridClosureException;
import org.apache.ignite.internal.util.lang.IgniteOutClosureX;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.internal.util.typedef.T3;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.util.worker.GridWorker;
import org.apache.ignite.internal.util.worker.GridWorkerFuture;
import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.lang.IgniteFuture;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.marshaller.jdk.JdkMarshaller;
+import org.apache.ignite.spi.discovery.DiscoveryDataBag;
import org.apache.ignite.spi.indexing.IndexingQueryFilter;
+import org.apache.ignite.thread.IgniteThread;
import org.jetbrains.annotations.Nullable;
-import org.jsr166.ConcurrentHashMap8;
+
+import javax.cache.Cache;
+import javax.cache.CacheException;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicBoolean;
import static org.apache.ignite.events.EventType.EVT_CACHE_QUERY_EXECUTED;
+import static org.apache.ignite.internal.GridTopic.TOPIC_SCHEMA;
import static org.apache.ignite.internal.IgniteComponentType.INDEXING;
+import static org.apache.ignite.internal.managers.communication.GridIoPolicy.SCHEMA_POOL;
/**
* Indexing processor.
@@ -82,26 +121,70 @@ public class GridQueryProcessor extends GridProcessorAdapter {
/** Queries detail metrics eviction frequency. */
private static final int QRY_DETAIL_METRICS_EVICTION_FREQ = 3_000;
+ /** */
+ private static final ThreadLocal<AffinityTopologyVersion> requestTopVer = new ThreadLocal<>();
+
/** For tests. */
public static Class<? extends GridQueryIndexing> idxCls;
+ /** JDK marshaller to serialize errors. */
+ private final JdkMarshaller marsh = new JdkMarshaller();
+
/** */
private final GridSpinBusyLock busyLock = new GridSpinBusyLock();
+ /** */
+ private GridTimeoutProcessor.CancelableTask qryDetailMetricsEvictTask;
+
/** Type descriptors. */
- private final Map<QueryTypeIdKey, QueryTypeDescriptorImpl> types = new ConcurrentHashMap8<>();
+ private final Map<QueryTypeIdKey, QueryTypeDescriptorImpl> types = new ConcurrentHashMap<>();
/** Type descriptors. */
- private final ConcurrentMap<QueryTypeNameKey, QueryTypeDescriptorImpl> typesByName = new ConcurrentHashMap8<>();
+ private final ConcurrentMap<QueryTypeNameKey, QueryTypeDescriptorImpl> typesByName = new ConcurrentHashMap<>();
/** */
private final GridQueryIndexing idx;
- /** */
- private GridTimeoutProcessor.CancelableTask qryDetailMetricsEvictTask;
+ /** All indexes. */
+ private final ConcurrentMap<QueryIndexKey, QueryIndexDescriptorImpl> idxs = new ConcurrentHashMap<>();
- /** */
- private static final ThreadLocal<AffinityTopologyVersion> requestTopVer = new ThreadLocal<>();
+ /** Schema operation futures created on client side. */
+ private final ConcurrentMap<UUID, SchemaOperationClientFuture> schemaCliFuts = new ConcurrentHashMap<>();
+
+ /** IO message listener. */
+ private final GridMessageListener ioLsnr;
+
+ /** Schema operations. */
+ private final ConcurrentHashMap<SchemaKey, SchemaOperation> schemaOps = new ConcurrentHashMap<>();
+
+ /** Active propose messages. */
+ private final LinkedHashMap<UUID, SchemaProposeDiscoveryMessage> activeProposals = new LinkedHashMap<>();
+
+ /** General state mutex. */
+ private final Object stateMux = new Object();
+
+ /** Coordinator node (initialized lazily). */
+ private ClusterNode crd;
+
+ /** Registered spaces. */
+ private final Collection<String> spaces = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
+
+ /** ID history for index create/drop discovery messages. */
+ private final GridBoundedConcurrentLinkedHashSet<IgniteUuid> dscoMsgIdHist =
+ new GridBoundedConcurrentLinkedHashSet<>(QueryUtils.discoveryHistorySize());
+
+ /** History of already completed operations. */
+ private final GridBoundedConcurrentLinkedHashSet<UUID> completedOpIds =
+ new GridBoundedConcurrentLinkedHashSet<>(QueryUtils.discoveryHistorySize());
+
+ /** Pending status messages. */
+ private final LinkedList<SchemaOperationStatusMessage> pendingMsgs = new LinkedList<>();
+
+ /** Disconnected flag. */
+ private boolean disconnected;
+
+ /** Whether exchange thread is ready to process further requests. */
+ private boolean exchangeReady;
/** */
private boolean skipFieldLookup;
@@ -119,6 +202,20 @@ public class GridQueryProcessor extends GridProcessorAdapter {
}
else
idx = INDEXING.inClassPath() ? U.<GridQueryIndexing>newInstance(INDEXING.className()) : null;
+
+ ioLsnr = new GridMessageListener() {
+ @Override public void onMessage(UUID nodeId, Object msg) {
+ if (msg instanceof SchemaOperationStatusMessage) {
+ SchemaOperationStatusMessage msg0 = (SchemaOperationStatusMessage)msg;
+
+ msg0.senderNodeId(nodeId);
+
+ processStatusMessage(msg0);
+ }
+ else
+ U.warn(log, "Unsupported IO message: " + msg);
+ }
+ };
}
/** {@inheritDoc} */
@@ -131,6 +228,8 @@ public class GridQueryProcessor extends GridProcessorAdapter {
idx.start(ctx, busyLock);
}
+ ctx.io().addMessageListener(TOPIC_SCHEMA, ioLsnr);
+
// Schedule queries detail metrics eviction.
qryDetailMetricsEvictTask = ctx.timeout().schedule(new Runnable() {
@Override public void run() {
@@ -140,6 +239,401 @@ public class GridQueryProcessor extends GridProcessorAdapter {
}, QRY_DETAIL_METRICS_EVICTION_FREQ, QRY_DETAIL_METRICS_EVICTION_FREQ);
}
+ /** {@inheritDoc} */
+ @Override public void onKernalStop(boolean cancel) {
+ super.onKernalStop(cancel);
+
+ if (cancel && idx != null) {
+ try {
+ while (!busyLock.tryBlock(500))
+ idx.cancelAllQueries();
+
+ return;
+ } catch (InterruptedException ignored) {
+ U.warn(log, "Interrupted while waiting for active queries cancellation.");
+
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ busyLock.block();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void stop(boolean cancel) throws IgniteCheckedException {
+ super.stop(cancel);
+
+ ctx.io().removeMessageListener(TOPIC_SCHEMA, ioLsnr);
+
+ if (idx != null)
+ idx.stop();
+
+ U.closeQuiet(qryDetailMetricsEvictTask);
+ }
+
+ /**
+ * Handle cache kernal start. At this point discovery and IO managers are operational, caches are not started yet.
+ *
+ * @throws IgniteCheckedException If failed.
+ */
+ public void onCacheKernalStart() throws IgniteCheckedException {
+ synchronized (stateMux) {
+ exchangeReady = true;
+
+ // Re-run pending top-level proposals.
+ for (SchemaOperation schemaOp : schemaOps.values())
+ onSchemaPropose(schemaOp.proposeMessage());
+ }
+ }
+
+ /**
+ * Handle cache reconnect.
+ *
+ * @throws IgniteCheckedException If failed.
+ */
+ public void onCacheReconnect() throws IgniteCheckedException {
+ synchronized (stateMux) {
+ assert disconnected;
+
+ disconnected = false;
+
+ onCacheKernalStart();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public DiscoveryDataExchangeType discoveryDataType() {
+ return DiscoveryDataExchangeType.QUERY_PROC;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void collectGridNodeData(DiscoveryDataBag dataBag) {
+ // Collect active proposals.
+ synchronized (stateMux) {
+ LinkedHashMap<UUID, SchemaProposeDiscoveryMessage> data = new LinkedHashMap<>(activeProposals);
+
+ dataBag.addGridCommonData(DiscoveryDataExchangeType.QUERY_PROC.ordinal(), data);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override public void onGridDataReceived(DiscoveryDataBag.GridDiscoveryData data) {
+ synchronized (stateMux) {
+ // Preserve proposals.
+ LinkedHashMap<UUID, SchemaProposeDiscoveryMessage> data0 =
+ (LinkedHashMap<UUID, SchemaProposeDiscoveryMessage>)data.commonData();
+
+ // Process proposals as if they were received as regular discovery messages.
+ if (data0 != null) {
+ for (SchemaProposeDiscoveryMessage activeProposal : data0.values())
+ onSchemaProposeDiscovery0(activeProposal);
+ }
+ }
+ }
+
+ /**
+ * Process schema propose message from discovery thread.
+ *
+ * @param msg Message.
+ * @return {@code True} if exchange should be triggered.
+ */
+ private boolean onSchemaProposeDiscovery(SchemaProposeDiscoveryMessage msg) {
+ UUID opId = msg.operation().id();
+ String space = msg.operation().space();
+
+ if (!msg.initialized()) {
+ // Ensure cache exists on coordinator node.
+ DynamicCacheDescriptor cacheDesc = ctx.cache().cacheDescriptor(space);
+
+ if (cacheDesc == null) {
+ if (log.isDebugEnabled())
+ log.debug("Received schema propose discovery message, but cache doesn't exist " +
+ "(will report error) [opId=" + opId + ", msg=" + msg + ']');
+
+ msg.onError(new SchemaOperationException(SchemaOperationException.CODE_CACHE_NOT_FOUND, space));
+ }
+ else {
+ CacheConfiguration ccfg = cacheDesc.cacheConfiguration();
+
+ if (ccfg.getCacheMode() == CacheMode.LOCAL) {
+ // Distributed operation is not allowed on LOCAL caches.
+ if (log.isDebugEnabled())
+ log.debug("Received schema propose discovery message, but cache is LOCAL " +
+ "(will report error) [opId=" + opId + ", msg=" + msg + ']');
+
+ msg.onError(new SchemaOperationException("Schema changes are not supported for LOCAL cache."));
+ }
+ else {
+ // Preserve deployment ID so that we can distinguish between different caches with the same name.
+ if (msg.deploymentId() == null)
+ msg.deploymentId(cacheDesc.deploymentId());
+
+ assert F.eq(cacheDesc.deploymentId(), msg.deploymentId());
+ }
+ }
+ }
+
+ // Complete client future and exit immediately in case of error.
+ if (msg.hasError()) {
+ SchemaOperationClientFuture cliFut = schemaCliFuts.remove(opId);
+
+ if (cliFut != null)
+ cliFut.onDone(msg.error());
+
+ return false;
+ }
+
+ return onSchemaProposeDiscovery0(msg);
+ }
+
+ /**
+ * Process schema propose message from discovery thread (or from cache start routine).
+ *
+ * @param msg Message.
+ * @return {@code True} if exchange should be triggered.
+ */
+ private boolean onSchemaProposeDiscovery0(SchemaProposeDiscoveryMessage msg) {
+ UUID opId = msg.operation().id();
+
+ synchronized (stateMux) {
+ if (disconnected) {
+ if (log.isDebugEnabled())
+ log.debug("Processing discovery schema propose message, but node is disconnected (will ignore) " +
+ "[opId=" + opId + ", msg=" + msg + ']');
+
+ return false;
+ }
+
+ if (log.isDebugEnabled())
+ log.debug("Processing discovery schema propose message [opId=" + opId + ", msg=" + msg + ']');
+
+ // Put message to active operations set.
+ SchemaProposeDiscoveryMessage oldDesc = activeProposals.put(msg.operation().id(), msg);
+
+ assert oldDesc == null;
+
+ // Create schema operation and either trigger it immediately from exchange thread or append to already
+ // running operation.
+ SchemaOperation schemaOp = new SchemaOperation(msg);
+
+ SchemaKey key = msg.schemaKey();
+
+ SchemaOperation prevSchemaOp = schemaOps.get(key);
+
+ if (prevSchemaOp != null) {
+ prevSchemaOp = prevSchemaOp.unwind();
+
+ if (log.isDebugEnabled())
+ log.debug("Schema change is enqueued and will be executed after previous operation is completed " +
+ "[opId=" + opId + ", prevOpId=" + prevSchemaOp.id() + ']');
+
+ prevSchemaOp.next(schemaOp);
+
+ return false;
+ }
+ else {
+ schemaOps.put(key, schemaOp);
+
+ return exchangeReady;
+ }
+ }
+ }
+
+ /**
+ * Handle schema propose from exchange thread.
+ *
+ * @param msg Discovery message.
+ */
+ @SuppressWarnings("ThrowableInstanceNeverThrown")
+ public void onSchemaPropose(SchemaProposeDiscoveryMessage msg) {
+ UUID opId = msg.operation().id();
+
+ if (log.isDebugEnabled())
+ log.debug("Processing schema propose message (exchange) [opId=" + opId + ']');
+
+ synchronized (stateMux) {
+ if (disconnected)
+ return;
+
+ SchemaOperation curOp = schemaOps.get(msg.schemaKey());
+
+ assert curOp != null;
+ assert F.eq(opId, curOp.id());
+ assert !curOp.started();
+
+ startSchemaChange(curOp);
+ }
+ }
+
+ /**
+ * Process schema finish message from discovery thread.
+ *
+ * @param msg Message.
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ private void onSchemaFinishDiscovery(SchemaFinishDiscoveryMessage msg) {
+ UUID opId = msg.operation().id();
+
+ if (log.isDebugEnabled())
+ log.debug("Received schema finish message (discovery) [opId=" + opId + ", msg=" + msg + ']');
+
+ synchronized (stateMux) {
+ if (disconnected)
+ return;
+
+ boolean completedOpAdded = completedOpIds.add(opId);
+
+ assert completedOpAdded;
+
+ // Remove propose message so that it will not be shared with joining nodes.
+ SchemaProposeDiscoveryMessage proposeMsg = activeProposals.remove(opId);
+
+ assert proposeMsg != null;
+
+ // Apply changes to public cache schema if operation is successful and original cache is still there.
+ if (!msg.hasError()) {
+ DynamicCacheDescriptor cacheDesc = ctx.cache().cacheDescriptor(msg.operation().space());
+
+ if (cacheDesc != null && F.eq(cacheDesc.deploymentId(), proposeMsg.deploymentId()))
+ cacheDesc.schemaChangeFinish(msg);
+ }
+
+ // Propose message will be used from exchange thread to
+ msg.proposeMessage(proposeMsg);
+
+ if (exchangeReady) {
+ SchemaOperation op = schemaOps.get(proposeMsg.schemaKey());
+
+ if (F.eq(op.id(), opId)) {
+ // Completed top operation.
+ op.finishMessage(msg);
+
+ if (op.started())
+ op.doFinish();
+ }
+ else {
+ // Completed operation in the middle, will schedule completion later.
+ while (op != null) {
+ if (F.eq(op.id(), opId))
+ break;
+
+ op = op.next();
+ }
+
+ assert op != null;
+ assert !op.started();
+
+ op.finishMessage(msg);
+ }
+ }
+ else {
+ // Set next operation as top-level one.
+ SchemaKey schemaKey = proposeMsg.schemaKey();
+
+ SchemaOperation op = schemaOps.remove(schemaKey);
+
+ assert op != null;
+ assert F.eq(op.id(), opId);
+
+ // Chain to the next operation (if any).
+ SchemaOperation nextOp = op.next();
+
+ if (nextOp != null)
+ schemaOps.put(schemaKey, nextOp);
+ }
+
+ // Clean stale IO messages from just-joined nodes.
+ cleanStaleStatusMessages(opId);
+ }
+
+ // Complete client future (if any).
+ SchemaOperationClientFuture cliFut = schemaCliFuts.remove(opId);
+
+ if (cliFut != null) {
+ if (msg.hasError())
+ cliFut.onDone(msg.error());
+ else
+ cliFut.onDone();
+ }
+ }
+
+ /**
+ * Initiate actual schema change operation.
+ *
+ * @param schemaOp Schema operation.
+ */
+ @SuppressWarnings({"unchecked", "ThrowableInstanceNeverThrown"})
+ private void startSchemaChange(SchemaOperation schemaOp) {
+ assert Thread.holdsLock(stateMux);
+ assert !schemaOp.started();
+
+ // Get current cache state.
+ SchemaProposeDiscoveryMessage msg = schemaOp.proposeMessage();
+
+ String space = msg.operation().space();
+
+ DynamicCacheDescriptor cacheDesc = ctx.cache().cacheDescriptor(space);
+
+ boolean cacheExists = cacheDesc != null && F.eq(msg.deploymentId(), cacheDesc.deploymentId());
+
+ boolean cacheRegistered = cacheExists && spaces.contains(CU.mask(space));
+
+ // Validate schema state and decide whether we should proceed or not.
+ SchemaAbstractOperation op = msg.operation();
+
+ QueryTypeDescriptorImpl type = null;
+ SchemaOperationException err;
+
+ boolean nop = false;
+
+ if (cacheExists) {
+ if (cacheRegistered) {
+ // If cache is started, we perform validation against real schema.
+ T3<QueryTypeDescriptorImpl, Boolean, SchemaOperationException> res = prepareChangeOnStartedCache(op);
+
+ assert res.get2() != null;
+
+ type = res.get1();
+ nop = res.get2();
+ err = res.get3();
+ }
+ else {
+ // If cache is not started yet, there is no schema. Take schema from cache descriptor and validate.
+ QuerySchema schema = cacheDesc.schema();
+
+ T2<Boolean, SchemaOperationException> res = prepareChangeOnNotStartedCache(op, schema);
+
+ assert res.get1() != null;
+
+ type = null;
+ nop = res.get1();
+ err = res.get2();
+ }
+ }
+ else
+ err = new SchemaOperationException(SchemaOperationException.CODE_CACHE_NOT_FOUND, op.space());
+
+ // Start operation.
+ SchemaOperationWorker worker =
+ new SchemaOperationWorker(ctx, this, msg.deploymentId(), op, nop, err, cacheRegistered, type);
+
+ SchemaOperationManager mgr = new SchemaOperationManager(ctx, this, worker,
+ ctx.clientNode() ? null : coordinator());
+
+ schemaOp.manager(mgr);
+
+ mgr.start();
+
+ // Unwind pending IO messages.
+ if (!ctx.clientNode() && coordinator().isLocal())
+ unwindPendingMessages(schemaOp.id(), mgr);
+
+ // Schedule operation finish handling if needed.
+ if (schemaOp.hasFinishMessage())
+ schemaOp.doFinish();
+ }
+
/**
* @return {@code true} If indexing module is in classpath and successfully initialized.
*/
@@ -148,55 +642,115 @@ public class GridQueryProcessor extends GridProcessorAdapter {
}
/**
+ * @return Indexing.
+ * @throws IgniteException If module is not enabled.
+ */
+ public GridQueryIndexing getIndexing() throws IgniteException {
+ checkxEnabled();
+
+ return idx;
+ }
+
+ /**
* @param cctx Cache context.
+ * @param schema Initial schema.
* @throws IgniteCheckedException If failed.
*/
- @SuppressWarnings("deprecation")
- private void initializeCache(GridCacheContext<?, ?> cctx) throws IgniteCheckedException {
+ @SuppressWarnings({"deprecation", "ThrowableResultOfMethodCallIgnored"})
+ private void initializeCache(GridCacheContext<?, ?> cctx, QuerySchema schema) throws IgniteCheckedException {
String space = cctx.name();
- CacheConfiguration<?,?> ccfg = cctx.config();
-
// Prepare candidates.
List<Class<?>> mustDeserializeClss = new ArrayList<>();
Collection<QueryTypeCandidate> cands = new ArrayList<>();
- if (!F.isEmpty(ccfg.getQueryEntities())) {
- for (QueryEntity qryEntity : ccfg.getQueryEntities()) {
+ Collection<QueryEntity> qryEntities = schema.entities();
+
+ if (!F.isEmpty(qryEntities)) {
+ for (QueryEntity qryEntity : qryEntities) {
QueryTypeCandidate cand = QueryUtils.typeForQueryEntity(space, cctx, qryEntity, mustDeserializeClss);
cands.add(cand);
}
}
- // Register candidates.
- idx.registerCache(space, cctx, cctx.config());
+ // Ensure that candidates has unique index names. Otherwise we will not be able to apply pending operations.
+ Map<String, QueryTypeDescriptorImpl> tblTypMap = new HashMap<>();
+ Map<String, QueryTypeDescriptorImpl> idxTypMap = new HashMap<>();
- try {
- for (QueryTypeCandidate cand : cands) {
- QueryTypeIdKey typeId = cand.typeId();
- QueryTypeIdKey altTypeId = cand.alternativeTypeId();
- QueryTypeDescriptorImpl desc = cand.descriptor();
+ for (QueryTypeCandidate cand : cands) {
+ QueryTypeDescriptorImpl desc = cand.descriptor();
- if (typesByName.putIfAbsent(new QueryTypeNameKey(space, desc.name()), desc) != null)
- throw new IgniteCheckedException("Type with name '" + desc.name() + "' already indexed " +
- "in cache '" + space + "'.");
+ QueryTypeDescriptorImpl oldDesc = tblTypMap.put(desc.tableName(), desc);
- types.put(typeId, desc);
+ if (oldDesc != null)
+ throw new IgniteException("Duplicate table name [tblName=" + desc.tableName() +
+ ", type1=" + desc.name() + ", type2=" + oldDesc.name() + ']');
- if (altTypeId != null)
- types.put(altTypeId, desc);
+ for (String idxName : desc.indexes().keySet()) {
+ oldDesc = idxTypMap.put(idxName, desc);
- idx.registerType(space, desc);
+ if (oldDesc != null)
+ throw new IgniteException("Duplicate index name [idxName=" + idxName +
+ ", type1=" + desc.name() + ", type2=" + oldDesc.name() + ']');
}
}
- catch (IgniteCheckedException | RuntimeException e) {
- unregisterCache0(space);
- throw e;
+ // Apply pending operation which could have been completed as no-op at this point. There could be only one
+ // in-flight operation for a cache.
+ synchronized (stateMux) {
+ if (disconnected)
+ return;
+
+ for (SchemaOperation op : schemaOps.values()) {
+ if (F.eq(op.proposeMessage().deploymentId(), cctx.dynamicDeploymentId())) {
+ if (op.started()) {
+ SchemaOperationWorker worker = op.manager().worker();
+
+ assert !worker.cacheRegistered();
+
+ if (!worker.nop()) {
+ IgniteInternalFuture fut = worker.future();
+
+ assert fut.isDone();
+
+ if (fut.error() == null) {
+ SchemaAbstractOperation op0 = op.proposeMessage().operation();
+
+ if (op0 instanceof SchemaIndexCreateOperation) {
+ SchemaIndexCreateOperation opCreate = (SchemaIndexCreateOperation)op0;
+
+ QueryTypeDescriptorImpl typeDesc = tblTypMap.get(opCreate.tableName());
+
+ assert typeDesc != null;
+
+ QueryUtils.processDynamicIndexChange(opCreate.indexName(), opCreate.index(),
+ typeDesc);
+ }
+ else if (op0 instanceof SchemaIndexDropOperation) {
+ SchemaIndexDropOperation opDrop = (SchemaIndexDropOperation)op0;
+
+ QueryTypeDescriptorImpl typeDesc = idxTypMap.get(opDrop.indexName());
+
+ assert typeDesc != null;
+
+ QueryUtils.processDynamicIndexChange(opDrop.indexName(), null, typeDesc);
+ }
+ else
+ assert false;
+ }
+ }
+ }
+
+ break;
+ }
+ }
}
+ // Ready to register at this point.
+ registerCache0(space, cctx, cands);
+
// Warn about possible implicit deserialization.
if (!mustDeserializeClss.isEmpty()) {
U.warn(log, "Some classes in query configuration cannot be written in binary format " +
@@ -209,46 +763,41 @@ public class GridQueryProcessor extends GridProcessorAdapter {
}
/** {@inheritDoc} */
- @Override public void onKernalStop(boolean cancel) {
- super.onKernalStop(cancel);
-
- if (cancel && idx != null)
- try {
- while (!busyLock.tryBlock(500))
- idx.cancelAllQueries();
-
- return;
- }
- catch (InterruptedException ignored) {
- U.warn(log, "Interrupted while waiting for active queries cancellation.");
+ @Override public void onDisconnected(IgniteFuture<?> reconnectFut) throws IgniteCheckedException {
+ Collection<SchemaOperationClientFuture> futs;
- Thread.currentThread().interrupt();
- }
+ synchronized (stateMux) {
+ disconnected = true;
+ exchangeReady = false;
- busyLock.block();
- }
+ // Clear client futures.
+ futs = new ArrayList<>(schemaCliFuts.values());
- /** {@inheritDoc} */
- @Override public void stop(boolean cancel) throws IgniteCheckedException {
- super.stop(cancel);
+ schemaCliFuts.clear();
- if (idx != null)
- idx.stop();
+ // Clear operations data.
+ activeProposals.clear();
+ schemaOps.clear();
+ }
- U.closeQuiet(qryDetailMetricsEvictTask);
- }
+ // Complete client futures outside of synchonized block because they may have listeners/chains.
+ for (SchemaOperationClientFuture fut : futs)
+ fut.onDone(new SchemaOperationException("Client node is disconnected (operation result is unknown)."));
- /** {@inheritDoc} */
- @Override public void onDisconnected(IgniteFuture<?> reconnectFut) throws IgniteCheckedException {
if (idx != null)
idx.onDisconnected(reconnectFut);
}
/**
+ * Handle cache start. Invoked either from GridCacheProcessor.onKernalStart() method or from exchange worker.
+ * When called for the first time, we initialize topology thus understanding whether current node is coordinator
+ * or not.
+ *
* @param cctx Cache context.
+ * @param schema Index states.
* @throws IgniteCheckedException If failed.
*/
- public void onCacheStart(GridCacheContext cctx) throws IgniteCheckedException {
+ public void onCacheStart(GridCacheContext cctx, QuerySchema schema) throws IgniteCheckedException {
if (idx == null)
return;
@@ -258,45 +807,506 @@ public class GridQueryProcessor extends GridProcessorAdapter {
cctx.shared().database().checkpointReadLock();
try {
- initializeCache(cctx);
+ initializeCache(cctx, schema);
}
finally {
cctx.shared().database().checkpointReadUnlock();
- busyLock.leaveBusy();
+ busyLock.leaveBusy();
+ }
+ }
+
+ /**
+ * @param cctx Cache context.
+ */
+ public void onCacheStop(GridCacheContext cctx) {
+ if (idx == null)
+ return;
+
+ if (!busyLock.enterBusy())
+ return;
+
+ try {
+ unregisterCache0(cctx.name());
+ }
+ finally {
+ busyLock.leaveBusy();
+ }
+ }
+
+ /**
+ * @return Skip field lookup flag.
+ */
+ public boolean skipFieldLookup() {
+ return skipFieldLookup;
+ }
+
+ /**
+ * @param skipFieldLookup Skip field lookup flag.
+ */
+ public void skipFieldLookup(boolean skipFieldLookup) {
+ this.skipFieldLookup = skipFieldLookup;
+ }
+
+ /**
+ * Handle custom discovery message.
+ *
+ * @param msg Message.
+ */
+ public void onDiscovery(SchemaAbstractDiscoveryMessage msg) {
+ IgniteUuid id = msg.id();
+
+ if (!dscoMsgIdHist.add(id)) {
+ U.warn(log, "Received duplicate schema custom discovery message (will ignore) [opId=" +
+ msg.operation().id() + ", msg=" + msg +']');
+
+ return;
+ }
+
+ if (msg instanceof SchemaProposeDiscoveryMessage) {
+ SchemaProposeDiscoveryMessage msg0 = (SchemaProposeDiscoveryMessage)msg;
+
+ boolean exchange = onSchemaProposeDiscovery(msg0);
+
+ msg0.exchange(exchange);
+ }
+ else if (msg instanceof SchemaFinishDiscoveryMessage) {
+ SchemaFinishDiscoveryMessage msg0 = (SchemaFinishDiscoveryMessage)msg;
+
+ onSchemaFinishDiscovery(msg0);
+ }
+ else
+ U.warn(log, "Received unsupported schema custom discovery message (will ignore) [opId=" +
+ msg.operation().id() + ", msg=" + msg +']');
+ }
+
+ /**
+ * Prepare change on started cache.
+ *
+ * @param op Operation.
+ * @return Result: affected type, nop flag, error.
+ */
+ private T3<QueryTypeDescriptorImpl, Boolean, SchemaOperationException> prepareChangeOnStartedCache(
+ SchemaAbstractOperation op) {
+ QueryTypeDescriptorImpl type = null;
+ boolean nop = false;
+ SchemaOperationException err = null;
+
+ String space = op.space();
+
+ if (op instanceof SchemaIndexCreateOperation) {
+ SchemaIndexCreateOperation op0 = (SchemaIndexCreateOperation) op;
+
+ QueryIndex idx = op0.index();
+
+ // Make sure table exists.
+ String tblName = op0.tableName();
+
+ type = type(space, tblName);
+
+ if (type == null)
+ err = new SchemaOperationException(SchemaOperationException.CODE_TABLE_NOT_FOUND, tblName);
+ else {
+ // Make sure that index can be applied to the given table.
+ for (String idxField : idx.getFieldNames()) {
+ if (!type.fields().containsKey(idxField)) {
+ err = new SchemaOperationException(SchemaOperationException.CODE_COLUMN_NOT_FOUND,
+ idxField);
+
+ break;
+ }
+ }
+ }
+
+ // Check conflict with other indexes.
+ if (err == null) {
+ String idxName = op0.index().getName();
+
+ QueryIndexKey idxKey = new QueryIndexKey(space, idxName);
+
+ if (idxs.get(idxKey) != null) {
+ if (op0.ifNotExists())
+ nop = true;
+ else
+ err = new SchemaOperationException(SchemaOperationException.CODE_INDEX_EXISTS, idxName);
+ }
+ }
+ }
+ else if (op instanceof SchemaIndexDropOperation) {
+ SchemaIndexDropOperation op0 = (SchemaIndexDropOperation) op;
+
+ String idxName = op0.indexName();
+
+ QueryIndexDescriptorImpl oldIdx = idxs.get(new QueryIndexKey(space, idxName));
+
+ if (oldIdx == null) {
+ if (op0.ifExists())
+ nop = true;
+ else
+ err = new SchemaOperationException(SchemaOperationException.CODE_INDEX_NOT_FOUND, idxName);
+ }
+ else
+ type = oldIdx.typeDescriptor();
+ }
+ else
+ err = new SchemaOperationException("Unsupported operation: " + op);
+
+ return new T3<>(type, nop, err);
+ }
+
+ /**
+ * Prepare operation on non-started cache.
+ *
+ * @param op Operation.
+ * @param schema Known cache schema.
+ * @return Result: nop flag, error.
+ */
+ private T2<Boolean, SchemaOperationException> prepareChangeOnNotStartedCache(SchemaAbstractOperation op,
+ QuerySchema schema) {
+ boolean nop = false;
+ SchemaOperationException err = null;
+
+ // Build table and index maps.
+ Map<String, QueryEntity> tblMap = new HashMap<>();
+ Map<String, T2<QueryEntity, QueryIndex>> idxMap = new HashMap<>();
+
+ for (QueryEntity entity : schema.entities()) {
+ String tblName = QueryUtils.tableName(entity);
+
+ QueryEntity oldEntity = tblMap.put(tblName, entity);
+
+ if (oldEntity != null) {
+ err = new SchemaOperationException("Invalid schema state (duplicate table found): " + tblName);
+
+ break;
+ }
+
+ for (QueryIndex entityIdx : entity.getIndexes()) {
+ String idxName = QueryUtils.indexName(entity, entityIdx);
+
+ T2<QueryEntity, QueryIndex> oldIdxEntity = idxMap.put(idxName, new T2<>(entity, entityIdx));
+
+ if (oldIdxEntity != null) {
+ err = new SchemaOperationException("Invalid schema state (duplicate index found): " +
+ idxName);
+
+ break;
+ }
+ }
+
+ if (err != null)
+ break;
+ }
+
+ // Now check whether operation can be applied to schema.
+ if (op instanceof SchemaIndexCreateOperation) {
+ SchemaIndexCreateOperation op0 = (SchemaIndexCreateOperation)op;
+
+ String idxName = op0.indexName();
+
+ T2<QueryEntity, QueryIndex> oldIdxEntity = idxMap.get(idxName);
+
+ if (oldIdxEntity == null) {
+ String tblName = op0.tableName();
+
+ QueryEntity oldEntity = tblMap.get(tblName);
+
+ if (oldEntity == null)
+ err = new SchemaOperationException(SchemaOperationException.CODE_TABLE_NOT_FOUND, tblName);
+ else {
+ for (String fieldName : op0.index().getFields().keySet()) {
+ Set<String> oldEntityFields = new HashSet<>(oldEntity.getFields().keySet());
+
+ for (Map.Entry<String, String> alias : oldEntity.getAliases().entrySet()) {
+ oldEntityFields.remove(alias.getKey());
+ oldEntityFields.add(alias.getValue());
+ }
+
+ if (!oldEntityFields.contains(fieldName)) {
+ err = new SchemaOperationException(SchemaOperationException.CODE_COLUMN_NOT_FOUND,
+ fieldName);
+
+ break;
+ }
+ }
+ }
+ }
+ else {
+ if (op0.ifNotExists())
+ nop = true;
+ else
+ err = new SchemaOperationException(SchemaOperationException.CODE_INDEX_EXISTS, idxName);
+ }
+ }
+ else if (op instanceof SchemaIndexDropOperation) {
+ SchemaIndexDropOperation op0 = (SchemaIndexDropOperation)op;
+
+ String idxName = op0.indexName();
+
+ T2<QueryEntity, QueryIndex> oldIdxEntity = idxMap.get(idxName);
+
+ if (oldIdxEntity == null) {
+ if (op0.ifExists())
+ nop = true;
+ else
+ err = new SchemaOperationException(SchemaOperationException.CODE_INDEX_NOT_FOUND, idxName);
+ }
+ }
+ else
+ err = new SchemaOperationException("Unsupported operation: " + op);
+
+ return new T2<>(nop, err);
+ }
+
+ /**
+ * Invoked when coordinator finished ensuring that all participants are ready.
+ *
+ * @param op Operation.
+ * @param err Error (if any).
+ */
+ public void onCoordinatorFinished(SchemaAbstractOperation op, @Nullable SchemaOperationException err) {
+ synchronized (stateMux) {
+ SchemaFinishDiscoveryMessage msg = new SchemaFinishDiscoveryMessage(op, err);
+
+ try {
+ ctx.discovery().sendCustomEvent(msg);
+ }
+ catch (Exception e) {
+ // Failed to send finish message over discovery. This is something unrecoverable.
+ U.warn(log, "Failed to send schema finish discovery message [opId=" + op.id() + ']', e);
+ }
+ }
+ }
+
+ /**
+ * Get current coordinator node.
+ *
+ * @return Coordinator node.
+ */
+ private ClusterNode coordinator() {
+ assert !ctx.clientNode();
+
+ synchronized (stateMux) {
+ if (crd == null) {
+ ClusterNode crd0 = null;
+
+ for (ClusterNode node : ctx.discovery().aliveServerNodes()) {
+ if (crd0 == null || crd0.order() > node.order())
+ crd0 = node;
+ }
+
+ assert crd0 != null;
+
+ crd = crd0;
+ }
+
+ return crd;
+ }
+ }
+
+ /**
+ * Get rid of stale IO message received from other nodes which joined when operation had been in progress.
+ *
+ * @param opId Operation ID.
+ */
+ private void cleanStaleStatusMessages(UUID opId) {
+ Iterator<SchemaOperationStatusMessage> it = pendingMsgs.iterator();
+
+ while (it.hasNext()) {
+ SchemaOperationStatusMessage statusMsg = it.next();
+
+ if (F.eq(opId, statusMsg.operationId())) {
+ it.remove();
+
+ if (log.isDebugEnabled())
+ log.debug("Dropped operation status message because it is already completed [opId=" + opId +
+ ", rmtNode=" + statusMsg.senderNodeId() + ']');
+ }
+ }
+ }
+
+ /**
+ * Apply positive index operation result.
+ *
+ * @param op Operation.
+ * @param type Type descriptor (if available),
+ */
+ public void onLocalOperationFinished(SchemaAbstractOperation op, @Nullable QueryTypeDescriptorImpl type) {
+ synchronized (stateMux) {
+ if (disconnected)
+ return;
+
+ // No need to apply anything to obsolete type.
+ if (type == null || type.obsolete()) {
+ if (log.isDebugEnabled())
+ log.debug("Local operation finished, but type descriptor is either missing or obsolete " +
+ "(will ignore) [opId=" + op.id() + ']');
+
+ return;
+ }
+
+ if (log.isDebugEnabled())
+ log.debug("Local operation finished successfully [opId=" + op.id() + ']');
+
+ try {
+ if (op instanceof SchemaIndexCreateOperation) {
+ SchemaIndexCreateOperation op0 = (SchemaIndexCreateOperation)op;
+
+ QueryUtils.processDynamicIndexChange(op0.indexName(), op0.index(), type);
+
+ QueryIndexDescriptorImpl idxDesc = type.index(op0.indexName());
+
+ QueryIndexKey idxKey = new QueryIndexKey(op.space(), op0.indexName());
+
+ idxs.put(idxKey, idxDesc);
+ }
+ else {
+ assert op instanceof SchemaIndexDropOperation;
+
+ SchemaIndexDropOperation op0 = (SchemaIndexDropOperation) op;
+
+ QueryUtils.processDynamicIndexChange(op0.indexName(), null, type);
+
+ QueryIndexKey idxKey = new QueryIndexKey(op.space(), op0.indexName());
+
+ idxs.remove(idxKey);
+ }
+ }
+ catch (IgniteCheckedException e) {
+ U.warn(log, "Failed to finish index operation [opId=" + op.id() + " op=" + op + ']', e);
+ }
+ }
+ }
+
+ /**
+ * Handle node leave.
+ *
+ * @param node Node.
+ */
+ public void onNodeLeave(ClusterNode node) {
+ synchronized (stateMux) {
+ // Clients do not send status messages and are never coordinators.
+ if (ctx.clientNode())
+ return;
+
+ ClusterNode crd0 = coordinator();
+
+ if (F.eq(node.id(), crd0.id())) {
+ crd = null;
+
+ crd0 = coordinator();
+ }
+
+ for (SchemaOperation op : schemaOps.values()) {
+ if (op.started()) {
+ op.manager().onNodeLeave(node.id(), crd0);
+
+ if (crd0.isLocal())
+ unwindPendingMessages(op.id(), op.manager());
+ }
+ }
+ }
+ }
+
+ /**
+ * Process index operation.
+ *
+ * @param op Operation.
+ * @param type Type descriptor.
+ * @param depId Cache deployment ID.
+ * @param cancelTok Cancel token.
+ * @throws SchemaOperationException If failed.
+ */
+ public void processIndexOperationLocal(SchemaAbstractOperation op, QueryTypeDescriptorImpl type, IgniteUuid depId,
+ SchemaIndexOperationCancellationToken cancelTok) throws SchemaOperationException {
+ if (log.isDebugEnabled())
+ log.debug("Started local index operation [opId=" + op.id() + ']');
+
+ String space = op.space();
+
+ GridCacheAdapter cache = ctx.cache().internalCache(op.space());
+
+ if (cache == null || !F.eq(depId, cache.context().dynamicDeploymentId()))
+ throw new SchemaOperationException(SchemaOperationException.CODE_CACHE_NOT_FOUND, op.space());
+
+ try {
+ if (op instanceof SchemaIndexCreateOperation) {
+ SchemaIndexCreateOperation op0 = (SchemaIndexCreateOperation) op;
+
+ QueryIndexDescriptorImpl idxDesc = QueryUtils.createIndexDescriptor(type, op0.index());
+
+ SchemaIndexCacheVisitor visitor =
+ new SchemaIndexCacheVisitorImpl(this, cache.context(), space, op0.tableName(), cancelTok);
+
+ idx.dynamicIndexCreate(space, op0.tableName(), idxDesc, op0.ifNotExists(), visitor);
+ }
+ else if (op instanceof SchemaIndexDropOperation) {
+ SchemaIndexDropOperation op0 = (SchemaIndexDropOperation) op;
+
+ idx.dynamicIndexDrop(space, op0.indexName(), op0.ifExists());
+ }
+ else
+ throw new SchemaOperationException("Unsupported operation: " + op);
+ }
+ catch (Exception e) {
+ if (e instanceof SchemaOperationException)
+ throw (SchemaOperationException)e;
+ else
+ throw new SchemaOperationException("Schema change operation failed: " + e.getMessage(), e);
}
}
/**
+ * Register cache in indexing SPI.
+ *
+ * @param space Space.
* @param cctx Cache context.
+ * @param cands Candidates.
+ * @throws IgniteCheckedException If failed.
*/
- public void onCacheStop(GridCacheContext cctx) {
- if (idx == null)
- return;
+ private void registerCache0(String space, GridCacheContext<?, ?> cctx, Collection<QueryTypeCandidate> cands)
+ throws IgniteCheckedException {
+ synchronized (stateMux) {
+ idx.registerCache(space, cctx, cctx.config());
- if (!busyLock.enterBusy())
- return;
+ try {
+ for (QueryTypeCandidate cand : cands) {
+ QueryTypeIdKey typeId = cand.typeId();
+ QueryTypeIdKey altTypeId = cand.alternativeTypeId();
+ QueryTypeDescriptorImpl desc = cand.descriptor();
- try {
- unregisterCache0(cctx.name());
- }
- finally {
- busyLock.leaveBusy();
- }
- }
+ if (typesByName.putIfAbsent(new QueryTypeNameKey(space, desc.name()), desc) != null)
+ throw new IgniteCheckedException("Type with name '" + desc.name() + "' already indexed " +
+ "in cache '" + space + "'.");
- /**
- * @return Skip field lookup flag.
- */
- public boolean skipFieldLookup() {
- return skipFieldLookup;
- }
+ types.put(typeId, desc);
- /**
- * @param skipFieldLookup Skip field lookup flag.
- */
- public void skipFieldLookup(boolean skipFieldLookup) {
- this.skipFieldLookup = skipFieldLookup;
+ if (altTypeId != null)
+ types.put(altTypeId, desc);
+
+ for (QueryIndexDescriptorImpl idx : desc.indexes0()) {
+ QueryIndexKey idxKey = new QueryIndexKey(space, idx.name());
+
+ QueryIndexDescriptorImpl oldIdx = idxs.putIfAbsent(idxKey, idx);
+
+ if (oldIdx != null) {
+ throw new IgniteException("Duplicate index name [space=" + space +
+ ", idxName=" + idx.name() + ", existingTable=" + oldIdx.typeDescriptor().tableName() +
+ ", table=" + desc.tableName() + ']');
+ }
+ }
+
+ idx.registerType(space, desc);
+ }
+
+ spaces.add(CU.mask(space));
+ }
+ catch (IgniteCheckedException | RuntimeException e) {
+ unregisterCache0(space);
+
+ throw e;
+ }
+ }
}
/**
@@ -307,13 +1317,8 @@ public class GridQueryProcessor extends GridProcessorAdapter {
private void unregisterCache0(String space) {
assert idx != null;
- try {
- idx.unregisterCache(space);
- }
- catch (Exception e) {
- U.error(log, "Failed to clear indexing on cache unregister (will ignore): " + space, e);
- }
- finally {
+ synchronized (stateMux) {
+ // Clear types.
Iterator<Map.Entry<QueryTypeIdKey, QueryTypeDescriptorImpl>> it = types.entrySet().iterator();
while (it.hasNext()) {
@@ -323,9 +1328,79 @@ public class GridQueryProcessor extends GridProcessorAdapter {
it.remove();
typesByName.remove(new QueryTypeNameKey(space, entry.getValue().name()));
+
+ entry.getValue().markObsolete();
}
}
+
+ // Clear indexes.
+ Iterator<Map.Entry<QueryIndexKey, QueryIndexDescriptorImpl>> idxIt = idxs.entrySet().iterator();
+
+ while (idxIt.hasNext()) {
+ Map.Entry<QueryIndexKey, QueryIndexDescriptorImpl> idxEntry = idxIt.next();
+
+ QueryIndexKey idxKey = idxEntry.getKey();
+
+ if (F.eq(space, idxKey.space()))
+ idxIt.remove();
+ }
+
+ // Notify in-progress index operations.
+ for (SchemaOperation op : schemaOps.values()) {
+ if (op.started())
+ op.manager().worker().cancel();
+ }
+
+ // Notify indexing.
+ try {
+ idx.unregisterCache(space);
+ }
+ catch (Exception e) {
+ U.error(log, "Failed to clear indexing on cache unregister (will ignore): " + space, e);
+ }
+
+ spaces.remove(CU.mask(space));
+ }
+ }
+
+ /**
+ * Check whether provided key and value belongs to expected space and table.
+ *
+ * @param cctx Target cache context.
+ * @param expSpace Expected space.
+ * @param expTblName Expected table name.
+ * @param key Key.
+ * @param val Value.
+ * @return {@code True} if this key-value pair belongs to expected space/table, {@code false} otherwise or
+ * if space or table doesn't exist.
+ * @throws IgniteCheckedException If failed.
+ */
+ @SuppressWarnings("ConstantConditions")
+ public boolean belongsToTable(GridCacheContext cctx, String expSpace, String expTblName, KeyCacheObject key,
+ CacheObject val) throws IgniteCheckedException {
+ QueryTypeDescriptorImpl desc = type(expSpace, val);
+
+ if (desc == null)
+ return false;
+
+ if (!F.eq(expTblName, desc.tableName()))
+ return false;
+
+ if (!cctx.cacheObjects().isBinaryObject(val)) {
+ Class<?> valCls = val.value(cctx.cacheObjectContext(), false).getClass();
+
+ if (!desc.valueClass().isAssignableFrom(valCls))
+ return false;
}
+
+ if (!cctx.cacheObjects().isBinaryObject(key)) {
+ Class<?> keyCls = key.value(cctx.cacheObjectContext(), false).getClass();
+
+ if (!desc.keyClass().isAssignableFrom(keyCls))
+ return false;
+ }
+
+ return true;
}
/**
@@ -457,7 +1532,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
if (desc == null)
return;
- idx.store(space, desc, key, partId, val, ver, expirationTime, link);
+ idx.store(space, desc.name(), key, partId, val, ver, expirationTime, link);
}
finally {
busyLock.leaveBusy();
@@ -518,6 +1593,30 @@ public class GridQueryProcessor extends GridProcessorAdapter {
}
/**
+ * Gets type descriptor for space by given object's type.
+ *
+ * @param space Space name.
+ * @param val Object to determine type for.
+ * @return Type descriptor.
+ * @throws IgniteCheckedException If failed.
+ */
+ @SuppressWarnings("ConstantConditions")
+ private QueryTypeDescriptorImpl type(@Nullable String space, CacheObject val) throws IgniteCheckedException {
+ CacheObjectContext coctx = cacheObjectContext(space);
+
+ QueryTypeIdKey id;
+
+ boolean binaryVal = ctx.cacheObjects().isBinaryObject(val);
+
+ if (binaryVal)
+ id = new QueryTypeIdKey(space, ctx.cacheObjects().typeId(val));
+ else
+ id = new QueryTypeIdKey(space, val.value(coctx, false).getClass());
+
+ return types.get(id);
+ }
+
+ /**
* @throws IgniteCheckedException If failed.
*/
private void checkEnabled() throws IgniteCheckedException {
@@ -637,9 +1736,9 @@ public class GridQueryProcessor extends GridProcessorAdapter {
@Override public QueryCursor<Cache.Entry<K, V>> applyx() throws IgniteCheckedException {
String type = qry.getType();
- QueryTypeDescriptorImpl typeDesc = type(cctx.name(), type);
+ String typeName = typeName(cctx.name(), type);
- qry.setType(typeDesc.name());
+ qry.setType(typeName);
sendQueryExecutedEvent(
qry.getSql(),
@@ -682,6 +1781,80 @@ public class GridQueryProcessor extends GridProcessorAdapter {
}
/**
+ * Entry point for index procedure.
+ *
+ * @param space Space name.
+ * @param tblName Table name.
+ * @param idx Index.
+ * @param ifNotExists When set to {@code true} operation will fail if index already exists.
+ * @return Future completed when index is created.
+ */
+ public IgniteInternalFuture<?> dynamicIndexCreate(String space, String tblName, QueryIndex idx,
+ boolean ifNotExists) {
+ SchemaAbstractOperation op = new SchemaIndexCreateOperation(UUID.randomUUID(), space, tblName, idx, ifNotExists);
+
+ return startIndexOperationDistributed(op);
+ }
+
+ /**
+ * Entry point for index drop procedure
+ *
+ * @param idxName Index name.
+ * @param ifExists When set to {@code true} operation fill fail if index doesn't exists.
+ * @return Future completed when index is created.
+ */
+ public IgniteInternalFuture<?> dynamicIndexDrop(String space, String idxName, boolean ifExists) {
+ SchemaAbstractOperation op = new SchemaIndexDropOperation(UUID.randomUUID(), space, idxName, ifExists);
+
+ return startIndexOperationDistributed(op);
+ }
+
+ /**
+ * Start distributed index change operation.
+ *
+ * @param op Operation.
+ * @return Future.
+ */
+ private IgniteInternalFuture<?> startIndexOperationDistributed(SchemaAbstractOperation op) {
+ SchemaOperationClientFuture fut = new SchemaOperationClientFuture(op.id());
+
+ SchemaOperationClientFuture oldFut = schemaCliFuts.put(op.id(), fut);
+
+ assert oldFut == null;
+
+ try {
+ ctx.discovery().sendCustomEvent(new SchemaProposeDiscoveryMessage(op));
+
+ if (log.isDebugEnabled())
+ log.debug("Sent schema propose discovery message [opId=" + op.id() + ", op=" + op + ']');
+
+ boolean disconnected0;
+
+ synchronized (stateMux) {
+ disconnected0 = disconnected;
+ }
+
+ if (disconnected0) {
+ fut.onDone(new SchemaOperationException("Client node is disconnected (operation result is unknown)."));
+
+ schemaCliFuts.remove(op.id());
+ }
+ }
+ catch (Exception e) {
+ if (e instanceof SchemaOperationException)
+ fut.onDone(e);
+ else {
+ fut.onDone(new SchemaOperationException("Failed to start schema change operation due to " +
+ "unexpected exception [opId=" + op.id() + ", op=" + op + ']', e));
+ }
+
+ schemaCliFuts.remove(op.id());
+ }
+
+ return fut;
+ }
+
+ /**
* @param sqlQry Sql query.
* @param params Params.
*/
@@ -704,14 +1877,15 @@ public class GridQueryProcessor extends GridProcessorAdapter {
}
/**
- * @param schema Schema.
+ *
+ * @param space Space name.
* @param sql Query.
* @return {@link PreparedStatement} from underlying engine to supply metadata to Prepared - most likely H2.
*/
- public PreparedStatement prepareNativeStatement(String schema, String sql) throws SQLException {
+ public PreparedStatement prepareNativeStatement(String space, String sql) throws SQLException {
checkxEnabled();
- return idx.prepareNativeStatement(schema, sql);
+ return idx.prepareNativeStatement(space, sql);
}
/**
@@ -838,9 +2012,9 @@ public class GridQueryProcessor extends GridProcessorAdapter {
return executeQuery(GridCacheQueryType.TEXT, clause, cctx,
new IgniteOutClosureX<GridCloseableIterator<IgniteBiTuple<K, V>>>() {
@Override public GridCloseableIterator<IgniteBiTuple<K, V>> applyx() throws IgniteCheckedException {
- QueryTypeDescriptorImpl type = type(space, resType);
+ String typeName = typeName(space, resType);
- return idx.queryLocalText(space, clause, type, filters);
+ return idx.queryLocalText(space, clause, typeName, filters);
}
}, true);
}
@@ -924,20 +2098,35 @@ public class GridQueryProcessor extends GridProcessorAdapter {
}
/**
- * Gets type descriptor for space and type name.
+ * Get type descriptor for the given space and table name.
+ * @param space Space.
+ * @param tblName Table name.
+ * @return Type (if any).
+ */
+ @Nullable private QueryTypeDescriptorImpl type(@Nullable String space, String tblName) {
+ for (QueryTypeDescriptorImpl type : types.values()) {
+ if (F.eq(space, type.space()) && F.eq(tblName, type.tableName()))
+ return type;
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets type name for provided space and type name if type is still valid.
*
* @param space Space name.
* @param typeName Type name.
* @return Type descriptor.
* @throws IgniteCheckedException If failed.
*/
- public QueryTypeDescriptorImpl type(@Nullable String space, String typeName) throws IgniteCheckedException {
+ private String typeName(@Nullable String space, String typeName) throws IgniteCheckedException {
QueryTypeDescriptorImpl type = typesByName.get(new QueryTypeNameKey(space, typeName));
if (type == null)
throw new IgniteCheckedException("Failed to find SQL table for type: " + typeName);
- return type;
+ return type.name();
}
/**
@@ -972,7 +2161,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
throw (IgniteCheckedException)err;
}
- catch (CacheException e) {
+ catch (CacheException | IgniteException e) {
err = e;
throw e;
@@ -998,6 +2187,146 @@ public class GridQueryProcessor extends GridProcessorAdapter {
}
/**
+ * Send status message to coordinator node.
+ *
+ * @param destNodeId Destination node ID.
+ * @param opId Operation ID.
+ * @param err Error.
+ */
+ public void sendStatusMessage(UUID destNodeId, UUID opId, SchemaOperationException err) {
+ if (log.isDebugEnabled())
+ log.debug("Sending schema operation status message [opId=" + opId + ", crdNode=" + destNodeId +
+ ", err=" + err + ']');
+
+ try {
+ byte[] errBytes = marshalSchemaError(opId, err);
+
+ SchemaOperationStatusMessage msg = new SchemaOperationStatusMessage(opId, errBytes);
+
+ // Messages must go to dedicated schema pool. We cannot push them to query pool because in this case
+ // they could be blocked with other query requests.
+ ctx.io().sendToGridTopic(destNodeId, TOPIC_SCHEMA, msg, SCHEMA_POOL);
+ }
+ catch (IgniteCheckedException e) {
+ if (log.isDebugEnabled())
+ log.debug("Failed to send schema status response [opId=" + opId + ", destNodeId=" + destNodeId +
+ ", err=" + e + ']');
+ }
+ }
+
+ /**
+ * Process status message.
+ *
+ * @param msg Status message.
+ */
+ private void processStatusMessage(SchemaOperationStatusMessage msg) {
+ synchronized (stateMux) {
+ if (completedOpIds.contains(msg.operationId())) {
+ // Received message from a node which joined topology in the middle of operation execution.
+ if (log.isDebugEnabled())
+ log.debug("Received status message for completed operation (will ignore) [" +
+ "opId=" + msg.operationId() + ", sndNodeId=" + msg.senderNodeId() + ']');
+
+ return;
+ }
+
+ UUID opId = msg.operationId();
+
+ SchemaProposeDiscoveryMessage proposeMsg = activeProposals.get(opId);
+
+ if (proposeMsg != null) {
+ SchemaOperation op = schemaOps.get(proposeMsg.schemaKey());
+
+ if (op != null && F.eq(op.id(), opId) && op.started() && coordinator().isLocal()) {
+ if (log.isDebugEnabled())
+ log.debug("Received status message [opId=" + msg.operationId() +
+ ", sndNodeId=" + msg.senderNodeId() + ']');
+
+ op.manager().onNodeFinished(msg.senderNodeId(), unmarshalSchemaError(msg.errorBytes()));
+
+ return;
+ }
+ }
+
+ // Put to pending set if operation is not visible/ready yet.
+ pendingMsgs.add(msg);
+
+ if (log.isDebugEnabled())
+ log.debug("Received status message (added to pending set) [opId=" + msg.operationId() +
+ ", sndNodeId=" + msg.senderNodeId() + ']');
+ }
+ }
+
+ /**
+ * Unwind pending messages for particular operation.
+ *
+ * @param opId Operation ID.
+ * @param mgr Manager.
+ */
+ private void unwindPendingMessages(UUID opId, SchemaOperationManager mgr) {
+ assert Thread.holdsLock(stateMux);
+
+ Iterator<SchemaOperationStatusMessage> it = pendingMsgs.iterator();
+
+ while (it.hasNext()) {
+ SchemaOperationStatusMessage msg = it.next();
+
+ if (F.eq(msg.operationId(), opId)) {
+ mgr.onNodeFinished(msg.senderNodeId(), unmarshalSchemaError(msg.errorBytes()));
+
+ it.remove();
+ }
+ }
+ }
+
+ /**
+ * Marshal schema error.
+ *
+ * @param err Error.
+ * @return Error bytes.
+ */
+ @Nullable private byte[] marshalSchemaError(UUID opId, @Nullable SchemaOperationException err) {
+ if (err == null)
+ return null;
+
+ try {
+ return U.marshal(marsh, err);
+ }
+ catch (Exception e) {
+ U.warn(log, "Failed to marshal schema operation error [opId=" + opId + ", err=" + err + ']', e);
+
+ try {
+ return U.marshal(marsh, new SchemaOperationException("Operation failed, but error cannot be " +
+ "serialized (see local node log for more details) [opId=" + opId + ", nodeId=" +
+ ctx.localNodeId() + ']'));
+ }
+ catch (Exception e0) {
+ assert false; // Impossible situation.
+
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Unmarshal schema error.
+ *
+ * @param errBytes Error bytes.
+ * @return Error.
+ */
+ @Nullable private SchemaOperationException unmarshalSchemaError(@Nullable byte[] errBytes) {
+ if (errBytes == null)
+ return null;
+
+ try {
+ return U.unmarshal(marsh, errBytes, U.resolveClassLoader(ctx.config()));
+ }
+ catch (Exception e) {
+ return new SchemaOperationException("Operation failed, but error cannot be deserialized.");
+ }
+ }
+
+ /**
* @param ver Version.
*/
public static void setRequestAffinityTopologyVersion(AffinityTopologyVersion ver) {
@@ -1010,4 +2339,160 @@ public class GridQueryProcessor extends GridProcessorAdapter {
public static AffinityTopologyVersion getRequestAffinityTopologyVersion() {
return requestTopVer.get();
}
+
+ /**
+ * Schema operation.
+ */
+ private class SchemaOperation {
+ /** Original propose msg. */
+ private final SchemaProposeDiscoveryMessage proposeMsg;
+
+ /** Next schema operation. */
+ private SchemaOperation next;
+
+ /** Operation manager. */
+ private SchemaOperationManager mgr;
+
+ /** Finish message. */
+ private SchemaFinishDiscoveryMessage finishMsg;
+
+ /** Finish guard. */
+ private final AtomicBoolean finishGuard = new AtomicBoolean();
+
+ /**
+ * Constructor.
+ *
+ * @param proposeMsg Original propose message.
+ */
+ public SchemaOperation(SchemaProposeDiscoveryMessage proposeMsg) {
+ this.proposeMsg = proposeMsg;
+ }
+
+ /**
+ * @return Operation ID.
+ */
+ public UUID id() {
+ return proposeMsg.operation().id();
+ }
+
+ /**
+ * @return Original propose message.
+ */
+ public SchemaProposeDiscoveryMessage proposeMessage() {
+ return proposeMsg;
+ }
+
+ /**
+ * @return Next schema operation.
+ */
+ @Nullable public SchemaOperation next() {
+ return next;
+ }
+
+ /**
+ * @param next Next schema operation.
+ */
+ public void next(SchemaOperation next) {
+ this.next = next;
+ }
+
+ /**
+ * @param finishMsg Finish message.
+ */
+ public void finishMessage(SchemaFinishDiscoveryMessage finishMsg) {
+ this.finishMsg = finishMsg;
+ }
+
+ /**
+ * @return {@code True} if finish request already received.
+ */
+ public boolean hasFinishMessage() {
+ return finishMsg != null;
+ }
+
+ /**
+ * Handle finish message.
+ */
+ @SuppressWarnings("unchecked")
+ public void doFinish() {
+ assert started();
+
+ if (!finishGuard.compareAndSet(false, true))
+ return;
+
+ final UUID opId = id();
+ final SchemaKey key = proposeMsg.schemaKey();
+
+ // Operation might be still in progress on client nodes which are not tracked by coordinator,
+ // so we chain to operation future instead of doing synchronous unwind.
+ mgr.worker().future().listen(new IgniteInClosure<IgniteInternalFuture>() {
+ @Override public void apply(IgniteInternalFuture fut) {
+ synchronized (stateMux) {
+ SchemaOperation op = schemaOps.remove(key);
+
+ assert op != null;
+ assert F.eq(op.id(), opId);
+
+ // Chain to the next operation (if any).
+ final SchemaOperation nextOp = op.next();
+
+ if (nextOp != null) {
+ schemaOps.put(key, nextOp);
+
+ if (log.isDebugEnabled())
+ log.debug("Next schema change operation started [opId=" + nextOp.id() + ']');
+
+ assert !nextOp.started();
+
+ // Cannot execute operation synchronously because it may cause starvation in exchange
+ // thread under load. Hence, moving short-lived operation to separate worker.
+ new IgniteThread(ctx.igniteInstanceName(), "schema-circuit-breaker-" + op.id(),
+ new Runnable() {
+ @Override public void run() {
+ onSchemaPropose(nextOp.proposeMessage());
+ }
+ }).start();
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Unwind operation queue and get tail operation.
+ *
+ * @return Tail operation.
+ */
+ public SchemaOperation unwind() {
+ if (next == null)
+ return this;
+ else
+ return next.unwind();
+ }
+
+ /**
+ * Whether operation started.
+ *
+ * @return {@code True} if started.
+ */
+ public boolean started() {
+ return mgr != null;
+ }
+
+ /**
+ * @return Operation manager.
+ */
+ public SchemaOperationManager manager() {
+ return mgr;
+ }
+
+ /**
+ * @param mgr Operation manager.
+ */
+ public void manager(SchemaOperationManager mgr) {
+ assert this.mgr == null;
+
+ this.mgr = mgr;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
index 44c41c1..b7434d3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
@@ -81,6 +81,13 @@ public interface GridQueryTypeDescriptor {
public Map<String, GridQueryIndexDescriptor> indexes();
/**
+ * Get text index for this type (if any).
+ *
+ * @return Text index or {@code null}.
+ */
+ public GridQueryIndexDescriptor textIndex();
+
+ /**
* Gets value class.
*
* @return Value class.
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/IgniteSQLException.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/IgniteSQLException.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/IgniteSQLException.java
index b15007e..0666493 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/IgniteSQLException.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/IgniteSQLException.java
@@ -81,6 +81,13 @@ public class IgniteSQLException extends IgniteException {
}
/**
+ * @return Ignite SQL error code.
+ */
+ public int statusCode() {
+ return statusCode;
+ }
+
+ /**
* @return JDBC exception containing details from this instance.
*/
public SQLException toJdbcException() {
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryIndexDescriptorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryIndexDescriptorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryIndexDescriptorImpl.java
index 9d2d20c..1b85af5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryIndexDescriptorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryIndexDescriptorImpl.java
@@ -17,7 +17,9 @@
package org.apache.ignite.internal.processors.query;
+import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cache.QueryIndexType;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.internal.S;
@@ -45,22 +47,48 @@ public class QueryIndexDescriptorImpl implements GridQueryIndexDescriptor {
/** Fields which should be indexed in descending order. */
private Collection<String> descendings;
+ /** Type descriptor. */
+ @GridToStringExclude
+ private final QueryTypeDescriptorImpl typDesc;
+
+ /** Index name. */
+ private final String name;
+
/** */
private final QueryIndexType type;
/** */
- private int inlineSize;
+ private final int inlineSize;
/**
+ * Constructor.
+ *
+ * @param typDesc Type descriptor.
+ * @param name Index name.
* @param type Type.
+ * @param inlineSize Inline size.
*/
- public QueryIndexDescriptorImpl(QueryIndexType type, int inlineSize) {
+ public QueryIndexDescriptorImpl(QueryTypeDescriptorImpl typDesc, String name, QueryIndexType type, int inlineSize) {
assert type != null;
+ this.typDesc = typDesc;
+ this.name = name;
this.type = type;
this.inlineSize = inlineSize;
}
+ /**
+ * @return Type descriptor.
+ */
+ public QueryTypeDescriptorImpl typeDescriptor() {
+ return typDesc;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return name;
+ }
+
/** {@inheritDoc} */
@Override public Collection<String> fields() {
Collection<String> res = new ArrayList<>(fields.size());
@@ -87,8 +115,14 @@ public class QueryIndexDescriptorImpl implements GridQueryIndexDescriptor {
* @param field Field name.
* @param orderNum Field order number in this index.
* @param descending Sort order.
+ * @return This instance for chaining.
+ * @throws IgniteCheckedException If failed.
*/
- public void addField(String field, int orderNum, boolean descending) {
+ public QueryIndexDescriptorImpl addField(String field, int orderNum, boolean descending)
+ throws IgniteCheckedException {
+ if (!typDesc.hasField(field))
+ throw new IgniteCheckedException("Field not found: " + field);
+
fields.add(new T2<>(field, orderNum));
if (descending) {
@@ -97,6 +131,8 @@ public class QueryIndexDescriptorImpl implements GridQueryIndexDescriptor {
descendings.add(field);
}
+
+ return this;
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryIndexKey.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryIndexKey.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryIndexKey.java
new file mode 100644
index 0000000..f580111
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryIndexKey.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query;
+
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.io.Serializable;
+
+/**
+ * Index key.
+ */
+public class QueryIndexKey implements Serializable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Space. */
+ private final String space;
+
+ /** Name. */
+ private final String name;
+
+ /**
+ * Constructor.
+ *
+ * @param space Space.
+ * @param name Name.
+ */
+ public QueryIndexKey(String space, String name) {
+ this.space = space;
+ this.name = name;
+ }
+
+ /**
+ * @return Space.
+ */
+ public String space() {
+ return space;
+ }
+
+ /**
+ * @return Name.
+ */
+ public String name() {
+ return name;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return 31 * (space != null ? space.hashCode() : 0) + (name != null ? name.hashCode() : 0);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ QueryIndexKey other = (QueryIndexKey)o;
+
+ return F.eq(name, other.name) && F.eq(space, other.space);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(QueryIndexKey.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QuerySchema.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QuerySchema.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QuerySchema.java
new file mode 100644
index 0000000..395f077
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QuerySchema.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query;
+
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.internal.processors.query.schema.message.SchemaFinishDiscoveryMessage;
+import org.apache.ignite.internal.processors.query.schema.operation.SchemaAbstractOperation;
+import org.apache.ignite.internal.processors.query.schema.operation.SchemaIndexCreateOperation;
+import org.apache.ignite.internal.processors.query.schema.operation.SchemaIndexDropOperation;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Dynamic cache schema.
+ */
+public class QuerySchema implements Serializable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Query entities. */
+ private final Collection<QueryEntity> entities = new LinkedList<>();
+
+ /** Mutex for state synchronization. */
+ private final Object mux = new Object();
+
+ /**
+ * Default constructor.
+ */
+ public QuerySchema() {
+ // No-op.
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param entities Query entities.
+ */
+ public QuerySchema(Collection<QueryEntity> entities) {
+ assert entities != null;
+
+ for (QueryEntity qryEntity : entities)
+ this.entities.add(new QueryEntity(qryEntity));
+ }
+
+ /**
+ * Copy object.
+ *
+ * @return Copy.
+ */
+ public QuerySchema copy() {
+ synchronized (mux) {
+ QuerySchema res = new QuerySchema();
+
+ for (QueryEntity qryEntity : entities)
+ res.entities.add(new QueryEntity(qryEntity));
+
+ return res;
+ }
+ }
+
+ /**
+ * Process finish message.
+ *
+ * @param msg Message.
+ */
+ public void finish(SchemaFinishDiscoveryMessage msg) {
+ synchronized (mux) {
+ SchemaAbstractOperation op = msg.operation();
+
+ if (op instanceof SchemaIndexCreateOperation) {
+ SchemaIndexCreateOperation op0 = (SchemaIndexCreateOperation)op;
+
+ for (QueryEntity entity : entities) {
+ String tblName = QueryUtils.tableName(entity);
+
+ if (F.eq(tblName, op0.tableName())) {
+ boolean exists = false;
+
+ for (QueryIndex idx : entity.getIndexes()) {
+ if (F.eq(idx.getName(), op0.indexName())) {
+ exists = true;
+
+ break;
+ }
+ }
+
+ if (!exists) {
+ List<QueryIndex> idxs = new ArrayList<>(entity.getIndexes());
+
+ idxs.add(op0.index());
+
+ entity.clearIndexes();
+ entity.setIndexes(idxs);
+ }
+
+ break;
+ }
+ }
+ }
+ else {
+ assert op instanceof SchemaIndexDropOperation;
+
+ SchemaIndexDropOperation op0 = (SchemaIndexDropOperation)op;
+
+ for (QueryEntity entity : entities) {
+ Collection<QueryIndex> idxs = entity.getIndexes();
+
+ QueryIndex victim = null;
+
+ for (QueryIndex idx : idxs) {
+ if (F.eq(idx.getName(), op0.indexName())) {
+ victim = idx;
+
+ break;
+ }
+ }
+
+ if (victim != null) {
+ List<QueryIndex> newIdxs = new ArrayList<>(entity.getIndexes());
+
+ newIdxs.remove(victim);
+
+ entity.clearIndexes();
+ entity.setIndexes(idxs);
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @return Query entities.
+ */
+ public Collection<QueryEntity> entities() {
+ synchronized (mux) {
+ return new ArrayList<>(entities);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(QuerySchema.class, this);
+ }
+}
[3/8] ignite git commit: IGNITE-4565: Implemented CREATE INDEX and
DROP INDEX. This closes #1773. This closes #1804.
Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
index 98d227c..a9c1a20 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
@@ -17,12 +17,10 @@
package org.apache.ignite.internal.processors.query.h2.opt;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.Statement;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
@@ -35,11 +33,9 @@ import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory;
-import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndex;
import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.lang.IgniteBiTuple;
-import org.h2.api.TableEngine;
import org.h2.command.ddl.CreateTableData;
import org.h2.engine.Session;
import org.h2.index.Index;
@@ -50,7 +46,6 @@ import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.IndexColumn;
import org.h2.table.TableBase;
-import org.h2.table.TableFilter;
import org.h2.value.Value;
import org.jetbrains.annotations.Nullable;
import org.jsr166.ConcurrentHashMap8;
@@ -72,7 +67,10 @@ public class GridH2Table extends TableBase {
private final GridH2RowDescriptor desc;
/** */
- private final ArrayList<Index> idxs;
+ private volatile ArrayList<Index> idxs;
+
+ /** */
+ private final Map<String, GridH2IndexBase> tmpIdxs = new HashMap<>();
/** */
private final ReadWriteLock lock;
@@ -81,7 +79,7 @@ public class GridH2Table extends TableBase {
private boolean destroyed;
/** */
- private final Set<Session> sessions = Collections.newSetFromMap(new ConcurrentHashMap8<Session,Boolean>());
+ private final ConcurrentMap<Session, Boolean> sessions = new ConcurrentHashMap8<>();
/** */
private final AtomicReference<Object[]> actualSnapshot = new AtomicReference<>();
@@ -106,11 +104,12 @@ public class GridH2Table extends TableBase {
*
* @param createTblData Table description.
* @param desc Row descriptor.
+ * @param rowFactory Row factory.
* @param idxsFactory Indexes factory.
* @param spaceName Space name.
*/
- public GridH2Table(CreateTableData createTblData, @Nullable GridH2RowDescriptor desc, IndexesFactory idxsFactory,
- @Nullable String spaceName) {
+ public GridH2Table(CreateTableData createTblData, @Nullable GridH2RowDescriptor desc, H2RowFactory rowFactory,
+ GridH2SystemIndexFactory idxsFactory, @Nullable String spaceName) {
super(createTblData);
assert idxsFactory != null;
@@ -143,18 +142,18 @@ public class GridH2Table extends TableBase {
}
}
+ this.rowFactory = rowFactory;
+
// Indexes must be created in the end when everything is ready.
- rowFactory = idxsFactory.createRowFactory(this);
- idxs = idxsFactory.createIndexes(this);
+ idxs = idxsFactory.createSystemIndexes(this);
assert idxs != null;
// Add scan index at 0 which is required by H2.
- if (idxs.size() >= 2
- && index(0).getIndexType().isHash())
- idxs.add(0, new ScanIndex(index(1), index(0)));
+ if (idxs.size() >= 2 && index(0).getIndexType().isHash())
+ idxs.add(0, new GridH2PrimaryScanIndex(this, index(1), index(0)));
else
- idxs.add(0, new ScanIndex(index(0), null));
+ idxs.add(0, new GridH2PrimaryScanIndex(this, index(0), null));
snapshotEnabled = desc == null || desc.snapshotableIndex();
@@ -238,6 +237,8 @@ public class GridH2Table extends TableBase {
desc.guard().begin();
try {
+ ensureNotDestroyed();
+
GridH2AbstractKeyValueRow row = (GridH2AbstractKeyValueRow)pk.findOne(searchRow);
if (row == null)
@@ -266,13 +267,21 @@ public class GridH2Table extends TableBase {
}
/** {@inheritDoc} */
- @SuppressWarnings({"LockAcquiredButNotSafelyReleased", "SynchronizationOnLocalVariableOrMethodParameter", "unchecked"})
- @Override public boolean lock(@Nullable final Session ses, boolean exclusive, boolean force) {
- if (ses != null) {
- if (!sessions.add(ses))
- return false;
+ @Override public boolean lock(Session ses, boolean exclusive, boolean force) {
+ Boolean putRes = sessions.putIfAbsent(ses, exclusive);
+
+ // In accordance with base method semantics, we'll return true if we were already exclusively locked
+ if (putRes != null)
+ return putRes;
+
+ ses.addLock(this);
+
+ lock(exclusive);
+
+ if (destroyed) {
+ unlock(exclusive);
- ses.addLock(this);
+ throw new IllegalStateException("Table " + identifier() + " already destroyed.");
}
if (snapshotInLock())
@@ -320,6 +329,8 @@ public class GridH2Table extends TableBase {
}
try {
+ ensureNotDestroyed();
+
// Try again inside of the lock.
snapshots = actualSnapshot.get();
@@ -362,12 +373,6 @@ public class GridH2Table extends TableBase {
throw new IgniteInterruptedException("Thread got interrupted while trying to acquire table lock.", e);
}
-
- if (destroyed) {
- unlock(exclusive);
-
- throw new IllegalStateException("Table " + identifier() + " already destroyed.");
- }
}
/**
@@ -388,12 +393,6 @@ public class GridH2Table extends TableBase {
throw new IgniteInterruptedException("Thread got interrupted while trying to acquire table lock.", e);
}
- if (destroyed) {
- unlock(exclusive);
-
- throw new IllegalStateException("Table " + identifier() + " already destroyed.");
- }
-
return true;
}
@@ -409,6 +408,14 @@ public class GridH2Table extends TableBase {
}
/**
+ * Check if table is not destroyed.
+ */
+ private void ensureNotDestroyed() {
+ if (destroyed)
+ throw new IllegalStateException("Table " + identifier() + " already destroyed.");
+ }
+
+ /**
* Must be called inside of write lock because when using multiple indexes we have to ensure that all of them have
* the same contents at snapshot taking time.
*
@@ -462,6 +469,8 @@ public class GridH2Table extends TableBase {
lock(true);
try {
+ ensureNotDestroyed();
+
assert sessions.isEmpty() : sessions;
destroyed = true;
@@ -475,12 +484,16 @@ public class GridH2Table extends TableBase {
}
/** {@inheritDoc} */
- @Override public void unlock(@Nullable Session ses) {
- if (ses != null && !sessions.remove(ses))
+ @Override public void unlock(Session ses) {
+ Boolean exclusive = sessions.remove(ses);
+
+ if (exclusive == null)
return;
if (snapshotInLock())
releaseSnapshots();
+
+ unlock(exclusive);
}
/**
@@ -599,6 +612,8 @@ public class GridH2Table extends TableBase {
desc.guard().begin();
try {
+ ensureNotDestroyed();
+
GridH2IndexBase pk = pk();
if (!del) {
@@ -623,18 +638,11 @@ public class GridH2Table extends TableBase {
while (++i < len) {
GridH2IndexBase idx = index(i);
- assert !idx.getIndexType().isUnique() : "Unique indexes are not supported: " + idx;
-
- GridH2Row old2 = idx.put(row);
-
- if (old2 != null) { // Row was replaced in index.
- if (!eq(pk, old2, old))
- throw new IllegalStateException("Row conflict should never happen, unique indexes are " +
- "not supported [idx=" + idx + ", old=" + old + ", old2=" + old2 + ']');
- }
- else if (old != null) // Row was not replaced, need to remove manually.
- idx.removex(old);
+ addToIndex(idx, pk, row, old, false);
}
+
+ for (GridH2IndexBase idx : tmpIdxs.values())
+ addToIndex(idx, pk, row, old, true);
}
else {
// index(1) is PK, get full row from there (search row here contains only key but no other columns).
@@ -656,6 +664,9 @@ public class GridH2Table extends TableBase {
assert eq(pk, res, old) : "\n" + old + "\n" + res + "\n" + i + " -> " + index(i).getName();
}
+ for (GridH2IndexBase idx : tmpIdxs.values())
+ idx.remove(old);
+
size.decrement();
}
else
@@ -676,6 +687,31 @@ public class GridH2Table extends TableBase {
}
/**
+ * Add row to index.
+ *
+ * @param idx Index to add row to.
+ * @param pk Primary key index.
+ * @param row Row to add to index.
+ * @param old Previous row state, if any.
+ * @param tmp {@code True} if this is proposed index which may be not consistent yet.
+ */
+ private void addToIndex(GridH2IndexBase idx, Index pk, GridH2Row row, GridH2Row old, boolean tmp) {
+ assert !idx.getIndexType().isUnique() : "Unique indexes are not supported: " + idx;
+
+ GridH2Row old2 = idx.put(row);
+
+ if (old2 != null) { // Row was replaced in index.
+ if (!tmp) {
+ if (!eq(pk, old2, old))
+ throw new IllegalStateException("Row conflict should never happen, unique indexes are " +
+ "not supported [idx=" + idx + ", old=" + old + ", old2=" + old2 + ']');
+ }
+ }
+ else if (old != null) // Row was not replaced, need to remove manually.
+ idx.removex(old);
+ }
+
+ /**
* Check row equality.
*
* @param pk Primary key index.
@@ -716,9 +752,119 @@ public class GridH2Table extends TableBase {
}
/** {@inheritDoc} */
- @Override public Index addIndex(Session ses, String s, int i, IndexColumn[] idxCols, IndexType idxType,
- boolean b, String s1) {
- throw DbException.getUnsupportedException("addIndex");
+ @Override public Index addIndex(Session ses, String idxName, int idxId, IndexColumn[] cols, IndexType idxType,
+ boolean create, String idxComment) {
+ return commitUserIndex(ses, idxName);
+ }
+
+ /**
+ * Add index that is in an intermediate state and is still being built, thus is not used in queries until it is
+ * promoted.
+ *
+ * @param idx Index to add.
+ * @throws IgniteCheckedException If failed.
+ */
+ public void proposeUserIndex(Index idx) throws IgniteCheckedException {
+ assert idx instanceof GridH2IndexBase;
+
+ lock(true);
+
+ try {
+ ensureNotDestroyed();
+
+ for (Index oldIdx : idxs) {
+ if (F.eq(oldIdx.getName(), idx.getName()))
+ throw new IgniteCheckedException("Index already exists: " + idx.getName());
+ }
+
+ Index oldTmpIdx = tmpIdxs.put(idx.getName(), (GridH2IndexBase)idx);
+
+ assert oldTmpIdx == null;
+ }
+ finally {
+ unlock(true);
+ }
+ }
+
+ /**
+ * Promote temporary index to make it usable in queries.
+ *
+ * @param ses H2 session.
+ * @param idxName Index name.
+ * @return Temporary index with given name.
+ */
+ private Index commitUserIndex(Session ses, String idxName) {
+ lock(true);
+
+ try {
+ ensureNotDestroyed();
+
+ Index idx = tmpIdxs.remove(idxName);
+
+ assert idx != null;
+
+ ArrayList<Index> newIdxs = new ArrayList<>(idxs.size() + 1);
+
+ newIdxs.addAll(idxs);
+
+ newIdxs.add(idx);
+
+ idxs = newIdxs;
+
+ database.addSchemaObject(ses, idx);
+
+ setModified();
+
+ return idx;
+ }
+ finally {
+ unlock(true);
+ }
+ }
+
+ /**
+ * Remove user index without promoting it.
+ *
+ * @param idxName Index name.
+ */
+ public void rollbackUserIndex(String idxName) {
+ lock(true);
+
+ try {
+ ensureNotDestroyed();
+
+ GridH2IndexBase rmvIdx = tmpIdxs.remove(idxName);
+
+ assert rmvIdx != null;
+ }
+ finally {
+ unlock(true);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void removeIndex(Index h2Idx) {
+ lock(true);
+
+ try {
+ ArrayList<Index> idxs = new ArrayList<>(this.idxs);
+
+ for (int i = 2; i < idxs.size(); i++) {
+ GridH2IndexBase idx = (GridH2IndexBase)idxs.get(i);
+
+ if (idx != h2Idx)
+ continue;
+
+ idxs.remove(i);
+
+ this.idxs = idxs;
+
+ return;
+ }
+ }
+ finally {
+ unlock(true);
+ }
}
/** {@inheritDoc} */
@@ -848,136 +994,4 @@ public class GridH2Table extends TableBase {
return rowFactory;
}
- /**
- * H2 Table engine.
- */
- @SuppressWarnings({"PublicInnerClass", "FieldAccessedSynchronizedAndUnsynchronized"})
- public static class Engine implements TableEngine {
- /** */
- private static GridH2RowDescriptor rowDesc;
-
- /** */
- private static IndexesFactory idxsFactory;
-
- /** */
- private static GridH2Table resTbl;
-
- /** */
- private static String spaceName;
-
- /** {@inheritDoc} */
- @Override public TableBase createTable(CreateTableData createTblData) {
- resTbl = new GridH2Table(createTblData, rowDesc, idxsFactory, spaceName);
-
- return resTbl;
- }
-
- /**
- * Creates table using given connection, DDL clause for given type descriptor and list of indexes.
- *
- * @param conn Connection.
- * @param sql DDL clause.
- * @param desc Row descriptor.
- * @param factory Indexes factory.
- * @param space Space name.
- * @throws SQLException If failed.
- * @return Created table.
- */
- public static synchronized GridH2Table createTable(Connection conn, String sql,
- @Nullable GridH2RowDescriptor desc, IndexesFactory factory, String space)
- throws SQLException {
- rowDesc = desc;
- idxsFactory = factory;
- spaceName = space;
-
- try {
- try (Statement s = conn.createStatement()) {
- s.execute(sql + " engine \"" + Engine.class.getName() + "\"");
- }
-
- return resTbl;
- }
- finally {
- resTbl = null;
- idxsFactory = null;
- rowDesc = null;
- }
- }
- }
-
- /**
- * Type which can create indexes list for given table.
- */
- @SuppressWarnings({"PackageVisibleInnerClass", "PublicInnerClass"})
- public static interface IndexesFactory {
- /**
- * Create list of indexes. First must be primary key, after that all unique indexes and
- * only then non-unique indexes.
- * All indexes must be subtypes of {@link H2TreeIndex}.
- *
- * @param tbl Table to create indexes for.
- * @return List of indexes.
- */
- ArrayList<Index> createIndexes(GridH2Table tbl);
-
- /**
- * @param tbl Table.
- * @return Data store.
- */
- H2RowFactory createRowFactory(GridH2Table tbl);
- }
-
- /**
- * Wrapper type for primary key.
- */
- @SuppressWarnings("PackageVisibleInnerClass")
- class ScanIndex extends GridH2ScanIndex<GridH2IndexBase> {
- /** */
- static final String SCAN_INDEX_NAME_SUFFIX = "__SCAN_";
-
- /** */
- private final GridH2IndexBase hashIdx;
-
- /**
- * Constructor.
- */
- private ScanIndex(GridH2IndexBase treeIdx, GridH2IndexBase hashIdx) {
- super(treeIdx);
-
- this.hashIdx = hashIdx;
- }
-
- /**
- *
- */
- @Override protected GridH2IndexBase delegate() {
- if (hashIdx != null)
- return rebuildFromHashInProgress ? hashIdx : super.delegate();
- else {
- assert !rebuildFromHashInProgress;
-
- return super.delegate();
- }
- }
-
- /** {@inheritDoc} */
- @Override public double getCost(Session ses, int[] masks, TableFilter[] filters, int filter,
- SortOrder sortOrder) {
- long rows = getRowCountApproximation();
- double baseCost = getCostRangeIndex(masks, rows, filters, filter, sortOrder, true);
- int mul = delegate().getDistributedMultiplier(ses, filters, filter);
-
- return mul * baseCost;
- }
-
- /** {@inheritDoc} */
- @Override public String getPlanSQL() {
- return delegate().getTable().getSQL() + "." + SCAN_INDEX_NAME_SUFFIX;
- }
-
- /** {@inheritDoc} */
- @Override public String getName() {
- return delegate().getName() + SCAN_INDEX_NAME_SUFFIX;
- }
- }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java
index b73bb96..4395024 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java
@@ -116,15 +116,7 @@ public class GridLuceneIndex implements AutoCloseable {
throw new IgniteCheckedException(e);
}
- GridQueryIndexDescriptor idx = null;
-
- for (GridQueryIndexDescriptor descriptor : type.indexes().values()) {
- if (descriptor.type() == QueryIndexType.FULLTEXT) {
- idx = descriptor;
-
- break;
- }
- }
+ GridQueryIndexDescriptor idx = type.textIndex();
if (idx != null) {
Collection<String> fields = idx.fields();
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateIndex.java
new file mode 100644
index 0000000..50d455c
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateIndex.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.sql;
+
+import java.util.Map;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.QueryIndexType;
+import org.h2.command.Parser;
+
+/**
+ * CREATE INDEX statement.
+ */
+public class GridSqlCreateIndex extends GridSqlStatement {
+ /** Schema name. */
+ private String schemaName;
+
+ /** Table name. */
+ private String tblName;
+
+ /** Attempt to create the index only if it does not exist. */
+ private boolean ifNotExists;
+
+ /** Index to create. */
+ private QueryIndex idx;
+
+ /**
+ * @return Schema name for new index.
+ */
+ public String schemaName() {
+ return schemaName;
+ }
+
+ /**
+ * @param schemaName Schema name for new index.
+ */
+ public void schemaName(String schemaName) {
+ this.schemaName = schemaName;
+ }
+
+ /**
+ * @return Table name.
+ */
+ public String tableName() {
+ return tblName;
+ }
+
+ /**
+ * @param tblName Table name.
+ */
+ public void tableName(String tblName) {
+ this.tblName = tblName;
+ }
+
+ /**
+ * @return whether attempt to create the index should be made only if it does not exist.
+ */
+ public boolean ifNotExists() {
+ return ifNotExists;
+ }
+
+ /**
+ * @param ifNotExists whether attempt to create the index should be made only if it does not exist.
+ */
+ public void ifNotExists(boolean ifNotExists) {
+ this.ifNotExists = ifNotExists;
+ }
+
+ /**
+ * @return Index to create.
+ */
+ public QueryIndex index() {
+ return idx;
+ }
+
+ /**
+ * @param idx Index to create.
+ */
+ public void index(QueryIndex idx) {
+ this.idx = idx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getSQL() {
+ StringBuilder sb = new StringBuilder("CREATE ")
+ .append(idx.getIndexType() == QueryIndexType.GEOSPATIAL ? "SPATIAL " : "")
+ .append("INDEX ").append(ifNotExists ? "IF NOT EXISTS " : "")
+ .append(Parser.quoteIdentifier(schemaName)).append('.')
+ .append(Parser.quoteIdentifier(idx.getName())).append(" ON ")
+ .append(Parser.quoteIdentifier(tblName)).append(" (");
+
+ boolean first = true;
+
+ for (Map.Entry<String, Boolean> e : idx.getFields().entrySet()) {
+ if (first)
+ first = false;
+ else
+ sb.append(", ");
+
+ sb.append(Parser.quoteIdentifier(e.getKey())).append(e.getValue() ? " ASC" : " DESC");
+ }
+
+ sb.append(')');
+
+ return sb.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlDropIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlDropIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlDropIndex.java
new file mode 100644
index 0000000..c844b49
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlDropIndex.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.sql;
+
+import org.h2.command.Parser;
+
+/**
+ * DROP INDEX statement.
+ */
+public class GridSqlDropIndex extends GridSqlStatement {
+ /** Index name. */
+ private String name;
+
+ /** Schema name. */
+ private String schemaName;
+
+ /** Attempt to drop the index only if it exists. */
+ private boolean ifExists;
+
+ /**
+ * @return Index name.
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * @param name Index name.
+ */
+ public void name(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return Schema name.
+ */
+ public String schemaName() {
+ return schemaName;
+ }
+
+ /**
+ * @param schemaName Schema name.
+ */
+ public void schemaName(String schemaName) {
+ this.schemaName = schemaName;
+ }
+
+ /**
+ * @return whether attempt to drop the index should be made only if it exists.
+ */
+ public boolean ifExists() {
+ return ifExists;
+ }
+
+ /**
+ * @param ifExists whether attempt to drop the index should be made only if it exists.
+ */
+ public void ifExists(boolean ifExists) {
+ this.ifExists = ifExists;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getSQL() {
+ return "DROP INDEX " + (ifExists ? "IF EXISTS " : "") + Parser.quoteIdentifier(schemaName) + '.' +
+ Parser.quoteIdentifier(name);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
index 0f940e9..199a157 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
@@ -28,9 +28,16 @@ import java.util.List;
import java.util.Map;
import javax.cache.CacheException;
import org.apache.ignite.IgniteException;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.QueryIndexType;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.h2.command.Command;
import org.h2.command.CommandContainer;
import org.h2.command.Prepared;
+import org.h2.command.ddl.CreateIndex;
+import org.h2.command.ddl.DropIndex;
+import org.h2.command.ddl.SchemaCommand;
import org.h2.command.dml.Delete;
import org.h2.command.dml.Explain;
import org.h2.command.dml.Insert;
@@ -63,8 +70,10 @@ import org.h2.expression.ValueExpression;
import org.h2.index.ViewIndex;
import org.h2.jdbc.JdbcPreparedStatement;
import org.h2.result.SortOrder;
+import org.h2.schema.Schema;
import org.h2.table.Column;
import org.h2.table.FunctionTable;
+import org.h2.table.IndexColumn;
import org.h2.table.RangeTable;
import org.h2.table.Table;
import org.h2.table.TableBase;
@@ -302,6 +311,48 @@ public class GridSqlQueryParser {
GridSqlQueryParser.<Command, Prepared>getter(CommandContainer.class, "prepared");
/** */
+ private static final Getter<CreateIndex, String> CREATE_INDEX_NAME = getter(CreateIndex.class, "indexName");
+
+ /** */
+ private static final Getter<CreateIndex, String> CREATE_INDEX_TABLE_NAME = getter(CreateIndex.class, "tableName");
+
+ /** */
+ private static final Getter<CreateIndex, IndexColumn[]> CREATE_INDEX_COLUMNS = getter(CreateIndex.class,
+ "indexColumns");
+
+ /** */
+ private static final Getter<CreateIndex, Boolean> CREATE_INDEX_SPATIAL = getter(CreateIndex.class, "spatial");
+
+ /** */
+ private static final Getter<CreateIndex, Boolean> CREATE_INDEX_PRIMARY_KEY = getter(CreateIndex.class,
+ "primaryKey");
+
+ /** */
+ private static final Getter<CreateIndex, Boolean> CREATE_INDEX_UNIQUE = getter(CreateIndex.class, "unique");
+
+ /** */
+ private static final Getter<CreateIndex, Boolean> CREATE_INDEX_HASH = getter(CreateIndex.class, "hash");
+
+ /** */
+ private static final Getter<CreateIndex, Boolean> CREATE_INDEX_IF_NOT_EXISTS = getter(CreateIndex.class,
+ "ifNotExists");
+
+ /** */
+ private static final Getter<IndexColumn, String> INDEX_COLUMN_NAME = getter(IndexColumn.class, "columnName");
+
+ /** */
+ private static final Getter<IndexColumn, Integer> INDEX_COLUMN_SORT_TYPE = getter(IndexColumn.class, "sortType");
+
+ /** */
+ private static final Getter<DropIndex, String> DROP_INDEX_NAME = getter(DropIndex.class, "indexName");
+
+ /** */
+ private static final Getter<DropIndex, Boolean> DROP_INDEX_IF_EXISTS = getter(DropIndex.class, "ifExists");
+
+ /** */
+ private static final Getter<SchemaCommand, Schema> SCHEMA_COMMAND_SCHEMA = getter(SchemaCommand.class, "schema");
+
+ /** */
private final IdentityHashMap<Object, Object> h2ObjToGridObj = new IdentityHashMap<>();
/** */
@@ -659,6 +710,72 @@ public class GridSqlQueryParser {
return res;
}
+
+
+ /**
+ * Parse {@code DROP INDEX} statement.
+ *
+ * @param dropIdx {@code DROP INDEX} statement.
+ * @see <a href="http://h2database.com/html/grammar.html#drop_index">H2 {@code DROP INDEX} spec.</a>
+ */
+ private GridSqlDropIndex parseDropIndex(DropIndex dropIdx) {
+ GridSqlDropIndex res = new GridSqlDropIndex();
+
+ res.name(DROP_INDEX_NAME.get(dropIdx));
+ res.schemaName(SCHEMA_COMMAND_SCHEMA.get(dropIdx).getName());
+ res.ifExists(DROP_INDEX_IF_EXISTS.get(dropIdx));
+
+ return res;
+ }
+
+ /**
+ * Parse {@code CREATE INDEX} statement.
+ *
+ * @param createIdx {@code CREATE INDEX} statement.
+ * @see <a href="http://h2database.com/html/grammar.html#create_index">H2 {@code CREATE INDEX} spec.</a>
+ */
+ private GridSqlCreateIndex parseCreateIndex(CreateIndex createIdx) {
+ if (CREATE_INDEX_HASH.get(createIdx) || CREATE_INDEX_PRIMARY_KEY.get(createIdx) ||
+ CREATE_INDEX_UNIQUE.get(createIdx))
+ throw new IgniteSQLException("Only SPATIAL modifier is supported for CREATE INDEX",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+ GridSqlCreateIndex res = new GridSqlCreateIndex();
+
+ Schema schema = SCHEMA_COMMAND_SCHEMA.get(createIdx);
+
+ String tblName = CREATE_INDEX_TABLE_NAME.get(createIdx);
+
+ res.schemaName(schema.getName());
+ res.tableName(tblName);
+ res.ifNotExists(CREATE_INDEX_IF_NOT_EXISTS.get(createIdx));
+
+ QueryIndex idx = new QueryIndex();
+
+ idx.setName(CREATE_INDEX_NAME.get(createIdx));
+ idx.setIndexType(CREATE_INDEX_SPATIAL.get(createIdx) ? QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED);
+
+ IndexColumn[] cols = CREATE_INDEX_COLUMNS.get(createIdx);
+
+ LinkedHashMap<String, Boolean> flds = new LinkedHashMap<>(cols.length);
+
+ for (IndexColumn col : CREATE_INDEX_COLUMNS.get(createIdx)) {
+ int sortType = INDEX_COLUMN_SORT_TYPE.get(col);
+
+ if ((sortType & SortOrder.NULLS_FIRST) != 0 || (sortType & SortOrder.NULLS_LAST) != 0)
+ throw new IgniteSQLException("NULLS FIRST and NULLS LAST modifiers are not supported for index columns",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+ flds.put(INDEX_COLUMN_NAME.get(col), (sortType & SortOrder.DESCENDING) == 0);
+ }
+
+ idx.setFields(flds);
+
+ res.index(idx);
+
+ return res;
+ }
+
/**
* @param sortOrder Sort order.
* @param qry Query.
@@ -722,6 +839,12 @@ public class GridSqlQueryParser {
if (stmt instanceof Explain)
return parse(EXPLAIN_COMMAND.get((Explain)stmt)).explain(true);
+ if (stmt instanceof CreateIndex)
+ return parseCreateIndex((CreateIndex)stmt);
+
+ if (stmt instanceof DropIndex)
+ return parseDropIndex((DropIndex)stmt);
+
throw new CacheException("Unsupported SQL statement: " + stmt);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/AbstractSchemaSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/AbstractSchemaSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/AbstractSchemaSelfTest.java
new file mode 100644
index 0000000..a865b18
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/AbstractSchemaSelfTest.java
@@ -0,0 +1,512 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.QueryIndexType;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
+import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
+import org.apache.ignite.internal.processors.query.QueryIndexDescriptorImpl;
+import org.apache.ignite.internal.processors.query.QueryTypeDescriptorImpl;
+import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tests for dynamic schema changes.
+ */
+@SuppressWarnings("unchecked")
+public class AbstractSchemaSelfTest extends GridCommonAbstractTest {
+ /** Cache. */
+ protected static final String CACHE_NAME = "cache";
+
+ /** Table name. */
+ protected static final String TBL_NAME = tableName(ValueClass.class);
+
+ /** Table name 2. */
+ protected static final String TBL_NAME_2 = tableName(ValueClass2.class);
+
+ /** Index name 1. */
+ protected static final String IDX_NAME_1 = "idx_1";
+
+ /** Index name 2. */
+ protected static final String IDX_NAME_2 = "idx_2";
+
+ /** Index name 3. */
+ protected static final String IDX_NAME_3 = "idx_3";
+
+ /** Key ID field. */
+ protected static final String FIELD_KEY = "id";
+
+ /** Field 1. */
+ protected static final String FIELD_NAME_1 = "field1";
+
+ /** Field 1. */
+ protected static final String FIELD_NAME_2 = "field2";
+
+ /** Field 3. */
+ protected static final String FIELD_NAME_3 = "field3";
+
+ /**
+ * Get type on the given node for the given cache and table name. Type must exist.
+ *
+ * @param node Node.
+ * @param cacheName Cache name.
+ * @param tblName Table name.
+ * @return Type.
+ */
+ protected static QueryTypeDescriptorImpl typeExisting(IgniteEx node, String cacheName, String tblName) {
+ QueryTypeDescriptorImpl res = type(node, cacheName, tblName);
+
+ assertNotNull(res);
+
+ return res;
+ }
+
+ /**
+ * Get type on the given node for the given cache and table name.
+ *
+ * @param node Node.
+ * @param cacheName Cache name.
+ * @param tblName Table name.
+ * @return Type.
+ */
+ @Nullable protected static QueryTypeDescriptorImpl type(IgniteEx node, String cacheName, String tblName) {
+ return types(node, cacheName).get(tblName);
+ }
+
+ /**
+ * Get available types on the given node for the given cache.
+ *
+ * @param node Node.
+ * @param cacheName Cache name.
+ * @return Map from table name to type.
+ */
+ protected static Map<String, QueryTypeDescriptorImpl> types(IgniteEx node, String cacheName) {
+ Map<String, QueryTypeDescriptorImpl> res = new HashMap<>();
+
+ Collection<GridQueryTypeDescriptor> descs = node.context().query().types(cacheName);
+
+ for (GridQueryTypeDescriptor desc : descs) {
+ QueryTypeDescriptorImpl desc0 = (QueryTypeDescriptorImpl)desc;
+
+ res.put(desc0.tableName(), desc0);
+ }
+
+ return res;
+ }
+
+ /**
+ * Assert index state on all nodes.
+ *
+ * @param cacheName Cache name.
+ * @param tblName Table name.
+ * @param idxName Index name.
+ * @param fields Fields.
+ */
+ protected static void assertIndex(String cacheName, String tblName, String idxName,
+ IgniteBiTuple<String, Boolean>... fields) {
+ for (Ignite node : Ignition.allGrids())
+ assertIndex((IgniteEx)node, cacheName, tblName, idxName, fields);
+ }
+
+ /**
+ * Assert index state on particular node.
+ *
+ * @param node Node.
+ * @param cacheName Cache name.
+ * @param tblName Table name.
+ * @param idxName Index name.
+ * @param fields Fields.
+ */
+ protected static void assertIndex(IgniteEx node, String cacheName, String tblName, String idxName,
+ IgniteBiTuple<String, Boolean>... fields) {
+ assertIndexDescriptor(node, cacheName, tblName, idxName, fields);
+
+ if (affinityNode(node, cacheName)) {
+ QueryTypeDescriptorImpl typeDesc = typeExisting(node, cacheName, tblName);
+
+ assertIndex(typeDesc, idxName, fields);
+ }
+ }
+
+ /**
+ * Make sure index exists in cache descriptor.
+ *
+ * @param node Node.
+ * @param cacheName Cache name.
+ * @param tblName Table name.
+ * @param idxName Index name.
+ * @param fields Fields.
+ */
+ protected static void assertIndexDescriptor(IgniteEx node, String cacheName, String tblName, String idxName,
+ IgniteBiTuple<String, Boolean>... fields) {
+ awaitCompletion();
+
+ DynamicCacheDescriptor desc = node.context().cache().cacheDescriptor(cacheName);
+
+ assert desc != null;
+
+ for (QueryEntity entity : desc.schema().entities()) {
+ if (F.eq(tblName, QueryUtils.tableName(entity))) {
+ for (QueryIndex idx : entity.getIndexes()) {
+ if (F.eq(QueryUtils.indexName(entity, idx), idxName)) {
+ LinkedHashMap<String, Boolean> idxFields = idx.getFields();
+
+ assertEquals(idxFields.size(), fields.length);
+
+ int i = 0;
+
+ for (String idxField : idxFields.keySet()) {
+ assertEquals(idxField, fields[i].get1());
+ assertEquals(idxFields.get(idxField), fields[i].get2());
+
+ i++;
+ }
+
+ return;
+ }
+ }
+ }
+ }
+
+ fail("Index not found [cacheName=" + cacheName + ", tlbName=" + tblName + ", idxName=" + idxName + ']');
+ }
+
+ /**
+ * Assert index state.
+ *
+ * @param typeDesc Type descriptor.
+ * @param idxName Index name.
+ * @param fields Fields (order is important).
+ */
+ protected static void assertIndex(QueryTypeDescriptorImpl typeDesc, String idxName,
+ IgniteBiTuple<String, Boolean>... fields) {
+ QueryIndexDescriptorImpl idxDesc = typeDesc.index(idxName);
+
+ assertNotNull(idxDesc);
+
+ assertEquals(idxName, idxDesc.name());
+ assertEquals(typeDesc, idxDesc.typeDescriptor());
+ assertEquals(QueryIndexType.SORTED, idxDesc.type());
+
+ List<String> fieldNames = new ArrayList<>(idxDesc.fields());
+
+ assertEquals(fields.length, fieldNames.size());
+
+ for (int i = 0; i < fields.length; i++) {
+ String expFieldName = fields[i].get1();
+ boolean expFieldAsc = fields[i].get2();
+
+ assertEquals("Index field mismatch [pos=" + i + ", expField=" + expFieldName +
+ ", actualField=" + fieldNames.get(i) + ']', expFieldName, fieldNames.get(i));
+
+ boolean fieldAsc = !idxDesc.descending(expFieldName);
+
+ assertEquals("Index field sort mismatch [pos=" + i + ", field=" + expFieldName +
+ ", expAsc=" + expFieldAsc + ", actualAsc=" + fieldAsc + ']', expFieldAsc, fieldAsc);
+ }
+ }
+
+ /**
+ * Assert index doesn't exist on all nodes.
+ *
+ * @param cacheName Cache name.
+ * @param tblName Table name.
+ * @param idxName Index name.
+ */
+ protected static void assertNoIndex(String cacheName, String tblName, String idxName) {
+ for (Ignite node : Ignition.allGrids())
+ assertNoIndex((IgniteEx)node, cacheName, tblName, idxName);
+ }
+
+ /**
+ * Assert index doesn't exist on particular node.
+ *
+ * @param node Node.
+ * @param cacheName Cache name.
+ * @param tblName Table name.
+ * @param idxName Index name.
+ */
+ protected static void assertNoIndex(IgniteEx node, String cacheName, String tblName, String idxName) {
+ assertNoIndexDescriptor(node, cacheName, tblName, idxName);
+
+ if (affinityNode(node, cacheName)) {
+ QueryTypeDescriptorImpl typeDesc = typeExisting(node, cacheName, tblName);
+
+ assertNoIndex(typeDesc, idxName);
+ }
+ }
+
+ /**
+ * Assert index doesn't exist in particular node's cache descriptor.
+ *
+ * @param node Node.
+ * @param cacheName Cache name.
+ * @param tblName Table name.
+ * @param idxName Index name.
+ */
+ protected static void assertNoIndexDescriptor(IgniteEx node, String cacheName, String tblName, String idxName) {
+ awaitCompletion();
+
+ DynamicCacheDescriptor desc = node.context().cache().cacheDescriptor(cacheName);
+
+ if (desc == null)
+ return;
+
+ for (QueryEntity entity : desc.schema().entities()) {
+ for (QueryIndex idx : entity.getIndexes()) {
+ if (F.eq(idxName, QueryUtils.indexName(entity, idx)))
+ fail("Index exists: " + idxName);
+ }
+ }
+ }
+
+ /**
+ * Await completion (hopefully) of pending operations.
+ */
+ private static void awaitCompletion() {
+ try {
+ U.sleep(100);
+ }
+ catch (IgniteInterruptedCheckedException e) {
+ fail();
+ }
+ }
+
+ /**
+ * Assert index doesn't exist.
+ *
+ * @param typeDesc Type descriptor.
+ * @param idxName Index name.
+ */
+ protected static void assertNoIndex(QueryTypeDescriptorImpl typeDesc, String idxName) {
+ assertNull(typeDesc.index(idxName));
+ }
+
+ /**
+ * Check whether this is affinity node for cache.
+ *
+ * @param node Node.
+ * @param cacheName Cache name.
+ * @return {@code True} if affinity node.
+ */
+ private static boolean affinityNode(IgniteEx node, String cacheName) {
+ if (node.configuration().isClientMode())
+ return false;
+
+ DynamicCacheDescriptor cacheDesc = node.context().cache().cacheDescriptor(cacheName);
+
+ IgnitePredicate<ClusterNode> filter = cacheDesc.cacheConfiguration().getNodeFilter();
+
+ return filter == null || filter.apply(node.localNode());
+ }
+
+ /**
+ * Get table name for class.
+ *
+ * @param cls Class.
+ * @return Table name.
+ */
+ protected static String tableName(Class cls) {
+ return cls.getSimpleName();
+ }
+
+ /**
+ * Convenient method for index creation.
+ *
+ * @param name Name.
+ * @param fields Fields.
+ * @return Index.
+ */
+ protected static QueryIndex index(String name, IgniteBiTuple<String, Boolean>... fields) {
+ QueryIndex idx = new QueryIndex();
+
+ idx.setName(name);
+
+ LinkedHashMap<String, Boolean> fields0 = new LinkedHashMap<>();
+
+ for (IgniteBiTuple<String, Boolean> field : fields)
+ fields0.put(field.getKey(), field.getValue());
+
+ idx.setFields(fields0);
+
+ return idx;
+ }
+
+ /**
+ * Get query processor.
+ *
+ * @param node Node.
+ * @return Query processor.
+ */
+ protected static GridQueryProcessor queryProcessor(Ignite node) {
+ return ((IgniteEx)node).context().query();
+ }
+
+ /**
+ * Field for index state check (ascending).
+ *
+ * @param name Name.
+ * @return Field.
+ */
+ protected static IgniteBiTuple<String, Boolean> field(String name) {
+ return field(name, true);
+ }
+
+ /**
+ * Field for index state check.
+ *
+ * @param name Name.
+ * @param asc Ascending flag.
+ * @return Field.
+ */
+ protected static IgniteBiTuple<String, Boolean> field(String name, boolean asc) {
+ return F.t(name, asc);
+ }
+
+ /**
+ * @param fieldName Field name.
+ * @return Alias.
+ */
+ protected static String alias(String fieldName) {
+ return fieldName + "_alias";
+ }
+
+ /**
+ * Key class.
+ */
+ public static class KeyClass {
+ /** ID. */
+ @QuerySqlField
+ private long id;
+
+ /**
+ * Constructor.
+ *
+ * @param id ID.
+ */
+ public KeyClass(long id) {
+ this.id = id;
+ }
+
+ /**
+ * @return ID.
+ */
+ public long id() {
+ return id;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ KeyClass keyClass = (KeyClass) o;
+
+ return id == keyClass.id;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return (int) (id ^ (id >>> 32));
+ }
+ }
+
+ /**
+ * Key class.
+ */
+ public static class ValueClass {
+ /** Field 1. */
+ @QuerySqlField
+ private String field1;
+
+ /**
+ * Constructor.
+ *
+ * @param field1 Field 1.
+ */
+ public ValueClass(String field1) {
+ this.field1 = field1;
+ }
+
+ /**
+ * @return Field 1
+ */
+ public String field1() {
+ return field1;
+ }
+ }
+
+ /**
+ * Key class.
+ */
+ public static class ValueClass2 {
+ /** Field 1. */
+ @QuerySqlField(name = "field1")
+ private String field;
+
+ /**
+ * Constructor.
+ *
+ * @param field Field 1.
+ */
+ public ValueClass2(String field) {
+ this.field = field;
+ }
+
+ /**
+ * @return Field 1
+ */
+ public String field() {
+ return field;
+ }
+ }
+
+ /**
+ * Runnable which can throw checked exceptions.
+ */
+ protected interface RunnableX {
+ /**
+ * Do run.
+ *
+ * @throws Exception If failed.
+ */
+ public void run() throws Exception;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractBasicSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractBasicSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractBasicSelfTest.java
new file mode 100644
index 0000000..6bc1576
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractBasicSelfTest.java
@@ -0,0 +1,950 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.index;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.NearCacheConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.LOCAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+
+/**
+ * Tests for dynamic index creation.
+ */
+@SuppressWarnings({"unchecked", "ThrowableResultOfMethodCallIgnored"})
+public abstract class DynamicIndexAbstractBasicSelfTest extends DynamicIndexAbstractSelfTest {
+ /** Node index for regular server (coordinator). */
+ protected static final int IDX_SRV_CRD = 0;
+
+ /** Node index for regular server (not coordinator). */
+ protected static final int IDX_SRV_NON_CRD = 1;
+
+ /** Node index for regular client. */
+ protected static final int IDX_CLI = 2;
+
+ /** Node index for server which doesn't pass node filter. */
+ protected static final int IDX_SRV_FILTERED = 3;
+
+ /** Node index for client with near-only cache. */
+ protected static final int IDX_CLI_NEAR_ONLY = 4;
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ super.beforeTestsStarted();
+
+ for (IgniteConfiguration cfg : configurations())
+ Ignition.start(cfg);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ node().destroyCache(CACHE_NAME);
+
+ super.afterTest();
+ }
+
+ /**
+ * Initialize cache for tests.
+ *
+ * @param mode Mode.
+ * @param atomicityMode Atomicity mode.
+ * @param near Near flag.
+ */
+ private void initialize(CacheMode mode, CacheAtomicityMode atomicityMode, boolean near) {
+ node().getOrCreateCache(cacheConfiguration(mode, atomicityMode, near));
+
+ grid(IDX_CLI_NEAR_ONLY).getOrCreateNearCache(CACHE_NAME, new NearCacheConfiguration<>());
+
+ assertNoIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1);
+
+ loadInitialData();
+ }
+
+ /**
+ * Create cache with the given cache mode and atomicity mode.
+ *
+ * @param mode Mode.
+ * @param atomicityMode Atomicity mode.
+ * @param near Whether near cache should be initialized.
+ * @return Cache configuration.
+ */
+ private CacheConfiguration<KeyClass, ValueClass> cacheConfiguration(CacheMode mode,
+ CacheAtomicityMode atomicityMode, boolean near) {
+ CacheConfiguration<KeyClass, ValueClass> ccfg = cacheConfiguration();
+
+ ccfg.setCacheMode(mode);
+ ccfg.setAtomicityMode(atomicityMode);
+
+ if (near)
+ ccfg.setNearConfiguration(new NearCacheConfiguration<KeyClass, ValueClass>());
+
+ return ccfg;
+ }
+
+ /**
+ * Load initial data.
+ */
+ private void loadInitialData() {
+ put(node(), 0, KEY_BEFORE);
+ }
+
+ /**
+ * Test simple index create for PARTITIONED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreatePartitionedAtomic() throws Exception {
+ checkCreate(PARTITIONED, ATOMIC, false);
+ }
+
+ /**
+ * Test simple index create for PARTITIONED ATOMIC cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreatePartitionedAtomicNear() throws Exception {
+ checkCreate(PARTITIONED, ATOMIC, true);
+ }
+
+ /**
+ * Test simple index create for PARTITIONED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreatePartitionedTransactional() throws Exception {
+ checkCreate(PARTITIONED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Test simple index create for PARTITIONED TRANSACTIONAL cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreatePartitionedTransactionalNear() throws Exception {
+ checkCreate(PARTITIONED, TRANSACTIONAL, true);
+ }
+
+ /**
+ * Test simple index create for REPLICATED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateReplicatedAtomic() throws Exception {
+ checkCreate(REPLICATED, ATOMIC, false);
+ }
+
+ /**
+ * Test simple index create for REPLICATED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateReplicatedTransactional() throws Exception {
+ checkCreate(REPLICATED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Check normal create operation.
+ *
+ * @param mode Mode.
+ * @param atomicityMode Atomicity mode.
+ * @param near Near flag.
+ * @throws Exception If failed.
+ */
+ private void checkCreate(CacheMode mode, CacheAtomicityMode atomicityMode, boolean near) throws Exception {
+ initialize(mode, atomicityMode, near);
+
+ final IgniteEx node = node();
+
+ final QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ queryProcessor(node).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, false).get();
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(FIELD_NAME_1));
+
+ assertSchemaException(new RunnableX() {
+ @Override public void run() throws Exception {
+ queryProcessor(node).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, false).get();
+ }
+ }, SchemaOperationException.CODE_INDEX_EXISTS);
+
+ queryProcessor(node).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, true).get();
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(FIELD_NAME_1));
+
+ assertSimpleIndexOperations(SQL_SIMPLE_FIELD_1);
+
+ assertIndexUsed(IDX_NAME_1, SQL_SIMPLE_FIELD_1, SQL_ARG_1);
+ }
+
+ /**
+ * Test composite index creation for PARTITIONED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateCompositePartitionedAtomic() throws Exception {
+ checkCreateComposite(PARTITIONED, ATOMIC, false);
+ }
+
+ /**
+ * Test composite index creation for PARTITIONED ATOMIC cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateCompositePartitionedAtomicNear() throws Exception {
+ checkCreateComposite(PARTITIONED, ATOMIC, true);
+ }
+
+ /**
+ * Test composite index creation for PARTITIONED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateCompositePartitionedTransactional() throws Exception {
+ checkCreateComposite(PARTITIONED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Test composite index creation for PARTITIONED TRANSACTIONAL cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateCompositePartitionedTransactionalNear() throws Exception {
+ checkCreateComposite(PARTITIONED, TRANSACTIONAL, true);
+ }
+
+ /**
+ * Test composite index creation for REPLICATED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateCompositeReplicatedAtomic() throws Exception {
+ checkCreateComposite(REPLICATED, ATOMIC, false);
+ }
+
+ /**
+ * Test composite index creation for REPLICATED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateCompositeReplicatedTransactional() throws Exception {
+ checkCreateComposite(REPLICATED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Check composite index creation.
+ *
+ * @param mode Mode.
+ * @param atomicityMode Atomicity mode.
+ * @param near Near flag.
+ * @throws Exception If failed.
+ */
+ private void checkCreateComposite(CacheMode mode, CacheAtomicityMode atomicityMode, boolean near) throws Exception {
+ initialize(mode, atomicityMode, near);
+
+ final QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1), field(alias(FIELD_NAME_2)));
+
+ queryProcessor(node()).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, false).get();
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(FIELD_NAME_1), field(alias(FIELD_NAME_2)));
+
+ assertCompositeIndexOperations(SQL_COMPOSITE);
+
+ assertIndexUsed(IDX_NAME_1, SQL_COMPOSITE, SQL_ARG_1, SQL_ARG_2);
+ }
+
+ /**
+ * Test create when cache doesn't exist for PARTITIONED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoCachePartitionedAtomic() throws Exception {
+ checkCreateNotCache(PARTITIONED, ATOMIC, false);
+ }
+
+ /**
+ * Test create when cache doesn't exist for PARTITIONED ATOMIC cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoCachePartitionedAtomicNear() throws Exception {
+ checkCreateNotCache(PARTITIONED, ATOMIC, true);
+ }
+
+ /**
+ * Test create when cache doesn't exist for PARTITIONED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoCachePartitionedTransactional() throws Exception {
+ checkCreateNotCache(PARTITIONED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Test create when cache doesn't exist for PARTITIONED TRANSACTIONAL cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoCachePartitionedTransactionalNear() throws Exception {
+ checkCreateNotCache(PARTITIONED, TRANSACTIONAL, true);
+ }
+
+ /**
+ * Test create when cache doesn't exist for REPLICATED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoCacheReplicatedAtomic() throws Exception {
+ checkCreateNotCache(REPLICATED, ATOMIC, false);
+ }
+
+ /**
+ * Test create when cache doesn't exist for REPLICATED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoCacheReplicatedTransactional() throws Exception {
+ checkCreateNotCache(REPLICATED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Check create when cache doesn't exist.
+ *
+ * @param mode Mode.
+ * @param atomicityMode Atomicity mode.
+ * @param near Near flag.
+ * @throws Exception If failed.
+ */
+ private void checkCreateNotCache(CacheMode mode, CacheAtomicityMode atomicityMode, boolean near) throws Exception {
+ initialize(mode, atomicityMode, near);
+
+ final QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ assertSchemaException(new RunnableX() {
+ @Override public void run() throws Exception {
+ queryProcessor(node()).dynamicIndexCreate(randomString(), TBL_NAME, idx, false).get();
+ }
+ }, SchemaOperationException.CODE_CACHE_NOT_FOUND);
+
+ assertNoIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1);
+ }
+
+ /**
+ * Test create when table doesn't exist for PARTITIONED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoTablePartitionedAtomic() throws Exception {
+ checkCreateNoTable(PARTITIONED, ATOMIC, false);
+ }
+
+ /**
+ * Test create when table doesn't exist for PARTITIONED ATOMIC cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoTablePartitionedAtomicNear() throws Exception {
+ checkCreateNoTable(PARTITIONED, ATOMIC, true);
+ }
+
+ /**
+ * Test create when table doesn't exist for PARTITIONED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoTablePartitionedTransactional() throws Exception {
+ checkCreateNoTable(PARTITIONED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Test create when table doesn't exist for PARTITIONED TRANSACTIONAL cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoTablePartitionedTransactionalNear() throws Exception {
+ checkCreateNoTable(PARTITIONED, TRANSACTIONAL, true);
+ }
+
+ /**
+ * Test create when table doesn't exist for REPLICATED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoTableReplicatedAtomic() throws Exception {
+ checkCreateNoTable(REPLICATED, ATOMIC, false);
+ }
+
+ /**
+ * Test create when table doesn't exist for REPLICATED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoTableReplicatedTransactional() throws Exception {
+ checkCreateNoTable(REPLICATED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Check create when table doesn't exist.
+ *
+ * @param mode Mode.
+ * @param atomicityMode Atomicity mode.
+ * @param near Near flag.
+ * @throws Exception If failed.
+ */
+ private void checkCreateNoTable(CacheMode mode, CacheAtomicityMode atomicityMode, boolean near) throws Exception {
+ initialize(mode, atomicityMode, near);
+
+ final QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ assertSchemaException(new RunnableX() {
+ @Override public void run() throws Exception {
+ queryProcessor(node()).dynamicIndexCreate(CACHE_NAME, randomString(), idx, false).get();
+ }
+ }, SchemaOperationException.CODE_TABLE_NOT_FOUND);
+
+ assertNoIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1);
+ }
+
+ /**
+ * Test create when table doesn't exist for PARTITIONED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoColumnPartitionedAtomic() throws Exception {
+ checkCreateNoColumn(PARTITIONED, ATOMIC, false);
+ }
+
+ /**
+ * Test create when table doesn't exist for PARTITIONED ATOMIC cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoColumnPartitionedAtomicNear() throws Exception {
+ checkCreateNoColumn(PARTITIONED, ATOMIC, true);
+ }
+
+ /**
+ * Test create when table doesn't exist for PARTITIONED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoColumnPartitionedTransactional() throws Exception {
+ checkCreateNoColumn(PARTITIONED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Test create when table doesn't exist for PARTITIONED TRANSACTIONAL cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoColumnPartitionedTransactionalNear() throws Exception {
+ checkCreateNoColumn(PARTITIONED, TRANSACTIONAL, true);
+ }
+
+ /**
+ * Test create when table doesn't exist for REPLICATED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoColumnReplicatedAtomic() throws Exception {
+ checkCreateNoColumn(REPLICATED, ATOMIC, false);
+ }
+
+ /**
+ * Test create when table doesn't exist for REPLICATED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateNoColumnReplicatedTransactional() throws Exception {
+ checkCreateNoColumn(REPLICATED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Check create when table doesn't exist.
+ *
+ * @param mode Mode.
+ * @param atomicityMode Atomicity mode.
+ * @param near Near flag.
+ * @throws Exception If failed.
+ */
+ private void checkCreateNoColumn(CacheMode mode, CacheAtomicityMode atomicityMode, boolean near) throws Exception {
+ initialize(mode, atomicityMode, near);
+
+ final QueryIndex idx = index(IDX_NAME_1, field(randomString()));
+
+ assertSchemaException(new RunnableX() {
+ @Override public void run() throws Exception {
+ queryProcessor(node()).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, false).get();
+ }
+ }, SchemaOperationException.CODE_COLUMN_NOT_FOUND);
+
+ assertNoIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1);
+ }
+
+ /**
+ * Test index creation on aliased column for PARTITIONED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateColumnWithAliasPartitionedAtomic() throws Exception {
+ checkCreateColumnWithAlias(PARTITIONED, ATOMIC, false);
+ }
+
+ /**
+ * Test index creation on aliased column for PARTITIONED ATOMIC cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateColumnWithAliasPartitionedAtomicNear() throws Exception {
+ checkCreateColumnWithAlias(PARTITIONED, ATOMIC, true);
+ }
+
+ /**
+ * Test index creation on aliased column for PARTITIONED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateColumnWithAliasPartitionedTransactional() throws Exception {
+ checkCreateColumnWithAlias(PARTITIONED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Test index creation on aliased column for PARTITIONED TRANSACTIONAL cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateColumnWithAliasPartitionedTransactionalNear() throws Exception {
+ checkCreateColumnWithAlias(PARTITIONED, TRANSACTIONAL, true);
+ }
+
+ /**
+ * Test index creation on aliased column for REPLICATED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateColumnWithAliasReplicatedAtomic() throws Exception {
+ checkCreateColumnWithAlias(REPLICATED, ATOMIC, false);
+ }
+
+ /**
+ * Test index creation on aliased column for REPLICATED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testCreateColumnWithAliasReplicatedTransactional() throws Exception {
+ checkCreateColumnWithAlias(REPLICATED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Check index creation on aliased column.
+ *
+ * @param mode Mode.
+ * @param atomicityMode Atomicity mode.
+ * @param near Near flag.
+ * @throws Exception If failed.
+ */
+ private void checkCreateColumnWithAlias(CacheMode mode, CacheAtomicityMode atomicityMode, boolean near)
+ throws Exception {
+ initialize(mode, atomicityMode, near);
+
+ assertSchemaException(new RunnableX() {
+ @Override public void run() throws Exception {
+ QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_2));
+
+ queryProcessor(node()).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, false).get();
+ }
+ }, SchemaOperationException.CODE_COLUMN_NOT_FOUND);
+
+ assertNoIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1);
+
+ QueryIndex idx = index(IDX_NAME_1, field(alias(FIELD_NAME_2)));
+
+ queryProcessor(node()).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, false).get();
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(alias(FIELD_NAME_2)));
+
+ assertSimpleIndexOperations(SQL_SIMPLE_FIELD_2);
+
+ assertIndexUsed(IDX_NAME_1, SQL_SIMPLE_FIELD_2, SQL_ARG_1);
+ }
+
+ /**
+ * Test simple index drop for PARTITIONED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropPartitionedAtomic() throws Exception {
+ checkDrop(PARTITIONED, ATOMIC, false);
+ }
+
+ /**
+ * Test simple index drop for PARTITIONED ATOMIC cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropPartitionedAtomicNear() throws Exception {
+ checkDrop(PARTITIONED, ATOMIC, true);
+ }
+
+ /**
+ * Test simple index drop for PARTITIONED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropPartitionedTransactional() throws Exception {
+ checkDrop(PARTITIONED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Test simple index drop for PARTITIONED TRANSACTIONAL cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropPartitionedTransactionalNear() throws Exception {
+ checkDrop(PARTITIONED, TRANSACTIONAL, true);
+ }
+
+ /**
+ * Test simple index drop for REPLICATED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropReplicatedAtomic() throws Exception {
+ checkDrop(REPLICATED, ATOMIC, false);
+ }
+
+ /**
+ * Test simple index drop for REPLICATED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropReplicatedTransactional() throws Exception {
+ checkDrop(REPLICATED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Check simple index drop.
+ *
+ * @param mode Mode.
+ * @param atomicityMode Atomicity mode.
+ * @param near Near flag.
+ * @throws Exception If failed.
+ */
+ public void checkDrop(CacheMode mode, CacheAtomicityMode atomicityMode, boolean near) throws Exception {
+ initialize(mode, atomicityMode, near);
+
+ QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ queryProcessor(node()).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, false).get();
+ assertIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1, field(FIELD_NAME_1));
+
+ assertIndexUsed(IDX_NAME_1, SQL_SIMPLE_FIELD_1, SQL_ARG_1);
+
+ assertSimpleIndexOperations(SQL_SIMPLE_FIELD_1);
+
+ loadInitialData();
+
+ queryProcessor(node()).dynamicIndexDrop(CACHE_NAME, IDX_NAME_1, false).get();
+ assertNoIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1);
+
+ assertSimpleIndexOperations(SQL_SIMPLE_FIELD_1);
+
+ assertIndexNotUsed(IDX_NAME_1, SQL_SIMPLE_FIELD_1, SQL_ARG_1);
+ }
+
+ /**
+ * Test drop when there is no index for PARTITIONED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropNoIndexPartitionedAtomic() throws Exception {
+ checkDropNoIndex(PARTITIONED, ATOMIC, false);
+ }
+
+ /**
+ * Test drop when there is no index for PARTITIONED ATOMIC cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropNoIndexPartitionedAtomicNear() throws Exception {
+ checkDropNoIndex(PARTITIONED, ATOMIC, true);
+ }
+
+ /**
+ * Test drop when there is no index for PARTITIONED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropNoIndexPartitionedTransactional() throws Exception {
+ checkDropNoIndex(PARTITIONED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Test drop when there is no index for PARTITIONED TRANSACTIONAL cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropNoIndexPartitionedTransactionalNear() throws Exception {
+ checkDropNoIndex(PARTITIONED, TRANSACTIONAL, true);
+ }
+
+ /**
+ * Test drop when there is no index for REPLICATED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropNoIndexReplicatedAtomic() throws Exception {
+ checkDropNoIndex(REPLICATED, ATOMIC, false);
+ }
+
+ /**
+ * Test drop when there is no index for REPLICATED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropNoIndexReplicatedTransactional() throws Exception {
+ checkDropNoIndex(REPLICATED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Check drop when there is no index.
+ *
+ * @param mode Mode.
+ * @param atomicityMode Atomicity mode.
+ * @param near Near flag.
+ * @throws Exception If failed.
+ */
+ private void checkDropNoIndex(CacheMode mode, CacheAtomicityMode atomicityMode, boolean near) throws Exception {
+ initialize(mode, atomicityMode, near);
+
+ assertSchemaException(new RunnableX() {
+ @Override public void run() throws Exception {
+ queryProcessor(node()).dynamicIndexDrop(CACHE_NAME, IDX_NAME_1, false).get();
+ }
+ }, SchemaOperationException.CODE_INDEX_NOT_FOUND);
+
+ queryProcessor(node()).dynamicIndexDrop(CACHE_NAME, IDX_NAME_1, true).get();
+ assertNoIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1);
+ }
+
+ /**
+ * Test drop when cache doesn't exist for PARTITIONED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropNoCachePartitionedAtomic() throws Exception {
+ checkDropNoCache(PARTITIONED, ATOMIC, false);
+ }
+
+ /**
+ * Test drop when cache doesn't exist for PARTITIONED ATOMIC cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropNoCachePartitionedAtomicNear() throws Exception {
+ checkDropNoCache(PARTITIONED, ATOMIC, true);
+ }
+
+ /**
+ * Test drop when cache doesn't exist for PARTITIONED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropNoCachePartitionedTransactional() throws Exception {
+ checkDropNoCache(PARTITIONED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Test drop when cache doesn't exist for PARTITIONED TRANSACTIONAL cache with near cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropNoCachePartitionedTransactionalNear() throws Exception {
+ checkDropNoCache(PARTITIONED, TRANSACTIONAL, true);
+ }
+
+ /**
+ * Test drop when cache doesn't exist for REPLICATED ATOMIC cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropNoCacheReplicatedAtomic() throws Exception {
+ checkDropNoCache(REPLICATED, ATOMIC, false);
+ }
+
+ /**
+ * Test drop when cache doesn't exist for REPLICATED TRANSACTIONAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testDropNoCacheReplicatedTransactional() throws Exception {
+ checkDropNoCache(REPLICATED, TRANSACTIONAL, false);
+ }
+
+ /**
+ * Check drop when cache doesn't exist.
+ *
+ * Check drop when there is no index.
+ *
+ * @param mode Mode.
+ * @param atomicityMode Atomicity mode.
+ * @param near Near flag.
+ * @throws Exception If failed.
+ */
+ private void checkDropNoCache(CacheMode mode, CacheAtomicityMode atomicityMode, boolean near) throws Exception {
+ initialize(mode, atomicityMode, near);
+
+ assertSchemaException(new RunnableX() {
+ @Override public void run() throws Exception {
+ queryProcessor(node()).dynamicIndexDrop(randomString(), "my_idx", false).get();
+ }
+ }, SchemaOperationException.CODE_CACHE_NOT_FOUND);
+
+ assertNoIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1);
+ }
+
+ /**
+ * Test that operations fail on LOCAL cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testFailOnLocalCache() throws Exception {
+ for (Ignite node : Ignition.allGrids()) {
+ if (!node.configuration().isClientMode())
+ node.getOrCreateCache(cacheConfiguration().setCacheMode(LOCAL));
+ }
+
+ final QueryIndex idx = index(IDX_NAME_1, field(FIELD_NAME_1));
+
+ assertSchemaException(new RunnableX() {
+ @Override public void run() throws Exception {
+ queryProcessor(node()).dynamicIndexCreate(CACHE_NAME, TBL_NAME, idx, true).get();
+ }
+ }, SchemaOperationException.CODE_GENERIC);
+
+ assertNoIndex(CACHE_NAME, TBL_NAME, IDX_NAME_1);
+
+ assertSchemaException(new RunnableX() {
+ @Override public void run() throws Exception {
+ queryProcessor(node()).dynamicIndexDrop(CACHE_NAME, IDX_NAME_1, true).get();
+ }
+ }, SchemaOperationException.CODE_GENERIC);
+ }
+
+ /**
+ * Get node which should be used to start operations.
+ *
+ * @return If failed.
+ */
+ protected IgniteEx node() {
+ return grid(nodeIndex());
+ }
+
+ /**
+ * Get index of the node which should be used to start operations.
+ *
+ * @return If failed.
+ */
+ protected abstract int nodeIndex();
+
+ /**
+ * Get configurations to be used in test.
+ *
+ * @return Configurations.
+ * @throws Exception If failed.
+ */
+ protected List<IgniteConfiguration> configurations() throws Exception {
+ return Arrays.asList(
+ serverCoordinatorConfiguration(IDX_SRV_CRD),
+ serverConfiguration(IDX_SRV_NON_CRD),
+ clientConfiguration(IDX_CLI),
+ serverConfiguration(IDX_SRV_FILTERED, true),
+ clientConfiguration(IDX_CLI_NEAR_ONLY)
+ );
+ }
+
+ /**
+ * Get server coordinator configuration.
+ *
+ * @param idx Index.
+ * @return Configuration.
+ * @throws Exception If failed.
+ */
+ protected IgniteConfiguration serverCoordinatorConfiguration(int idx) throws Exception {
+ return serverConfiguration(idx);
+ }
+
+ /**
+ * Assert FIELD_1 index usage.
+ *
+ * @param sql Simple SQL.
+ */
+ private void assertSimpleIndexOperations(String sql) {
+ for (Ignite node : Ignition.allGrids())
+ assertSqlSimpleData(node, sql, KEY_BEFORE - SQL_ARG_1);
+
+ put(node(), KEY_BEFORE, KEY_AFTER);
+
+ for (Ignite node : Ignition.allGrids())
+ assertSqlSimpleData(node, sql, KEY_AFTER - SQL_ARG_1);
+
+ remove(node(), 0, KEY_BEFORE);
+
+ for (Ignite node : Ignition.allGrids())
+ assertSqlSimpleData(node, sql, KEY_AFTER - KEY_BEFORE);
+
+ remove(node(), KEY_BEFORE, KEY_AFTER);
+
+ for (Ignite node : Ignition.allGrids())
+ assertSqlSimpleData(node, sql, 0);
+ }
+
+ /**
+ * Assert composite index usage.
+ *
+ * @param sql Simple SQL.
+ */
+ private void assertCompositeIndexOperations(String sql) {
+ for (Ignite node : Ignition.allGrids())
+ assertSqlCompositeData(node, sql, KEY_BEFORE - SQL_ARG_2);
+
+ put(node(), KEY_BEFORE, KEY_AFTER);
+
+ for (Ignite node : Ignition.allGrids())
+ assertSqlCompositeData(node, sql, KEY_AFTER - SQL_ARG_2);
+
+ remove(node(), 0, KEY_BEFORE);
+
+ for (Ignite node : Ignition.allGrids())
+ assertSqlCompositeData(node, sql, KEY_AFTER - KEY_BEFORE);
+
+ remove(node(), KEY_BEFORE, KEY_AFTER);
+
+ for (Ignite node : Ignition.allGrids())
+ assertSqlCompositeData(node, sql, 0);
+ }
+}
[4/8] ignite git commit: IGNITE-4565: Implemented CREATE INDEX and
DROP INDEX. This closes #1773. This closes #1804.
Posted by vo...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaIndexDropOperation.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaIndexDropOperation.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaIndexDropOperation.java
new file mode 100644
index 0000000..da60560
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/operation/SchemaIndexDropOperation.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.schema.operation;
+
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.util.UUID;
+
+/**
+ * Schema index drop operation.
+ */
+public class SchemaIndexDropOperation extends SchemaIndexAbstractOperation {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Index name. */
+ private final String idxName;
+
+ /** Ignore operation if index doesn't exist. */
+ private final boolean ifExists;
+
+ /**
+ * Constructor.
+ *
+ * @param opId Operation id.
+ * @param space Space.
+ * @param idxName Index name.
+ * @param ifExists Ignore operation if index doesn't exist.
+ */
+ public SchemaIndexDropOperation(UUID opId, String space, String idxName, boolean ifExists) {
+ super(opId, space);
+
+ this.idxName = idxName;
+ this.ifExists = ifExists;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String indexName() {
+ return idxName;
+ }
+
+ /**
+ * @return Ignore operation if index doesn't exist.
+ */
+ public boolean ifExists() {
+ return ifExists;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaIndexDropOperation.class, this, "parent", super.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java
index 1545b8c..a060f7e 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java
@@ -32,6 +32,7 @@ import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersionedEntryEx;
import org.apache.ignite.internal.processors.dr.GridDrType;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
import org.apache.ignite.internal.util.lang.GridMetadataAwareAdapter;
import org.apache.ignite.internal.util.lang.GridTuple3;
import org.jetbrains.annotations.Nullable;
@@ -853,6 +854,12 @@ public class GridCacheTestEntryEx extends GridMetadataAwareAdapter implements Gr
}
/** {@inheritDoc} */
+ @Override public void updateIndex(SchemaIndexCacheVisitorClosure clo, long link) throws IgniteCheckedException,
+ GridCacheEntryRemovedException {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
@Override public boolean deleted() {
return false;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestKernalContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestKernalContext.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestKernalContext.java
index 77d0ea7..ff67b77 100644
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestKernalContext.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestKernalContext.java
@@ -66,6 +66,7 @@ public class GridTestKernalContext extends GridKernalContextImpl {
null,
null,
null,
+ null,
U.allPluginProviders()
);
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/geospatial/src/test/java/org/apache/ignite/internal/processors/query/h2/GridH2IndexingGeoSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/geospatial/src/test/java/org/apache/ignite/internal/processors/query/h2/GridH2IndexingGeoSelfTest.java b/modules/geospatial/src/test/java/org/apache/ignite/internal/processors/query/h2/GridH2IndexingGeoSelfTest.java
index b53387f..4404e9c 100644
--- a/modules/geospatial/src/test/java/org/apache/ignite/internal/processors/query/h2/GridH2IndexingGeoSelfTest.java
+++ b/modules/geospatial/src/test/java/org/apache/ignite/internal/processors/query/h2/GridH2IndexingGeoSelfTest.java
@@ -101,7 +101,7 @@ public class GridH2IndexingGeoSelfTest extends GridCacheAbstractSelfTest {
List<List<?>> res = cache.query(new SqlFieldsQuery("explain select _key from Geometry where _val && ?")
.setArgs(r.read("POLYGON((5 70, 5 80, 30 80, 30 70, 5 70))")).setLocal(true)).getAll();
- assertTrue("__ explain: " + res, res.get(0).get(0).toString().contains("_val_idx"));
+ assertTrue("__ explain: " + res, res.get(0).get(0).toString().toLowerCase().contains("_val_idx"));
}
finally {
cache.destroy();
@@ -167,7 +167,7 @@ public class GridH2IndexingGeoSelfTest extends GridCacheAbstractSelfTest {
// Check explaint request.
assertTrue(F.first(cache.query(new SqlFieldsQuery("explain select * from EnemyCamp " +
- "where coords && 'POINT(25 75)'")).getAll()).get(0).toString().contains("coords_idx"));
+ "where coords && 'POINT(25 75)'")).getAll()).get(0).toString().toLowerCase().contains("coords_idx"));
}
finally {
cache.destroy();
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
index 7129691..e8dc73b 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
@@ -41,9 +41,11 @@ import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteDataStreamer;
import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteLogger;
import org.apache.ignite.binary.BinaryObject;
import org.apache.ignite.binary.BinaryObjectBuilder;
import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.processors.cache.CacheOperationContext;
import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
@@ -51,7 +53,6 @@ import org.apache.ignite.internal.processors.cache.QueryCursorImpl;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
import org.apache.ignite.internal.processors.query.GridQueryCacheObjectsIterator;
import org.apache.ignite.internal.processors.query.GridQueryCancel;
-import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
import org.apache.ignite.internal.processors.query.GridQueryFieldsResult;
import org.apache.ignite.internal.processors.query.GridQueryFieldsResultAdapter;
import org.apache.ignite.internal.processors.query.GridQueryProperty;
@@ -73,14 +74,18 @@ import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.lang.IgniteInClosure;
import org.apache.ignite.spi.indexing.IndexingQueryFilter;
import org.h2.command.Prepared;
+import org.h2.command.dml.Delete;
+import org.h2.command.dml.Insert;
+import org.h2.command.dml.Merge;
+import org.h2.command.dml.Update;
import org.h2.table.Column;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import org.jsr166.ConcurrentHashMap8;
import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.createJdbcSqlException;
+import static org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing.UPDATE_RESULT_META;
/**
*
@@ -90,11 +95,10 @@ public class DmlStatementsProcessor {
private final static int DFLT_DML_RERUN_ATTEMPTS = 4;
/** Indexing. */
- private final IgniteH2Indexing indexing;
+ private IgniteH2Indexing idx;
- /** Set of binary type ids for which warning about missing identity in configuration has been printed. */
- private final static Set<Integer> WARNED_TYPES =
- Collections.newSetFromMap(new ConcurrentHashMap8<Integer, Boolean>());
+ /** Logger. */
+ private IgniteLogger log;
/** Default size for update plan cache. */
private static final int PLAN_CACHE_SIZE = 1024;
@@ -102,15 +106,16 @@ public class DmlStatementsProcessor {
/** Update plans cache. */
private final ConcurrentMap<String, ConcurrentMap<String, UpdatePlan>> planCache = new ConcurrentHashMap<>();
- /** Dummy metadata for update result. */
- private final static List<GridQueryFieldMetadata> UPDATE_RESULT_META = Collections.<GridQueryFieldMetadata>
- singletonList(new IgniteH2Indexing.SqlFieldMetadata(null, null, "UPDATED", Long.class.getName()));
-
/**
- * @param indexing indexing.
+ * Constructor.
+ *
+ * @param ctx Kernal context.
+ * @param idx indexing.
*/
- DmlStatementsProcessor(IgniteH2Indexing indexing) {
- this.indexing = indexing;
+ public void start(GridKernalContext ctx, IgniteH2Indexing idx) {
+ this.idx = idx;
+
+ log = ctx.log(DmlStatementsProcessor.class);
}
/**
@@ -251,7 +256,7 @@ public class DmlStatementsProcessor {
final ArrayList<List<?>> data = new ArrayList<>(plan.rowsNum);
- final GridQueryFieldsResult res = indexing.queryLocalSqlFields(cctx.name(), plan.selectQry,
+ final GridQueryFieldsResult res = idx.queryLocalSqlFields(cctx.name(), plan.selectQry,
F.asList(args), null, false, 0, null);
QueryCursorImpl<List<?>> stepCur = new QueryCursorImpl<>(new Iterable<List<?>>() {
@@ -335,10 +340,10 @@ public class DmlStatementsProcessor {
.setPageSize(fieldsQry.getPageSize())
.setTimeout(fieldsQry.getTimeout(), TimeUnit.MILLISECONDS);
- cur = (QueryCursorImpl<List<?>>) indexing.queryTwoStep(cctx, newFieldsQry, cancel);
+ cur = (QueryCursorImpl<List<?>>) idx.queryTwoStep(cctx, newFieldsQry, cancel);
}
else {
- final GridQueryFieldsResult res = indexing.queryLocalSqlFields(cctx.name(), plan.selectQry,
+ final GridQueryFieldsResult res = idx.queryLocalSqlFields(cctx.name(), plan.selectQry,
F.asList(fieldsQry.getArgs()), filters, fieldsQry.isEnforceJoinOrder(), fieldsQry.getTimeout(), cancel);
cur = new QueryCursorImpl<>(new Iterable<List<?>>() {
@@ -476,7 +481,7 @@ public class DmlStatementsProcessor {
while (it.hasNext()) {
List<?> e = it.next();
if (e.size() != 2) {
- U.warn(indexing.getLogger(), "Invalid row size on DELETE - expected 2, got " + e.size());
+ U.warn(log, "Invalid row size on DELETE - expected 2, got " + e.size());
continue;
}
@@ -1069,6 +1074,16 @@ public class DmlStatementsProcessor {
}
}
+ /**
+ * Check whether statement is DML statement.
+ *
+ * @param stmt Statement.
+ * @return {@code True} if this is DML.
+ */
+ static boolean isDmlStatement(Prepared stmt) {
+ return stmt instanceof Merge || stmt instanceof Insert || stmt instanceof Update || stmt instanceof Delete;
+ }
+
/** Update result - modifications count and keys to re-run query with, if needed. */
private final static class UpdateResult {
/** Result to return for operations that affected 1 item - mostly to be used for fast updates and deletes. */
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
index 531b760..4f0a9f9 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
@@ -78,10 +78,10 @@ import org.apache.ignite.internal.processors.cache.CacheObjectContext;
import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
import org.apache.ignite.internal.processors.cache.GridCacheAffinityManager;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
-import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
+import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException;
import org.apache.ignite.internal.processors.cache.QueryCursorImpl;
import org.apache.ignite.internal.processors.cache.database.CacheDataRow;
import org.apache.ignite.internal.processors.cache.database.tree.io.PageIO;
@@ -100,6 +100,9 @@ import org.apache.ignite.internal.processors.query.GridQueryProperty;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
import org.apache.ignite.internal.processors.query.GridRunningQueryInfo;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.QueryIndexDescriptorImpl;
+import org.apache.ignite.internal.processors.query.h2.ddl.DdlStatementsProcessor;
+import org.apache.ignite.internal.processors.query.h2.opt.DistributedJoinMode;
import org.apache.ignite.internal.processors.query.h2.database.H2PkHashIndex;
import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory;
import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndex;
@@ -107,8 +110,9 @@ import org.apache.ignite.internal.processors.query.h2.database.io.H2ExtrasInnerI
import org.apache.ignite.internal.processors.query.h2.database.io.H2ExtrasLeafIO;
import org.apache.ignite.internal.processors.query.h2.database.io.H2InnerIO;
import org.apache.ignite.internal.processors.query.h2.database.io.H2LeafIO;
-import org.apache.ignite.internal.processors.query.h2.opt.DistributedJoinMode;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2DefaultTableEngine;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2SystemIndexFactory;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOffheap;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryContext;
@@ -116,17 +120,19 @@ import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowFactory;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2TreeIndex;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2ValueCacheObject;
import org.apache.ignite.internal.processors.query.h2.opt.GridLuceneIndex;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuerySplitter;
import org.apache.ignite.internal.processors.query.h2.twostep.GridMapQueryExecutor;
import org.apache.ignite.internal.processors.query.h2.twostep.GridReduceQueryExecutor;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitor;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
import org.apache.ignite.internal.processors.timeout.GridTimeoutProcessor;
import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashMap;
import org.apache.ignite.internal.util.GridEmptyCloseableIterator;
import org.apache.ignite.internal.util.GridSpinBusyLock;
+import org.apache.ignite.internal.util.GridStringBuilder;
import org.apache.ignite.internal.util.lang.GridCloseableIterator;
import org.apache.ignite.internal.util.lang.GridPlainRunnable;
import org.apache.ignite.internal.util.lang.IgniteInClosure2X;
@@ -150,14 +156,15 @@ import org.apache.ignite.resources.LoggerResource;
import org.apache.ignite.spi.indexing.IndexingQueryFilter;
import org.h2.api.ErrorCode;
import org.h2.api.JavaObjectSerializer;
+import org.h2.api.TableEngine;
import org.h2.command.CommandInterface;
import org.h2.command.Prepared;
+import org.h2.command.ddl.CreateTableData;
import org.h2.command.dml.Insert;
import org.h2.engine.Session;
import org.h2.engine.SysProperties;
import org.h2.index.Cursor;
import org.h2.index.Index;
-import org.h2.index.SpatialIndex;
import org.h2.jdbc.JdbcConnection;
import org.h2.jdbc.JdbcPreparedStatement;
import org.h2.jdbc.JdbcStatement;
@@ -167,6 +174,7 @@ import org.h2.result.SortOrder;
import org.h2.server.web.WebServer;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
+import org.h2.table.TableBase;
import org.h2.tools.Server;
import org.h2.util.JdbcUtils;
import org.h2.value.DataType;
@@ -221,8 +229,7 @@ import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType
*/
@SuppressWarnings({"UnnecessaryFullyQualifiedName", "NonFinalStaticVariableUsedInClassInitialization"})
public class IgniteH2Indexing implements GridQueryIndexing {
-
- /**
+ /*
* Register IO for indexes.
*/
static {
@@ -231,6 +238,10 @@ public class IgniteH2Indexing implements GridQueryIndexing {
H2ExtrasLeafIO.register();
}
+ /** Spatial index class name. */
+ private static final String SPATIAL_IDX_CLS =
+ "org.apache.ignite.internal.processors.query.h2.opt.GridH2SpatialIndex";
+
/** Default DB options. */
private static final String DB_OPTIONS = ";LOCK_MODE=3;MULTI_THREADED=1;DB_CLOSE_ON_EXIT=FALSE" +
";DEFAULT_LOCK_TIMEOUT=10000;FUNCTIONS_IN_SCHEMA=true;OPTIMIZE_REUSE_RESULTS=0;QUERY_CACHE_SIZE=0" +
@@ -241,6 +252,10 @@ public class IgniteH2Indexing implements GridQueryIndexing {
// Uncomment this setting to get debug output from H2 to sysout.
// ";TRACE_LEVEL_SYSTEM_OUT=3";
+ /** Dummy metadata for update result. */
+ public static final List<GridQueryFieldMetadata> UPDATE_RESULT_META = Collections.<GridQueryFieldMetadata>
+ singletonList(new SqlFieldMetadata(null, null, "UPDATED", Long.class.getName()));
+
/** */
private static final int PREPARED_STMT_CACHE_SIZE = 256;
@@ -371,10 +386,13 @@ public class IgniteH2Indexing implements GridQueryIndexing {
};
/** */
- private volatile GridKernalContext ctx;
+ protected volatile GridKernalContext ctx;
+
+ /** */
+ private final DmlStatementsProcessor dmlProc = new DmlStatementsProcessor();
/** */
- private final DmlStatementsProcessor dmlProc = new DmlStatementsProcessor(this);
+ private DdlStatementsProcessor ddlProc;
/** */
private final ConcurrentMap<String, GridH2Table> dataTables = new ConcurrentHashMap8<>();
@@ -419,13 +437,6 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
/**
- * @return Logger.
- */
- IgniteLogger getLogger() {
- return log;
- }
-
- /**
* @param c Connection.
* @param sql SQL.
* @param useStmtCache If {@code true} uses statement cache.
@@ -468,15 +479,15 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
/** {@inheritDoc} */
- @Override public PreparedStatement prepareNativeStatement(String schema, String sql) throws SQLException {
- return prepareStatement(connectionForSpace(space(schema)), sql, true);
+ @Override public PreparedStatement prepareNativeStatement(String space, String sql) throws SQLException {
+ return prepareStatement(connectionForSpace(space), sql, true);
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override public IgniteDataStreamer<?, ?> createStreamer(String spaceName, PreparedStatement nativeStmt,
long autoFlushFreq, int nodeBufSize, int nodeParOps, boolean allowOverwrite) {
- Prepared prep = GridSqlQueryParser.prepared((JdbcPreparedStatement) nativeStmt);
+ Prepared prep = GridSqlQueryParser.prepared(nativeStmt);
if (!(prep instanceof Insert))
throw new IgniteSQLException("Only INSERT operations are supported in streaming mode",
@@ -625,14 +636,14 @@ public class IgniteH2Indexing implements GridQueryIndexing {
/** {@inheritDoc} */
@Override public void store(@Nullable String spaceName,
- GridQueryTypeDescriptor type,
+ String typeName,
KeyCacheObject k,
int partId,
CacheObject v,
GridCacheVersion ver,
long expirationTime,
long link) throws IgniteCheckedException {
- TableDescriptor tbl = tableDescriptor(spaceName, type);
+ TableDescriptor tbl = tableDescriptor(typeName, spaceName);
if (tbl == null)
return; // Type was rejected.
@@ -700,7 +711,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
if (log.isDebugEnabled())
log.debug("Removing key from cache query index [locId=" + nodeId + ", key=" + key + ", val=" + val + ']');
- TableDescriptor tbl = tableDescriptor(spaceName, type);
+ TableDescriptor tbl = tableDescriptor(type.name(), spaceName);
if (tbl == null)
return;
@@ -803,12 +814,232 @@ public class IgniteH2Indexing implements GridQueryIndexing {
tbl.schema.tbls.remove(tbl.typeName());
}
+ /**
+ * Add initial user index.
+ *
+ * @param spaceName Space name.
+ * @param desc Table descriptor.
+ * @param h2Idx User index.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void addInitialUserIndex(String spaceName, TableDescriptor desc, GridH2IndexBase h2Idx)
+ throws IgniteCheckedException {
+ GridH2Table h2Tbl = desc.tbl;
+
+ h2Tbl.proposeUserIndex(h2Idx);
+
+ try {
+ String sql = indexCreateSql(desc.fullTableName(), h2Idx, false, desc.schema.escapeAll());
+
+ executeSql(spaceName, sql);
+ }
+ catch (Exception e) {
+ // Rollback and re-throw.
+ h2Tbl.rollbackUserIndex(h2Idx.getName());
+
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void dynamicIndexCreate(@Nullable final String spaceName, final String tblName,
+ final QueryIndexDescriptorImpl idxDesc, boolean ifNotExists, SchemaIndexCacheVisitor cacheVisitor)
+ throws IgniteCheckedException {
+ // Locate table.
+ String schemaName = schema(spaceName);
+
+ Schema schema = schemas.get(schemaName);
+
+ TableDescriptor desc = (schema != null ? schema.tbls.get(tblName) : null);
+
+ if (desc == null)
+ throw new IgniteCheckedException("Table not found in internal H2 database [schemaName=" + schemaName +
+ ", tblName=" + tblName + ']');
+
+ GridH2Table h2Tbl = desc.tbl;
+
+ // Create index.
+ final GridH2IndexBase h2Idx = desc.createUserIndex(idxDesc);
+
+ h2Tbl.proposeUserIndex(h2Idx);
+
+ try {
+ // Populate index with existing cache data.
+ final GridH2RowDescriptor rowDesc = h2Tbl.rowDescriptor();
+
+ SchemaIndexCacheVisitorClosure clo = new SchemaIndexCacheVisitorClosure() {
+ @Override public void apply(KeyCacheObject key, int part, CacheObject val, GridCacheVersion ver,
+ long expTime, long link) throws IgniteCheckedException {
+ if (expTime == 0L)
+ expTime = Long.MAX_VALUE;
+
+ GridH2Row row = rowDesc.createRow(key, part, val, ver, expTime);
+
+ row.link(link);
+
+ h2Idx.put(row);
+ }
+ };
+
+ cacheVisitor.visit(clo);
+
+ // At this point index is in consistent state, promote it through H2 SQL statement, so that cached
+ // prepared statements are re-built.
+ String sql = indexCreateSql(desc.fullTableName(), h2Idx, ifNotExists, schema.escapeAll());
+
+ executeSql(spaceName, sql);
+ }
+ catch (Exception e) {
+ // Rollback and re-throw.
+ h2Tbl.rollbackUserIndex(h2Idx.getName());
+
+ throw e;
+ }
+ }
+
/** {@inheritDoc} */
+ @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
+ @Override public void dynamicIndexDrop(@Nullable final String spaceName, String idxName, boolean ifExists)
+ throws IgniteCheckedException{
+ String schemaName = schema(spaceName);
+
+ Schema schema = schemas.get(schemaName);
+
+ String sql = indexDropSql(schemaName, idxName, ifExists, schema.escapeAll());
+
+ executeSql(spaceName, sql);
+ }
+
+ /**
+ * Execute DDL command.
+ *
+ * @param spaceName Space name.
+ * @param sql SQL.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void executeSql(String spaceName, String sql) throws IgniteCheckedException {
+ try {
+ Connection conn = connectionForSpace(spaceName);
+
+ try (PreparedStatement stmt = prepareStatement(conn, sql, false)) {
+ stmt.execute();
+ }
+ }
+ catch (Exception e) {
+ throw new IgniteCheckedException("Failed to execute SQL statement on internal H2 database: " + sql, e);
+ }
+ }
+
+ /**
+ * Generate {@code CREATE INDEX} SQL statement for given params.
+ * @param fullTblName Fully qualified table name.
+ * @param h2Idx H2 index.
+ * @param ifNotExists Quietly skip index creation if it exists.
+ * @return Statement string.
+ */
+ private static String indexCreateSql(String fullTblName, GridH2IndexBase h2Idx, boolean ifNotExists,
+ boolean escapeAll) {
+ boolean spatial = F.eq(SPATIAL_IDX_CLS, h2Idx.getClass().getName());
+
+ GridStringBuilder sb = new SB("CREATE ")
+ .a(spatial ? "SPATIAL " : "")
+ .a("INDEX ")
+ .a(ifNotExists ? "IF NOT EXISTS " : "")
+ .a(escapeName(h2Idx.getName(), escapeAll))
+ .a(" ON ")
+ .a(fullTblName)
+ .a(" (");
+
+ boolean first = true;
+
+ for (IndexColumn col : h2Idx.getIndexColumns()) {
+ if (first)
+ first = false;
+ else
+ sb.a(", ");
+
+ sb.a("\"" + col.columnName + "\"").a(" ").a(col.sortType == SortOrder.ASCENDING ? "ASC" : "DESC");
+ }
+
+ sb.a(')');
+
+ return sb.toString();
+ }
+
+ /**
+ * Generate {@code CREATE INDEX} SQL statement for given params.
+ * @param schemaName <b>Quoted</b> schema name.
+ * @param idxName Index name.
+ * @param ifExists Quietly skip index drop if it exists.
+ * @param escapeAll Escape flag.
+ * @return Statement string.
+ */
+ private static String indexDropSql(String schemaName, String idxName, boolean ifExists, boolean escapeAll) {
+ return "DROP INDEX " + (ifExists ? "IF EXISTS " : "") + schemaName + '.' + escapeName(idxName, escapeAll);
+ }
+
+ /**
+ * Create sorted index.
+ *
+ * @param schema Schema.
+ * @param name Index name,
+ * @param tbl Table.
+ * @param pk Primary key flag.
+ * @param cols Columns.
+ * @return Index.
+ */
+ private GridH2IndexBase createSortedIndex(Schema schema, String name, GridH2Table tbl, boolean pk,
+ List<IndexColumn> cols, int inlineSize) {
+ try {
+ GridCacheContext cctx = schema.cacheContext();
+
+ if (log.isDebugEnabled())
+ log.debug("Creating cache index [cacheId=" + cctx.cacheId() + ", idxName=" + name + ']');
+
+ final int segments = tbl.rowDescriptor().configuration().getQueryParallelism();
+
+ return new H2TreeIndex(cctx, tbl, name, pk, cols, inlineSize, segments);
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ /**
+ * Create spatial index.
+ *
+ * @param tbl Table.
+ * @param idxName Index name.
+ * @param cols Columns.
+ */
+ private GridH2IndexBase createSpatialIndex(GridH2Table tbl, String idxName, IndexColumn[] cols
+ ) {
+ try {
+ Class<?> cls = Class.forName(SPATIAL_IDX_CLS);
+
+ Constructor<?> ctor = cls.getConstructor(
+ GridH2Table.class,
+ String.class,
+ Integer.TYPE,
+ IndexColumn[].class);
+
+ if (!ctor.isAccessible())
+ ctor.setAccessible(true);
+
+ final int segments = tbl.rowDescriptor().configuration().getQueryParallelism();
+
+ return (GridH2IndexBase)ctor.newInstance(tbl, idxName, segments, cols);
+ }
+ catch (Exception e) {
+ throw new IgniteException("Failed to instantiate: " + SPATIAL_IDX_CLS, e);
+ }
+ }
+
@SuppressWarnings("unchecked")
@Override public <K, V> GridCloseableIterator<IgniteBiTuple<K, V>> queryLocalText(
- @Nullable String spaceName, String qry, GridQueryTypeDescriptor type,
+ @Nullable String spaceName, String qry, String typeName,
IndexingQueryFilter filters) throws IgniteCheckedException {
- TableDescriptor tbl = tableDescriptor(spaceName, type);
+ TableDescriptor tbl = tableDescriptor(typeName, spaceName);
if (tbl != null && tbl.luceneIdx != null) {
GridRunningQueryInfo run = new GridRunningQueryInfo(qryIdGen.incrementAndGet(), qry, TEXT, spaceName,
@@ -828,9 +1059,9 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
/** {@inheritDoc} */
- @Override public void unregisterType(@Nullable String spaceName, GridQueryTypeDescriptor type)
+ @Override public void unregisterType(@Nullable String spaceName, String typeName)
throws IgniteCheckedException {
- TableDescriptor tbl = tableDescriptor(spaceName, type);
+ TableDescriptor tbl = tableDescriptor(typeName, spaceName);
if (tbl != null)
removeTable(tbl);
@@ -860,9 +1091,9 @@ public class IgniteH2Indexing implements GridQueryIndexing {
final PreparedStatement stmt = preparedStatementWithParams(conn, qry, params, true);
- Prepared p = GridSqlQueryParser.prepared((JdbcPreparedStatement)stmt);
+ Prepared p = GridSqlQueryParser.prepared(stmt);
- if (!p.isQuery()) {
+ if (DmlStatementsProcessor.isDmlStatement(p)) {
SqlFieldsQuery fldsQry = new SqlFieldsQuery(qry);
if (params != null)
@@ -873,6 +1104,9 @@ public class IgniteH2Indexing implements GridQueryIndexing {
return dmlProc.updateLocalSqlFields(spaceName, stmt, fldsQry, filter, cancel);
}
+ else if (DdlStatementsProcessor.isDdlStatement(p))
+ throw new IgniteSQLException("DDL statements are supported for the whole cluster only",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
List<GridQueryFieldMetadata> meta;
@@ -1419,6 +1653,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
.distributedJoinMode(distributedJoinMode));
PreparedStatement stmt = null;
+ Prepared prepared;
boolean cachesCreated = false;
@@ -1434,7 +1669,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
catch (SQLException e) {
if (!cachesCreated && e.getErrorCode() == ErrorCode.SCHEMA_NOT_FOUND_1) {
try {
- ctx.cache().createMissingCaches();
+ ctx.cache().createMissingQueryCaches();
}
catch (IgniteCheckedException ignored) {
throw new CacheException("Failed to create missing caches.", e);
@@ -1449,7 +1684,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
- Prepared prepared = GridSqlQueryParser.prepared(stmt);
+ prepared = GridSqlQueryParser.prepared(stmt);
if (qry instanceof JdbcSqlFieldsQuery && ((JdbcSqlFieldsQuery) qry).isQuery() != prepared.isQuery())
throw new IgniteSQLException("Given statement type does not match that declared by JDBC driver",
@@ -1470,12 +1705,23 @@ public class IgniteH2Indexing implements GridQueryIndexing {
// It is a DML statement if we did not create a twoStepQuery.
if (twoStepQry == null) {
- try {
- return dmlProc.updateSqlFieldsTwoStep(cctx.namexx(), stmt, qry, cancel);
+ if (DmlStatementsProcessor.isDmlStatement(prepared)) {
+ try {
+ return dmlProc.updateSqlFieldsTwoStep(cctx.namexx(), stmt, qry, cancel);
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteSQLException("Failed to execute DML statement [stmt=" + sqlQry +
+ ", params=" + Arrays.deepToString(qry.getArgs()) + "]", e);
+ }
}
- catch (IgniteCheckedException e) {
- throw new IgniteSQLException("Failed to execute DML statement [qry=" + sqlQry + ", params=" +
- Arrays.deepToString(qry.getArgs()) + "]", e);
+
+ if (DdlStatementsProcessor.isDdlStatement(prepared)) {
+ try {
+ return ddlProc.runDdlStatement(stmt);
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteSQLException("Failed to execute DDL statement [stmt=" + sqlQry + ']', e);
+ }
}
}
@@ -1660,7 +1906,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
try {
Connection conn = connectionForThread(schemaName);
- createTable(schema, tbl, conn);
+ createTable(spaceName, schema, tbl, conn);
schema.add(tbl);
}
@@ -1754,12 +2000,15 @@ public class IgniteH2Indexing implements GridQueryIndexing {
/**
* Create db table by using given table descriptor.
*
+ * @param spaceName Space name.
* @param schema Schema.
* @param tbl Table descriptor.
* @param conn Connection.
* @throws SQLException If failed to create db table.
+ * @throws IgniteCheckedException If failed.
*/
- private void createTable(Schema schema, TableDescriptor tbl, Connection conn) throws SQLException {
+ private void createTable(String spaceName, Schema schema, TableDescriptor tbl, Connection conn)
+ throws SQLException, IgniteCheckedException {
assert schema != null;
assert tbl != null;
@@ -1783,12 +2032,17 @@ public class IgniteH2Indexing implements GridQueryIndexing {
if (log.isDebugEnabled())
log.debug("Creating DB table with SQL: " + sql);
- GridH2RowDescriptor desc = new RowDescriptor(tbl.type(), schema);
+ GridH2RowDescriptor rowDesc = new RowDescriptor(tbl.type(), schema);
- GridH2Table res = GridH2Table.Engine.createTable(conn, sql.toString(), desc, tbl, tbl.schema.spaceName);
+ H2RowFactory rowFactory = tbl.rowFactory(rowDesc);
- if (dataTables.putIfAbsent(res.identifier(), res) != null)
- throw new IllegalStateException("Table already exists: " + res.identifier());
+ GridH2Table h2Tbl = H2TableEngine.createTable(conn, sql.toString(), rowDesc, rowFactory, tbl);
+
+ for (GridH2IndexBase usrIdx : tbl.createUserIndexes())
+ addInitialUserIndex(spaceName, tbl, usrIdx);
+
+ if (dataTables.putIfAbsent(h2Tbl.identifier(), h2Tbl) != null)
+ throw new IllegalStateException("Table already exists: " + h2Tbl.identifier());
}
/**
@@ -1800,24 +2054,29 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
/**
- * Gets corresponding DB type from java class.
+ * Find table by name in given schema.
*
- * @param cls Java class.
- * @return DB type name.
+ * @param schemaName Schema name.
+ * @param tblName Table name.
+ * @return Table or {@code null} if none found.
*/
- private String dbTypeFromClass(Class<?> cls) {
- return DBTypeEnum.fromClass(cls).dBTypeAsString();
+ public GridH2Table dataTable(String schemaName, String tblName) {
+ for (GridH2Table tbl : dataTables.values()) {
+ if (tbl.getSchema().getName().equals(schemaName) && tbl.getName().equals(tblName))
+ return tbl;
+ }
+
+ return null;
}
/**
- * Gets table descriptor by value type.
+ * Gets corresponding DB type from java class.
*
- * @param spaceName Space name.
- * @param type Value type descriptor.
- * @return Table descriptor or {@code null} if not found.
+ * @param cls Java class.
+ * @return DB type name.
*/
- @Nullable private TableDescriptor tableDescriptor(@Nullable String spaceName, GridQueryTypeDescriptor type) {
- return tableDescriptor(type.name(), spaceName);
+ private String dbTypeFromClass(Class<?> cls) {
+ return DBTypeEnum.fromClass(cls).dBTypeAsString();
}
/**
@@ -1903,7 +2162,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
*/
@Override public void rebuildIndexesFromHash(@Nullable String spaceName,
GridQueryTypeDescriptor type) throws IgniteCheckedException {
- TableDescriptor tbl = tableDescriptor(spaceName, type);
+ TableDescriptor tbl = tableDescriptor(type.name(), spaceName);
if (tbl == null)
return;
@@ -1961,7 +2220,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
/** {@inheritDoc} */
@Override public void markForRebuildFromHash(@Nullable String spaceName, GridQueryTypeDescriptor type) {
- TableDescriptor tbl = tableDescriptor(spaceName, type);
+ TableDescriptor tbl = tableDescriptor(type.name(), spaceName);
if (tbl == null)
return;
@@ -1975,12 +2234,12 @@ public class IgniteH2Indexing implements GridQueryIndexing {
* Gets size (for tests only).
*
* @param spaceName Space name.
- * @param type Type descriptor.
+ * @param typeName Type name.
* @return Size.
* @throws IgniteCheckedException If failed or {@code -1} if the type is unknown.
*/
- long size(@Nullable String spaceName, GridQueryTypeDescriptor type) throws IgniteCheckedException {
- TableDescriptor tbl = tableDescriptor(spaceName, type);
+ long size(@Nullable String spaceName, String typeName) throws IgniteCheckedException {
+ TableDescriptor tbl = tableDescriptor(typeName, spaceName);
if (tbl == null)
return -1;
@@ -2093,6 +2352,11 @@ public class IgniteH2Indexing implements GridQueryIndexing {
cleanupStatementCache();
}
}, CLEANUP_STMT_CACHE_PERIOD, CLEANUP_STMT_CACHE_PERIOD);
+
+ ddlProc = new DdlStatementsProcessor();
+
+ dmlProc.start(ctx, this);
+ ddlProc.start(ctx, this);
}
if (JdbcUtils.serializer != null)
@@ -2255,7 +2519,6 @@ public class IgniteH2Indexing implements GridQueryIndexing {
log.debug("Stopping cache query index...");
// unregisterMBean(); TODO https://issues.apache.org/jira/browse/IGNITE-2139
-
if (ctx != null && !ctx.cache().context().database().persistenceEnabled()) {
for (Schema schema : schemas.values())
schema.onDrop();
@@ -2778,7 +3041,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
/**
* Information about table in database.
*/
- private class TableDescriptor implements GridH2Table.IndexesFactory {
+ private class TableDescriptor implements GridH2SystemIndexFactory {
/** */
private final String fullTblName;
@@ -2795,9 +3058,6 @@ public class IgniteH2Indexing implements GridQueryIndexing {
private GridLuceneIndex luceneIdx;
/** */
- private Index pkTreeIdx;
-
- /** */
private H2PkHashIndex pkHashIdx;
/**
@@ -2808,7 +3068,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
this.type = type;
this.schema = schema;
- String tblName = escapeName(type.tableName() != null ? type.tableName() : type.name(), schema.escapeAll());
+ String tblName = escapeName(type.tableName(), schema.escapeAll());
fullTblName = schema.schemaName + "." + tblName;
}
@@ -2846,22 +3106,23 @@ public class IgniteH2Indexing implements GridQueryIndexing {
return S.toString(TableDescriptor.class, this);
}
- /** {@inheritDoc} */
- @Override public H2RowFactory createRowFactory(GridH2Table tbl) {
- int cacheId = CU.cacheId(schema.ccfg.getName());
-
- GridCacheContext cctx = ctx.cache().context().cacheContext(cacheId);
+ /**
+ * Create H2 row factory.
+ *
+ * @param rowDesc Row descriptor.
+ * @return H2 row factory.
+ */
+ H2RowFactory rowFactory(GridH2RowDescriptor rowDesc) {
+ GridCacheContext cctx = schema.cacheContext();
if (cctx.affinityNode() && cctx.offheapIndex())
- return new H2RowFactory(tbl.rowDescriptor(), cctx);
+ return new H2RowFactory(rowDesc, cctx);
return null;
}
/** {@inheritDoc} */
- @Override public ArrayList<Index> createIndexes(GridH2Table tbl) {
- this.tbl = tbl;
-
+ @Override public ArrayList<Index> createSystemIndexes(GridH2Table tbl) {
ArrayList<Index> idxs = new ArrayList<>();
IndexColumn keyCol = tbl.indexColumn(KEY_COL, SortOrder.ASCENDING);
@@ -2870,25 +3131,27 @@ public class IgniteH2Indexing implements GridQueryIndexing {
if (affCol != null && equal(affCol, keyCol))
affCol = null;
- int cacheId = CU.cacheId(schema.ccfg.getName());
-
Index hashIdx = createHashIndex(
- cacheId,
- "_key_PK_hash",
- tbl,
- treeIndexColumns(new ArrayList<IndexColumn>(2), keyCol, affCol));
+ schema,
+ tbl,
+ "_key_PK_hash",
+ treeIndexColumns(new ArrayList<IndexColumn>(2), keyCol, affCol)
+ );
if (hashIdx != null)
idxs.add(hashIdx);
// Add primary key index.
- idxs.add(createSortedIndex(
- cacheId,
+ Index pkIdx = createSortedIndex(
+ schema,
"_key_PK",
tbl,
true,
treeIndexColumns(new ArrayList<IndexColumn>(2), keyCol, affCol),
- -1));
+ -1
+ );
+
+ idxs.add(pkIdx);
if (type().valueClass() == String.class) {
try {
@@ -2901,50 +3164,40 @@ public class IgniteH2Indexing implements GridQueryIndexing {
boolean affIdxFound = false;
- for (Map.Entry<String, GridQueryIndexDescriptor> e : type.indexes().entrySet()) {
- String name = e.getKey();
- GridQueryIndexDescriptor idx = e.getValue();
+ GridQueryIndexDescriptor textIdx = type.textIndex();
- if (idx.type() == QueryIndexType.FULLTEXT) {
- try {
- luceneIdx = new GridLuceneIndex(ctx, schema.offheap, schema.spaceName, type);
- }
- catch (IgniteCheckedException e1) {
- throw new IgniteException(e1);
- }
+ if (textIdx != null) {
+ try {
+ luceneIdx = new GridLuceneIndex(ctx, schema.offheap, schema.spaceName, type);
}
- else {
- List<IndexColumn> cols = new ArrayList<>(idx.fields().size() + 2);
-
- boolean escapeAll = schema.escapeAll();
+ catch (IgniteCheckedException e1) {
+ throw new IgniteException(e1);
+ }
+ }
- for (String field : idx.fields()) {
- String fieldName = escapeAll ? field : escapeName(field, false).toUpperCase();
+ // Locate index where affinity column is first (if any).
+ if (affCol != null) {
+ for (GridQueryIndexDescriptor idxDesc : type.indexes().values()) {
+ if (idxDesc.type() != QueryIndexType.SORTED)
+ continue;
- Column col = tbl.getColumn(fieldName);
+ String firstField = idxDesc.fields().iterator().next();
- cols.add(tbl.indexColumn(col.getColumnId(),
- idx.descending(field) ? SortOrder.DESCENDING : SortOrder.ASCENDING));
- }
+ String firstFieldName =
+ schema.escapeAll() ? firstField : escapeName(firstField, false).toUpperCase();
- if (idx.type() == QueryIndexType.SORTED) {
- // We don't care about number of fields in affinity index, just affinity key must be the first.
- affIdxFound |= affCol != null && equal(cols.get(0), affCol);
+ Column col = tbl.getColumn(firstFieldName);
- cols = treeIndexColumns(cols, keyCol, affCol);
+ IndexColumn idxCol = tbl.indexColumn(col.getColumnId(),
+ idxDesc.descending(firstField) ? SortOrder.DESCENDING : SortOrder.ASCENDING);
- idxs.add(createSortedIndex(cacheId, name, tbl, false, cols, idx.inlineSize()));
- }
- else if (idx.type() == QueryIndexType.GEOSPATIAL)
- idxs.add(createH2SpatialIndex(tbl, name, cols.toArray(new IndexColumn[cols.size()])));
- else
- throw new IllegalStateException("Index type: " + idx.type());
+ affIdxFound |= equal(idxCol, affCol);
}
}
// Add explicit affinity key index if nothing alike was found.
if (affCol != null && !affIdxFound) {
- idxs.add(createSortedIndex(cacheId, "AFFINITY_KEY", tbl, false,
+ idxs.add(createSortedIndex(schema, "AFFINITY_KEY", tbl, false,
treeIndexColumns(new ArrayList<IndexColumn>(2), affCol, keyCol), -1));
}
@@ -2952,121 +3205,92 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
/**
- * @param cacheId Cache ID.
- * @param name Index name,
- * @param tbl Table.
- * @param pk Primary key flag.
- * @param cols Columns.
- * @return Index.
+ * Get collection of user indexes.
+ *
+ * @return User indexes.
*/
- private Index createSortedIndex(
- int cacheId,
- String name,
- GridH2Table tbl,
- boolean pk,
- List<IndexColumn> cols,
- int inlineSize
- ) {
- try {
- GridCacheSharedContext<Object, Object> scctx = ctx.cache().context();
+ public Collection<GridH2IndexBase> createUserIndexes() {
+ assert tbl != null;
- GridCacheContext cctx = scctx.cacheContext(cacheId);
+ ArrayList<GridH2IndexBase> res = new ArrayList<>();
- if (log.isInfoEnabled())
- log.info("Creating cache index [cacheId=" + cctx.cacheId() + ", idxName=" + name + ']');
+ for (GridQueryIndexDescriptor idxDesc : type.indexes().values()) {
+ GridH2IndexBase idx = createUserIndex(idxDesc);
- final int segments = tbl.rowDescriptor().configuration().getQueryParallelism();
-
- return new H2TreeIndex(cctx, tbl, name, pk, cols, inlineSize, segments);
- }
- catch (IgniteCheckedException e) {
- throw new IgniteException(e);
+ res.add(idx);
}
+
+ return res;
}
/**
+ * Create user index.
+ *
+ * @param idxDesc Index descriptor.
* @return Index.
*/
- private Index createHashIndex(
- int cacheId,
- String name,
- GridH2Table tbl,
- List<IndexColumn> cols
- ) {
- GridCacheSharedContext<Object, Object> scctx = ctx.cache().context();
+ private GridH2IndexBase createUserIndex(GridQueryIndexDescriptor idxDesc) {
+ String name = schema.escapeAll() ? idxDesc.name() : escapeName(idxDesc.name(), false).toUpperCase();
- GridCacheContext cctx = scctx.cacheContext(cacheId);
+ IndexColumn keyCol = tbl.indexColumn(KEY_COL, SortOrder.ASCENDING);
+ IndexColumn affCol = tbl.getAffinityKeyColumn();
- if (cctx.affinityNode() && cctx.offheapIndex()) {
- assert pkHashIdx == null : pkHashIdx;
+ List<IndexColumn> cols = new ArrayList<>(idxDesc.fields().size() + 2);
- pkHashIdx = new H2PkHashIndex(
- cctx,
- tbl,
- name,
- cols);
+ boolean escapeAll = schema.escapeAll();
- return pkHashIdx;
- }
+ for (String field : idxDesc.fields()) {
+ String fieldName = escapeAll ? field : escapeName(field, false).toUpperCase();
- return null;
- }
+ Column col = tbl.getColumn(fieldName);
- /**
- *
- */
- void onDrop() {
- dataTables.remove(tbl.identifier(), tbl);
+ cols.add(tbl.indexColumn(col.getColumnId(),
+ idxDesc.descending(field) ? SortOrder.DESCENDING : SortOrder.ASCENDING));
+ }
- tbl.destroy();
+ if (idxDesc.type() == QueryIndexType.SORTED) {
+ cols = treeIndexColumns(cols, keyCol, affCol);
- U.closeQuiet(luceneIdx);
+ return createSortedIndex(schema, name, tbl, false, cols, idxDesc.inlineSize());
+ }
+ else if (idxDesc.type() == QueryIndexType.GEOSPATIAL)
+ return createSpatialIndex(tbl, name, cols.toArray(new IndexColumn[cols.size()]));
+
+ throw new IllegalStateException("Index type: " + idxDesc.type());
}
/**
+ * Create hash index.
+ *
+ * @param schema Schema.
* @param tbl Table.
* @param idxName Index name.
* @param cols Columns.
+ * @return Index.
*/
- private SpatialIndex createH2SpatialIndex(
- GridH2Table tbl,
- String idxName,
- IndexColumn[] cols
- ) {
- String className = "org.apache.ignite.internal.processors.query.h2.opt.GridH2SpatialIndex";
-
- try {
- Class<?> cls = Class.forName(className);
+ private Index createHashIndex(Schema schema, GridH2Table tbl, String idxName, List<IndexColumn> cols) {
+ GridCacheContext cctx = schema.cacheContext();
- Constructor<?> ctor = cls.getConstructor(
- GridH2Table.class,
- String.class,
- Integer.TYPE,
- IndexColumn[].class);
-
- if (!ctor.isAccessible())
- ctor.setAccessible(true);
+ if (cctx.affinityNode() && cctx.offheapIndex()) {
+ assert pkHashIdx == null : pkHashIdx;
- final int segments = tbl.rowDescriptor().configuration().getQueryParallelism();
+ pkHashIdx = new H2PkHashIndex(cctx, tbl, idxName, cols);
- return (SpatialIndex)ctor.newInstance(tbl, idxName, segments, cols);
- }
- catch (Exception e) {
- throw new IgniteException("Failed to instantiate: " + className, e);
+ return pkHashIdx;
}
+
+ return null;
}
/**
- * @param idxName Index name.
- * @param tbl Table.
- * @param pk Primary key flag.
- * @param columns Index column list.
- * @return
+ *
*/
- private Index createTreeIndex(String idxName, GridH2Table tbl, boolean pk, List<IndexColumn> columns) {
- final int segments = tbl.rowDescriptor().configuration().getQueryParallelism();
+ void onDrop() {
+ dataTables.remove(tbl.identifier(), tbl);
+
+ tbl.destroy();
- return new GridH2TreeIndex(idxName, tbl, pk, columns, segments);
+ U.closeQuiet(luceneIdx);
}
}
@@ -3252,6 +3476,13 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
/**
+ * @return Cache context.
+ */
+ public GridCacheContext cacheContext() {
+ return cctx;
+ }
+
+ /**
* @param tbl Table descriptor.
*/
public void add(TableDescriptor tbl) {
@@ -3368,7 +3599,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
/** {@inheritDoc} */
@Override public GridCacheContext<?, ?> context() {
- return schema.cctx;
+ return schema.cacheContext();
}
/** {@inheritDoc} */
@@ -3625,4 +3856,63 @@ public class IgniteH2Indexing implements GridQueryIndexing {
lastUsage = U.currentTimeMillis();
}
}
+
+ /**
+ * H2 Table engine.
+ */
+ public static class H2TableEngine implements TableEngine {
+ /** */
+ private static GridH2RowDescriptor rowDesc0;
+
+ /** */
+ private static H2RowFactory rowFactory0;
+
+ /** */
+ private static TableDescriptor tblDesc0;
+
+ /** */
+ private static GridH2Table resTbl0;
+
+ /**
+ * Creates table using given connection, DDL clause for given type descriptor and list of indexes.
+ *
+ * @param conn Connection.
+ * @param sql DDL clause.
+ * @param rowDesc Row descriptor.
+ * @param rowFactory Row factory.
+ * @param tblDesc Table descriptor.
+ * @throws SQLException If failed.
+ * @return Created table.
+ */
+ public static synchronized GridH2Table createTable(Connection conn, String sql,
+ @Nullable GridH2RowDescriptor rowDesc, H2RowFactory rowFactory, TableDescriptor tblDesc)
+ throws SQLException {
+ rowDesc0 = rowDesc;
+ rowFactory0 = rowFactory;
+ tblDesc0 = tblDesc;
+
+ try {
+ try (Statement s = conn.createStatement()) {
+ s.execute(sql + " engine \"" + H2TableEngine.class.getName() + "\"");
+ }
+
+ tblDesc.tbl = resTbl0;
+
+ return resTbl0;
+ }
+ finally {
+ resTbl0 = null;
+ tblDesc0 = null;
+ rowFactory0 = null;
+ rowDesc0 = null;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public TableBase createTable(CreateTableData createTblData) {
+ resTbl0 = new GridH2Table(createTblData, rowDesc0, rowFactory0, tblDesc0, tblDesc0.schema.spaceName);
+
+ return resTbl0;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
new file mode 100644
index 0000000..5b4b494
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.ddl;
+
+import java.sql.PreparedStatement;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.cache.QueryCursorImpl;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.GridQueryProperty;
+import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlCreateIndex;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlDropIndex;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlStatement;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
+import org.h2.command.Prepared;
+import org.h2.command.ddl.CreateIndex;
+import org.h2.command.ddl.DropIndex;
+import org.h2.jdbc.JdbcPreparedStatement;
+
+import static org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing.UPDATE_RESULT_META;
+
+/**
+ * DDL statements processor.<p>
+ * Contains higher level logic to handle operations as a whole and communicate with the client.
+ */
+public class DdlStatementsProcessor {
+ /** Kernal context. */
+ GridKernalContext ctx;
+
+ /** Indexing. */
+ IgniteH2Indexing idx;
+
+ /**
+ * Initialize message handlers and this' fields needed for further operation.
+ *
+ * @param ctx Kernal context.
+ * @param idx Indexing.
+ */
+ public void start(final GridKernalContext ctx, IgniteH2Indexing idx) {
+ this.ctx = ctx;
+ this.idx = idx;
+ }
+
+ /**
+ * Execute DDL statement.
+ *
+ * @param stmt H2 statement to parse and execute.
+ */
+ @SuppressWarnings("unchecked")
+ public QueryCursor<List<?>> runDdlStatement(PreparedStatement stmt)
+ throws IgniteCheckedException {
+ assert stmt instanceof JdbcPreparedStatement;
+
+ IgniteInternalFuture fut;
+
+ try {
+ GridSqlStatement gridStmt = new GridSqlQueryParser(false).parse(GridSqlQueryParser.prepared(stmt));
+
+ if (gridStmt instanceof GridSqlCreateIndex) {
+ GridSqlCreateIndex createIdx = (GridSqlCreateIndex)gridStmt;
+
+ String spaceName = idx.space(createIdx.schemaName());
+
+ QueryIndex newIdx = new QueryIndex();
+
+ newIdx.setName(createIdx.index().getName());
+
+ newIdx.setIndexType(createIdx.index().getIndexType());
+
+ LinkedHashMap<String, Boolean> flds = new LinkedHashMap<>();
+
+ GridH2Table tbl = idx.dataTable(createIdx.schemaName(), createIdx.tableName());
+
+ if (tbl == null)
+ throw new IgniteSQLException("Table not found [schemaName=" + createIdx.schemaName() + ", " +
+ "tblName=" + createIdx.tableName() + ']', IgniteQueryErrorCode.TABLE_NOT_FOUND);
+
+ assert tbl.rowDescriptor() != null;
+
+ // Let's replace H2's table and property names by those operated by GridQueryProcessor.
+ GridQueryTypeDescriptor typeDesc = tbl.rowDescriptor().type();
+
+ for (Map.Entry<String, Boolean> e : createIdx.index().getFields().entrySet()) {
+ GridQueryProperty prop = typeDesc.property(e.getKey());
+
+ if (prop == null)
+ throw new IgniteSQLException("Property not found [typeName=" + typeDesc.name() + ", propName=" +
+ e.getKey() + ']');
+
+ flds.put(prop.name(), e.getValue());
+ }
+
+ newIdx.setFields(flds);
+
+ fut = ctx.query().dynamicIndexCreate(spaceName, typeDesc.tableName(), newIdx, createIdx.ifNotExists());
+ }
+ else if (gridStmt instanceof GridSqlDropIndex) {
+ GridSqlDropIndex dropIdx = (GridSqlDropIndex)gridStmt;
+
+ String spaceName = idx.space(dropIdx.schemaName());
+
+ fut = ctx.query().dynamicIndexDrop(spaceName, dropIdx.name(), dropIdx.ifExists());
+ }
+ else
+ throw new IgniteSQLException("Unexpected DDL operation [type=" + gridStmt.getClass() + ']',
+ IgniteQueryErrorCode.UNEXPECTED_OPERATION);
+
+ fut.get();
+
+ QueryCursorImpl<List<?>> resCur = (QueryCursorImpl<List<?>>)new QueryCursorImpl(Collections.singletonList
+ (Collections.singletonList(0L)), null, false);
+
+ resCur.fieldsMeta(UPDATE_RESULT_META);
+
+ return resCur;
+ }
+ catch (SchemaOperationException e) {
+ throw convert(e);
+ }
+ catch (Exception e) {
+ throw new IgniteSQLException("DLL operation failed.", e);
+ }
+ }
+
+ /**
+ * @return {@link IgniteSQLException} with the message same as of {@code this}'s and
+ */
+ private IgniteSQLException convert(SchemaOperationException e) {
+ int sqlCode;
+
+ switch (e.code()) {
+ case SchemaOperationException.CODE_CACHE_NOT_FOUND:
+ sqlCode = IgniteQueryErrorCode.CACHE_NOT_FOUND;
+
+ break;
+
+ case SchemaOperationException.CODE_TABLE_NOT_FOUND:
+ sqlCode = IgniteQueryErrorCode.TABLE_NOT_FOUND;
+
+ break;
+
+ case SchemaOperationException.CODE_TABLE_EXISTS:
+ sqlCode = IgniteQueryErrorCode.TABLE_ALREADY_EXISTS;
+
+ break;
+
+ case SchemaOperationException.CODE_COLUMN_NOT_FOUND:
+ sqlCode = IgniteQueryErrorCode.COLUMN_NOT_FOUND;
+
+ break;
+
+ case SchemaOperationException.CODE_COLUMN_EXISTS:
+ sqlCode = IgniteQueryErrorCode.COLUMN_ALREADY_EXISTS;
+
+ break;
+
+ case SchemaOperationException.CODE_INDEX_NOT_FOUND:
+ sqlCode = IgniteQueryErrorCode.INDEX_NOT_FOUND;
+
+ break;
+
+ case SchemaOperationException.CODE_INDEX_EXISTS:
+ sqlCode = IgniteQueryErrorCode.INDEX_ALREADY_EXISTS;
+
+ break;
+
+ default:
+ sqlCode = IgniteQueryErrorCode.UNKNOWN;
+ }
+
+ return new IgniteSQLException(e.getMessage(), sqlCode);
+ }
+
+ /**
+ * @param cmd Statement.
+ * @return Whether {@code cmd} is a DDL statement we're able to handle.
+ */
+ public static boolean isDdlStatement(Prepared cmd) {
+ return cmd instanceof CreateIndex || cmd instanceof DropIndex;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
index 67e294a..7163834 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
@@ -376,7 +376,7 @@ public abstract class GridH2IndexBase extends BaseIndex {
/** {@inheritDoc} */
@Override public void remove(Session ses) {
- throw DbException.getUnsupportedException("remove index");
+ // No-op: destroyed from owning table.
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2PrimaryScanIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2PrimaryScanIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2PrimaryScanIndex.java
new file mode 100644
index 0000000..097b34e
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2PrimaryScanIndex.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.opt;
+
+import org.h2.engine.Session;
+import org.h2.result.SortOrder;
+import org.h2.table.TableFilter;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Wrapper type for primary key.
+ */
+@SuppressWarnings("PackageVisibleInnerClass")
+public class GridH2PrimaryScanIndex extends GridH2ScanIndex<GridH2IndexBase> {
+ /** */
+ static final String SCAN_INDEX_NAME_SUFFIX = "__SCAN_";
+
+ /** Parent table. */
+ private final GridH2Table tbl;
+
+ /** */
+ private final GridH2IndexBase hashIdx;
+
+ /**
+ * Constructor.
+ *
+ * @param tbl Table.
+ * @param treeIdx Tree index.
+ * @param hashIdx Hash index.
+ */
+ GridH2PrimaryScanIndex(GridH2Table tbl, GridH2IndexBase treeIdx, @Nullable GridH2IndexBase hashIdx) {
+ super(treeIdx);
+
+ this.tbl = tbl;
+ this.hashIdx = hashIdx;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected GridH2IndexBase delegate() {
+ boolean rebuildFromHashInProgress = tbl.rebuildFromHashInProgress();
+
+ if (hashIdx != null)
+ return rebuildFromHashInProgress ? hashIdx : super.delegate();
+ else {
+ assert !rebuildFromHashInProgress;
+
+ return super.delegate();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public double getCost(Session ses, int[] masks, TableFilter[] filters, int filter,
+ SortOrder sortOrder) {
+ long rows = getRowCountApproximation();
+
+ double baseCost = getCostRangeIndex(masks, rows, filters, filter, sortOrder, true);
+
+ int mul = delegate().getDistributedMultiplier(ses, filters, filter);
+
+ return mul * baseCost;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getPlanSQL() {
+ return delegate().getTable().getSQL() + "." + SCAN_INDEX_NAME_SUFFIX;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getName() {
+ return delegate().getName() + SCAN_INDEX_NAME_SUFFIX;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SystemIndexFactory.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SystemIndexFactory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SystemIndexFactory.java
new file mode 100644
index 0000000..f150b6a
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SystemIndexFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.opt;
+
+import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndex;
+import org.h2.index.Index;
+
+import java.util.ArrayList;
+
+/**
+ * Factory for system table indexes.
+ */
+public interface GridH2SystemIndexFactory {
+ /**
+ * Create list of indexes. First must be primary key, after that all unique indexes and
+ * only then non-unique indexes.
+ * All indexes must be subtypes of {@link H2TreeIndex}.
+ *
+ * @param tbl Table to create indexes for.
+ * @return List of indexes.
+ */
+ ArrayList<Index> createSystemIndexes(GridH2Table tbl);
+}
[8/8] ignite git commit: IGNITE-4565: Implemented CREATE INDEX and
DROP INDEX. This closes #1773. This closes #1804.
Posted by vo...@apache.org.
IGNITE-4565: Implemented CREATE INDEX and DROP INDEX. This closes #1773. This closes #1804.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/2edb935c
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/2edb935c
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/2edb935c
Branch: refs/heads/ignite-2.0
Commit: 2edb935cbf87198993c403724e26efc655710c25
Parents: 9e7421f
Author: devozerov <vo...@gridgain.com>
Authored: Tue Apr 18 17:11:34 2017 +0300
Committer: devozerov <vo...@gridgain.com>
Committed: Tue Apr 18 17:11:36 2017 +0300
----------------------------------------------------------------------
.../jdbc2/JdbcAbstractDmlStatementSelfTest.java | 92 +-
...BinaryMarshallerInsertStatementSelfTest.java | 9 +-
...cBinaryMarshallerMergeStatementSelfTest.java | 9 +-
.../jdbc2/JdbcDynamicIndexAbstractSelfTest.java | 367 ++++
...namicIndexAtomicPartitionedNearSelfTest.java | 26 +
...bcDynamicIndexAtomicPartitionedSelfTest.java | 39 +
...dbcDynamicIndexAtomicReplicatedSelfTest.java | 39 +
...dexTransactionalPartitionedNearSelfTest.java | 26 +
...icIndexTransactionalPartitionedSelfTest.java | 39 +
...micIndexTransactionalReplicatedSelfTest.java | 39 +
.../jdbc2/JdbcInsertStatementSelfTest.java | 4 +-
.../jdbc/suite/IgniteJdbcDriverTestSuite.java | 8 +
.../apache/ignite/IgniteSystemProperties.java | 8 +
.../org/apache/ignite/cache/QueryEntity.java | 82 +-
.../org/apache/ignite/cache/QueryIndex.java | 9 +
.../configuration/CacheConfiguration.java | 7 +-
.../apache/ignite/internal/GridComponent.java | 5 +-
.../ignite/internal/GridKernalContext.java | 7 +
.../ignite/internal/GridKernalContextImpl.java | 13 +-
.../org/apache/ignite/internal/GridTopic.java | 5 +-
.../apache/ignite/internal/IgniteKernal.java | 27 +-
.../org/apache/ignite/internal/IgnitionEx.java | 18 +
.../managers/communication/GridIoManager.java | 2 +
.../communication/GridIoMessageFactory.java | 6 +
.../managers/communication/GridIoPolicy.java | 3 +
.../cache/CacheAffinitySharedManager.java | 3 +-
.../cache/CachePartitionExchangeWorkerTask.java | 6 +-
.../cache/DynamicCacheChangeRequest.java | 23 +-
.../cache/DynamicCacheDescriptor.java | 50 +-
.../processors/cache/GridCacheEntryEx.java | 13 +-
.../processors/cache/GridCacheMapEntry.java | 17 +
.../GridCachePartitionExchangeManager.java | 36 +-
.../processors/cache/GridCacheProcessor.java | 135 +-
.../cache/GridCacheSharedContext.java | 2 +
.../GridDhtPartitionsExchangeFuture.java | 5 -
.../cache/query/GridCacheQueryManager.java | 7 +-
.../cache/query/IgniteQueryErrorCode.java | 27 +-
.../internal/processors/pool/PoolProcessor.java | 5 +
.../query/GridQueryIndexDescriptor.java | 5 +
.../processors/query/GridQueryIndexing.java | 43 +-
.../processors/query/GridQueryProcessor.java | 1707 ++++++++++++++++--
.../query/GridQueryTypeDescriptor.java | 7 +
.../processors/query/IgniteSQLException.java | 7 +
.../query/QueryIndexDescriptorImpl.java | 42 +-
.../processors/query/QueryIndexKey.java | 85 +
.../internal/processors/query/QuerySchema.java | 168 ++
.../query/QueryTypeDescriptorImpl.java | 150 +-
.../internal/processors/query/QueryUtils.java | 219 ++-
.../query/schema/SchemaExchangeWorkerTask.java | 53 +
.../query/schema/SchemaIndexCacheVisitor.java | 33 +
.../schema/SchemaIndexCacheVisitorClosure.java | 42 +
.../schema/SchemaIndexCacheVisitorImpl.java | 197 ++
.../SchemaIndexOperationCancellationToken.java | 53 +
.../processors/query/schema/SchemaKey.java | 59 +
.../SchemaNodeLeaveExchangeWorkerTask.java | 53 +
.../schema/SchemaOperationClientFuture.java | 52 +
.../query/schema/SchemaOperationException.java | 138 ++
.../query/schema/SchemaOperationManager.java | 292 +++
.../query/schema/SchemaOperationWorker.java | 205 +++
.../message/SchemaAbstractDiscoveryMessage.java | 70 +
.../message/SchemaFinishDiscoveryMessage.java | 98 +
.../message/SchemaOperationStatusMessage.java | 168 ++
.../message/SchemaProposeDiscoveryMessage.java | 133 ++
.../operation/SchemaAbstractOperation.java | 67 +
.../operation/SchemaIndexAbstractOperation.java | 40 +
.../operation/SchemaIndexCreateOperation.java | 91 +
.../operation/SchemaIndexDropOperation.java | 68 +
.../processors/cache/GridCacheTestEntryEx.java | 7 +
.../junits/GridTestKernalContext.java | 1 +
.../query/h2/GridH2IndexingGeoSelfTest.java | 4 +-
.../query/h2/DmlStatementsProcessor.java | 49 +-
.../processors/query/h2/IgniteH2Indexing.java | 678 +++++--
.../query/h2/ddl/DdlStatementsProcessor.java | 208 +++
.../query/h2/opt/GridH2IndexBase.java | 2 +-
.../query/h2/opt/GridH2PrimaryScanIndex.java | 87 +
.../query/h2/opt/GridH2SystemIndexFactory.java | 38 +
.../processors/query/h2/opt/GridH2Table.java | 382 ++--
.../query/h2/opt/GridLuceneIndex.java | 10 +-
.../query/h2/sql/GridSqlCreateIndex.java | 121 ++
.../query/h2/sql/GridSqlDropIndex.java | 82 +
.../query/h2/sql/GridSqlQueryParser.java | 123 ++
.../cache/index/AbstractSchemaSelfTest.java | 512 ++++++
.../DynamicIndexAbstractBasicSelfTest.java | 950 ++++++++++
.../DynamicIndexAbstractConcurrentSelfTest.java | 921 ++++++++++
.../index/DynamicIndexAbstractSelfTest.java | 467 +++++
.../index/DynamicIndexClientBasicSelfTest.java | 28 +
...ndexPartitionedAtomicConcurrentSelfTest.java | 33 +
...titionedTransactionalConcurrentSelfTest.java | 33 +
...IndexReplicatedAtomicConcurrentSelfTest.java | 33 +
...plicatedTransactionalConcurrentSelfTest.java | 33 +
.../index/DynamicIndexServerBasicSelfTest.java | 28 +
...amicIndexServerCoordinatorBasicSelfTest.java | 28 +
...namicIndexServerNodeFIlterBasicSelfTest.java | 28 +
...erverNodeFilterCoordinatorBasicSelfTest.java | 30 +
.../index/H2DynamicIndexAbstractSelfTest.java | 400 ++++
...namicIndexAtomicPartitionedNearSelfTest.java | 26 +
...H2DynamicIndexAtomicPartitionedSelfTest.java | 39 +
.../H2DynamicIndexAtomicReplicatedSelfTest.java | 39 +
...dexTransactionalPartitionedNearSelfTest.java | 26 +
...icIndexTransactionalPartitionedSelfTest.java | 39 +
...micIndexTransactionalReplicatedSelfTest.java | 39 +
.../cache/index/SchemaExchangeSelfTest.java | 589 ++++++
.../local/IgniteCacheLocalQuerySelfTest.java | 2 +-
.../query/IgniteQueryDedicatedPoolTest.java | 1 -
.../query/IgniteSqlSplitterSelfTest.java | 2 +-
.../h2/GridIndexingSpiAbstractSelfTest.java | 109 +-
.../query/h2/IgniteSqlQueryMinMaxTest.java | 16 +-
.../query/h2/opt/GridH2TableSelfTest.java | 171 +-
.../query/h2/sql/GridQueryParsingTest.java | 212 ++-
.../IgniteCacheQuerySelfTestSuite.java | 37 +-
.../IgniteCacheQuerySelfTestSuite2.java | 11 +
111 files changed, 11221 insertions(+), 1016 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractDmlStatementSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractDmlStatementSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractDmlStatementSelfTest.java
index 440f6d0..f23dde7 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractDmlStatementSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractDmlStatementSelfTest.java
@@ -21,17 +21,10 @@ import java.io.Serializable;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Collections;
-import org.apache.ignite.cache.CachePeekMode;
import org.apache.ignite.cache.QueryEntity;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.configuration.ConnectorConfiguration;
-import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.internal.binary.BinaryMarshaller;
import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
-import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
-import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import static org.apache.ignite.IgniteJdbcDriver.CFG_URL_PREFIX;
@@ -42,9 +35,6 @@ import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
* Statement test.
*/
public abstract class JdbcAbstractDmlStatementSelfTest extends GridCommonAbstractTest {
- /** IP finder. */
- private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
-
/** JDBC URL. */
private static final String BASE_URL = CFG_URL_PREFIX + "modules/clients/src/test/config/jdbc-config.xml";
@@ -58,18 +48,28 @@ public abstract class JdbcAbstractDmlStatementSelfTest extends GridCommonAbstrac
protected Connection conn;
/** {@inheritDoc} */
- @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
- return getConfiguration0(igniteInstanceName);
+ @Override protected void beforeTestsStarted() throws Exception {
+ startGridsMultiThreaded(3);
+
+ Class.forName("org.apache.ignite.IgniteJdbcDriver");
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ stopAllGrids();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ conn = DriverManager.getConnection(getCfgUrl());
+
+ ignite(0).getOrCreateCache(cacheConfig());
}
/**
- * @param igniteInstanceName Ignite instance name.
- * @return Grid configuration used for starting the grid.
- * @throws Exception If failed.
+ * @return Cache configuration for non binary marshaller tests.
*/
- private IgniteConfiguration getConfiguration0(String igniteInstanceName) throws Exception {
- IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
-
+ private CacheConfiguration nonBinCacheConfig() {
CacheConfiguration<?,?> cache = defaultCacheConfiguration();
cache.setCacheMode(PARTITIONED);
@@ -79,32 +79,18 @@ public abstract class JdbcAbstractDmlStatementSelfTest extends GridCommonAbstrac
String.class, Person.class
);
- cfg.setCacheConfiguration(cache);
-
- TcpDiscoverySpi disco = new TcpDiscoverySpi();
-
- disco.setIpFinder(IP_FINDER);
-
- cfg.setDiscoverySpi(disco);
-
- cfg.setConnectorConfiguration(new ConnectorConfiguration());
-
- return cfg;
+ return cache;
}
/**
- * @param igniteInstanceName Ignite instance name.
- * @return Grid configuration used for starting the grid ready for manipulating binary objects.
- * @throws Exception If failed.
+ * @return Cache configuration for binary marshaller tests.
*/
- IgniteConfiguration getBinaryConfiguration(String igniteInstanceName) throws Exception {
- IgniteConfiguration cfg = getConfiguration0(igniteInstanceName);
-
- cfg.setMarshaller(new BinaryMarshaller());
-
- CacheConfiguration ccfg = cfg.getCacheConfiguration()[0];
+ final CacheConfiguration binaryCacheConfig() {
+ CacheConfiguration<?,?> cache = defaultCacheConfiguration();
- ccfg.getQueryEntities().clear();
+ cache.setCacheMode(PARTITIONED);
+ cache.setBackups(1);
+ cache.setWriteSynchronizationMode(FULL_SYNC);
QueryEntity e = new QueryEntity();
@@ -116,26 +102,16 @@ public abstract class JdbcAbstractDmlStatementSelfTest extends GridCommonAbstrac
e.addQueryField("firstName", String.class.getName(), null);
e.addQueryField("lastName", String.class.getName(), null);
- ccfg.setQueryEntities(Collections.singletonList(e));
-
- return cfg;
- }
-
- /** {@inheritDoc} */
- @Override protected void beforeTestsStarted() throws Exception {
- startGridsMultiThreaded(3);
+ cache.setQueryEntities(Collections.singletonList(e));
- Class.forName("org.apache.ignite.IgniteJdbcDriver");
+ return cache;
}
- /** {@inheritDoc} */
- @Override protected void afterTestsStopped() throws Exception {
- stopAllGrids();
- }
-
- /** {@inheritDoc} */
- @Override protected void beforeTest() throws Exception {
- conn = DriverManager.getConnection(getCfgUrl());
+ /**
+ * @return Configuration of cache to create.
+ */
+ CacheConfiguration cacheConfig() {
+ return nonBinCacheConfig();
}
/**
@@ -147,9 +123,7 @@ public abstract class JdbcAbstractDmlStatementSelfTest extends GridCommonAbstrac
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
- grid(0).cache(null).clear();
-
- assertEquals(0, grid(0).cache(null).size(CachePeekMode.ALL));
+ grid(0).destroyCache(null);
conn.close();
assertTrue(conn.isClosed());
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBinaryMarshallerInsertStatementSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBinaryMarshallerInsertStatementSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBinaryMarshallerInsertStatementSelfTest.java
index e8a09d9..878e4de 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBinaryMarshallerInsertStatementSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBinaryMarshallerInsertStatementSelfTest.java
@@ -17,7 +17,9 @@
package org.apache.ignite.internal.jdbc2;
+import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.binary.BinaryMarshaller;
/**
* JDBC test of INSERT statement w/binary marshaller - no nodes know about classes.
@@ -30,6 +32,11 @@ public class JdbcBinaryMarshallerInsertStatementSelfTest extends JdbcInsertState
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
- return getBinaryConfiguration(igniteInstanceName);
+ return super.getConfiguration(igniteInstanceName).setMarshaller(new BinaryMarshaller());
+ }
+
+ /** {@inheritDoc} */
+ @Override CacheConfiguration cacheConfig() {
+ return binaryCacheConfig();
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBinaryMarshallerMergeStatementSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBinaryMarshallerMergeStatementSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBinaryMarshallerMergeStatementSelfTest.java
index 5e4b559..8b4d3c7 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBinaryMarshallerMergeStatementSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBinaryMarshallerMergeStatementSelfTest.java
@@ -17,7 +17,9 @@
package org.apache.ignite.internal.jdbc2;
+import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.binary.BinaryMarshaller;
/**
* JDBC test of MERGE statement w/binary marshaller - no nodes know about classes.
@@ -30,6 +32,11 @@ public class JdbcBinaryMarshallerMergeStatementSelfTest extends JdbcMergeStateme
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
- return getBinaryConfiguration(igniteInstanceName);
+ return super.getConfiguration(igniteInstanceName).setMarshaller(new BinaryMarshaller());
+ }
+
+ /** {@inheritDoc} */
+ @Override CacheConfiguration cacheConfig() {
+ return binaryCacheConfig();
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAbstractSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAbstractSelfTest.java
new file mode 100644
index 0000000..84ffc28
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAbstractSelfTest.java
@@ -0,0 +1,367 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.jdbc2;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Collections;
+import java.util.List;
+import javax.cache.CacheException;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.NearCacheConfiguration;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.util.typedef.F;
+
+/**
+ * Test that checks indexes handling with JDBC.
+ */
+public abstract class JdbcDynamicIndexAbstractSelfTest extends JdbcAbstractDmlStatementSelfTest {
+ /** */
+ private final static String CREATE_INDEX = "create index idx on Person (id desc)";
+
+ /** */
+ private final static String DROP_INDEX = "drop index idx";
+
+ /** */
+ private final static String CREATE_INDEX_IF_NOT_EXISTS = "create index if not exists idx on Person (id desc)";
+
+ /** */
+ private final static String DROP_INDEX_IF_EXISTS = "drop index idx if exists";
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ try (PreparedStatement ps =
+ conn.prepareStatement("INSERT INTO Person (_key, id, age, firstName, lastName) values (?, ?, ?, ?, ?)")) {
+
+ ps.setString(1, "j");
+ ps.setInt(2, 1);
+ ps.setInt(3, 10);
+ ps.setString(4, "John");
+ ps.setString(5, "Smith");
+ ps.executeUpdate();
+
+ ps.setString(1, "m");
+ ps.setInt(2, 2);
+ ps.setInt(3, 20);
+ ps.setString(4, "Mark");
+ ps.setString(5, "Stone");
+ ps.executeUpdate();
+
+ ps.setString(1, "s");
+ ps.setInt(2, 3);
+ ps.setInt(3, 30);
+ ps.setString(4, "Sarah");
+ ps.setString(5, "Pazzi");
+ ps.executeUpdate();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override CacheConfiguration cacheConfig() {
+ CacheConfiguration ccfg = super.cacheConfig();
+
+ ccfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
+
+ ccfg.setCacheMode(cacheMode());
+ ccfg.setAtomicityMode(atomicityMode());
+
+ if (nearCache())
+ ccfg.setNearConfiguration(new NearCacheConfiguration());
+
+ return ccfg;
+ }
+
+ /**
+ * @return Cache mode to use.
+ */
+ protected abstract CacheMode cacheMode();
+
+ /**
+ * @return Cache atomicity mode to use.
+ */
+ protected abstract CacheAtomicityMode atomicityMode();
+
+ /**
+ * @return Whether to use near cache.
+ */
+ protected abstract boolean nearCache();
+
+ /**
+ * Execute given SQL statement.
+ * @param sql Statement.
+ * @throws SQLException if failed.
+ */
+ private void jdbcRun(String sql) throws SQLException {
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute(sql);
+ }
+ }
+
+ /** */
+ private Object getSingleValue(ResultSet rs) throws SQLException {
+ assertEquals(1, rs.getMetaData().getColumnCount());
+
+ assertTrue(rs.next());
+
+ Object res = rs.getObject(1);
+
+ assertTrue(rs.isLast());
+
+ return res;
+ }
+
+ /**
+ * Test that after index creation index is used by queries.
+ */
+ public void testCreateIndex() throws SQLException {
+ assertSize(3);
+
+ assertColumnValues(30, 20, 10);
+
+ jdbcRun(CREATE_INDEX);
+
+ // Test that local queries on all server nodes use new index.
+ for (int i = 0 ; i < 3; i++) {
+ List<List<?>> locRes = ignite(i).cache(null).query(new SqlFieldsQuery("explain select id from " +
+ "Person where id = 5").setLocal(true)).getAll();
+
+ assertEquals(F.asList(
+ Collections.singletonList("SELECT\n" +
+ " ID\n" +
+ "FROM \"\".PERSON\n" +
+ " /* \"\".IDX: ID = 5 */\n" +
+ "WHERE ID = 5")
+ ), locRes);
+ }
+
+ assertSize(3);
+
+ assertColumnValues(30, 20, 10);
+ }
+
+ /**
+ * Test that creating an index with duplicate name yields an error.
+ */
+ public void testCreateIndexWithDuplicateName() throws SQLException {
+ jdbcRun(CREATE_INDEX);
+
+ assertSqlException(new RunnableX() {
+ /** {@inheritDoc} */
+ @Override public void run() throws Exception {
+ jdbcRun(CREATE_INDEX);
+ }
+ }, IgniteQueryErrorCode.INDEX_ALREADY_EXISTS);
+ }
+
+ /**
+ * Test that creating an index with duplicate name does not yield an error with {@code IF NOT EXISTS}.
+ */
+ public void testCreateIndexIfNotExists() throws SQLException {
+ jdbcRun(CREATE_INDEX);
+
+ // Despite duplicate name, this does not yield an error.
+ jdbcRun(CREATE_INDEX_IF_NOT_EXISTS);
+ }
+
+ /**
+ * Test that after index drop there are no attempts to use it, and data state remains intact.
+ */
+ public void testDropIndex() throws SQLException {
+ assertSize(3);
+
+ jdbcRun(CREATE_INDEX);
+
+ assertSize(3);
+
+ jdbcRun(DROP_INDEX);
+
+ // Test that no local queries on server nodes use new index.
+ for (int i = 0 ; i < 3; i++) {
+ List<List<?>> locRes = ignite(i).cache(null).query(new SqlFieldsQuery("explain select id from " +
+ "Person where id = 5").setLocal(true)).getAll();
+
+ assertEquals(F.asList(
+ Collections.singletonList("SELECT\n" +
+ " ID\n" +
+ "FROM \"\".PERSON\n" +
+ " /* \"\".PERSON.__SCAN_ */\n" +
+ "WHERE ID = 5")
+ ), locRes);
+ }
+
+ assertSize(3);
+ }
+
+ /**
+ * Test that dropping a non-existent index yields an error.
+ */
+ public void testDropMissingIndex() {
+ assertSqlException(new RunnableX() {
+ /** {@inheritDoc} */
+ @Override public void run() throws Exception {
+ jdbcRun(DROP_INDEX);
+ }
+ }, IgniteQueryErrorCode.INDEX_NOT_FOUND);
+ }
+
+ /**
+ * Test that dropping a non-existent index does not yield an error with {@code IF EXISTS}.
+ */
+ public void testDropMissingIndexIfExists() throws SQLException {
+ // Despite index missing, this does not yield an error.
+ jdbcRun(DROP_INDEX_IF_EXISTS);
+ }
+
+ /**
+ * Test that changes in cache affect index, and vice versa.
+ */
+ public void testIndexState() throws SQLException {
+ IgniteCache<String, Person> cache = cache();
+
+ assertSize(3);
+
+ assertColumnValues(30, 20, 10);
+
+ jdbcRun(CREATE_INDEX);
+
+ assertSize(3);
+
+ assertColumnValues(30, 20, 10);
+
+ cache.remove("m");
+
+ assertColumnValues(30, 10);
+
+ cache.put("a", new Person(4, "someVal", "a", 5));
+
+ assertColumnValues(5, 30, 10);
+
+ jdbcRun(DROP_INDEX);
+
+ assertColumnValues(5, 30, 10);
+ }
+
+ /**
+ * Check that values of {@code field1} match what we expect.
+ * @param vals Expected values.
+ */
+ private void assertColumnValues(int... vals) throws SQLException {
+ try (Statement stmt = conn.createStatement()) {
+ try (ResultSet rs = stmt.executeQuery("SELECT age FROM Person ORDER BY id desc")) {
+ assertEquals(1, rs.getMetaData().getColumnCount());
+
+ for (int i = 0; i < vals.length; i++) {
+ assertTrue("Result set must have " + vals.length + " rows, got " + i, rs.next());
+
+ assertEquals(vals[i], rs.getInt(1));
+ }
+
+ assertFalse("Result set must have exactly " + vals.length + " rows", rs.next());
+ }
+ }
+ }
+
+ /**
+ * Do a {@code SELECT COUNT(*)} query to check index state correctness.
+ * @param expSize Expected number of items in table.
+ */
+ private void assertSize(long expSize) throws SQLException {
+ assertEquals(expSize, cache().size());
+
+ try (Statement stmt = conn.createStatement()) {
+ try (ResultSet rs = stmt.executeQuery("SELECT COUNT(*) from Person")) {
+ assertEquals(expSize, getSingleValue(rs));
+ }
+ }
+ }
+
+ /**
+ * @return Cache.
+ */
+ private IgniteCache<String, Person> cache() {
+ return grid(0).cache(null);
+ }
+
+ /**
+ * Ensure that SQL exception is thrown.
+ *
+ * @param r Runnable.
+ * @param expCode Error code.
+ */
+ private static void assertSqlException(RunnableX r, int expCode) {
+ // We expect IgniteSQLException with given code inside CacheException inside JDBC SQLException.
+
+ try {
+ r.run();
+ }
+ catch (SQLException ex) {
+ if (ex.getCause() != null) {
+ try {
+ throw ex.getCause();
+ }
+ catch (CacheException ex1) {
+ if (ex1.getCause() != null) {
+ try {
+ throw ex1.getCause();
+ }
+ catch (IgniteSQLException e) {
+ assertEquals("Unexpected error code [expected=" + expCode + ", actual=" + e.statusCode() + ']',
+ expCode, e.statusCode());
+
+ return;
+ }
+ catch (Throwable t) {
+ fail("Unexpected exception: " + t);
+ }
+ }
+ }
+ catch (Throwable t) {
+ fail("Unexpected exception: " + t);
+ }
+ }
+ }
+ catch (Exception e) {
+ fail("Unexpected exception: " + e);
+ }
+
+ fail(IgniteSQLException.class.getSimpleName() + " is not thrown.");
+ }
+
+ /**
+ * Runnable which can throw checked exceptions.
+ */
+ private interface RunnableX {
+ /**
+ * Do run.
+ *
+ * @throws Exception If failed.
+ */
+ public void run() throws Exception;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAtomicPartitionedNearSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAtomicPartitionedNearSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAtomicPartitionedNearSelfTest.java
new file mode 100644
index 0000000..c2b5011
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAtomicPartitionedNearSelfTest.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.jdbc2;
+
+/** */
+public class JdbcDynamicIndexAtomicPartitionedNearSelfTest extends JdbcDynamicIndexAtomicPartitionedSelfTest {
+ /** {@inheritDoc} */
+ @Override protected boolean nearCache() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAtomicPartitionedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAtomicPartitionedSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAtomicPartitionedSelfTest.java
new file mode 100644
index 0000000..41e07e7
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAtomicPartitionedSelfTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.jdbc2;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+
+/** */
+public class JdbcDynamicIndexAtomicPartitionedSelfTest extends JdbcDynamicIndexAbstractSelfTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.PARTITIONED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected CacheAtomicityMode atomicityMode() {
+ return CacheAtomicityMode.ATOMIC;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean nearCache() {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAtomicReplicatedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAtomicReplicatedSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAtomicReplicatedSelfTest.java
new file mode 100644
index 0000000..7a5b015
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexAtomicReplicatedSelfTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.jdbc2;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+
+/** */
+public class JdbcDynamicIndexAtomicReplicatedSelfTest extends JdbcDynamicIndexAbstractSelfTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.REPLICATED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected CacheAtomicityMode atomicityMode() {
+ return CacheAtomicityMode.ATOMIC;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean nearCache() {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexTransactionalPartitionedNearSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexTransactionalPartitionedNearSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexTransactionalPartitionedNearSelfTest.java
new file mode 100644
index 0000000..2815dff
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexTransactionalPartitionedNearSelfTest.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.jdbc2;
+
+/** */
+public class JdbcDynamicIndexTransactionalPartitionedNearSelfTest extends JdbcDynamicIndexTransactionalPartitionedSelfTest {
+ /** {@inheritDoc} */
+ @Override protected boolean nearCache() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexTransactionalPartitionedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexTransactionalPartitionedSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexTransactionalPartitionedSelfTest.java
new file mode 100644
index 0000000..47b257f
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexTransactionalPartitionedSelfTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.jdbc2;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+
+/** */
+public class JdbcDynamicIndexTransactionalPartitionedSelfTest extends JdbcDynamicIndexAbstractSelfTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.PARTITIONED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected CacheAtomicityMode atomicityMode() {
+ return CacheAtomicityMode.TRANSACTIONAL;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean nearCache() {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexTransactionalReplicatedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexTransactionalReplicatedSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexTransactionalReplicatedSelfTest.java
new file mode 100644
index 0000000..9b135d8
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDynamicIndexTransactionalReplicatedSelfTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.jdbc2;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+
+/** */
+public class JdbcDynamicIndexTransactionalReplicatedSelfTest extends JdbcDynamicIndexAbstractSelfTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.REPLICATED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected CacheAtomicityMode atomicityMode() {
+ return CacheAtomicityMode.TRANSACTIONAL;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean nearCache() {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java
index 1bd6d34..9e01bc7 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java
@@ -24,8 +24,8 @@ import java.sql.Statement;
import java.util.Arrays;
import java.util.HashSet;
import java.util.concurrent.Callable;
-import org.apache.ignite.IgniteException;
import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.testframework.GridTestUtils;
/**
@@ -164,7 +164,7 @@ public class JdbcInsertStatementSelfTest extends JdbcAbstractDmlStatementSelfTes
assertNotNull(reason);
- assertEquals(IgniteException.class, reason.getClass());
+ assertEquals(IgniteSQLException.class, reason.getClass());
assertEquals("Failed to INSERT some keys because they are already in cache [keys=[p2]]", reason.getMessage());
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
index 2489de9..75671de 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
@@ -73,6 +73,14 @@ public class IgniteJdbcDriverTestSuite extends TestSuite {
suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDeleteStatementSelfTest.class));
suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcStreamingSelfTest.class));
+ // DDL tests.
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDynamicIndexAtomicPartitionedNearSelfTest.class));
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDynamicIndexAtomicPartitionedSelfTest.class));
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDynamicIndexAtomicReplicatedSelfTest.class));
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDynamicIndexTransactionalPartitionedNearSelfTest.class));
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDynamicIndexTransactionalPartitionedSelfTest.class));
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDynamicIndexTransactionalReplicatedSelfTest.class));
+
return suite;
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
index 1216db8..37e8c6b 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
@@ -575,6 +575,14 @@ public final class IgniteSystemProperties {
*/
public static final String IGNITE_MAX_INDEX_PAYLOAD_SIZE = "IGNITE_MAX_INDEX_PAYLOAD_SIZE";
+ /**
+ * Indexing discovery history size. Protects from duplicate messages maintaining the list of IDs of recently
+ * arrived discovery messages.
+ * <p>
+ * Defaults to {@code 1000}.
+ */
+ public static final String IGNITE_INDEXING_DISCOVERY_HISTORY_SIZE = "IGNITE_INDEXING_DISCOVERY_HISTORY_SIZE";
+
/** Returns true for system properties only avoiding sending sensitive information. */
private static final IgnitePredicate<Map.Entry<String, String>> PROPS_FILTER = new IgnitePredicate<Map.Entry<String, String>>() {
@Override public boolean apply(final Map.Entry<String, String> entry) {
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
index 9f4313e..31fe264 100644
--- a/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
+++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
@@ -20,9 +20,12 @@ package org.apache.ignite.cache;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
+
+import org.apache.ignite.internal.processors.query.QueryUtils;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.A;
@@ -63,6 +66,24 @@ public class QueryEntity implements Serializable {
}
/**
+ * Copy constructor.
+ *
+ * @param other Other entity.
+ */
+ public QueryEntity(QueryEntity other) {
+ keyType = other.keyType;
+ valType = other.valType;
+
+ fields = new LinkedHashMap<>(other.fields);
+ keyFields = other.keyFields != null ? new HashSet<>(other.keyFields) : null;
+
+ aliases = new HashMap<>(other.aliases);
+ idxs = new HashMap<>(other.idxs);
+
+ tableName = other.tableName;
+ }
+
+ /**
* Creates a query entity with the given key and value types.
*
* @param keyType Key type.
@@ -204,7 +225,7 @@ public class QueryEntity implements Serializable {
for (QueryIndex idx : idxs) {
if (!F.isEmpty(idx.getFields())) {
if (idx.getName() == null)
- idx.setName(defaultIndexName(idx));
+ idx.setName(QueryUtils.indexName(this, idx));
if (idx.getIndexType() == null)
throw new IllegalArgumentException("Index type is not set " + idx.getName());
@@ -220,6 +241,13 @@ public class QueryEntity implements Serializable {
}
/**
+ * Clear indexes.
+ */
+ public void clearIndexes() {
+ this.idxs.clear();
+ }
+
+ /**
* Gets table name for this query entity.
*
* @return table name
@@ -254,56 +282,4 @@ public class QueryEntity implements Serializable {
return this;
}
-
- /**
- * Ensures that index with the given name exists.
- *
- * @param idxName Index name.
- * @param idxType Index type.
- */
- public void ensureIndex(String idxName, QueryIndexType idxType) {
- QueryIndex idx = idxs.get(idxName);
-
- if (idx == null) {
- idx = new QueryIndex();
-
- idx.setName(idxName);
- idx.setIndexType(idxType);
-
- idxs.put(idxName, idx);
- }
- else
- throw new IllegalArgumentException("An index with the same name and of a different type already exists " +
- "[idxName=" + idxName + ", existingIdxType=" + idx.getIndexType() + ", newIdxType=" + idxType + ']');
- }
-
- /**
- * Generates default index name by concatenating all index field names.
- *
- * @param idx Index to build name for.
- * @return Index name.
- */
- public static String defaultIndexName(QueryIndex idx) {
- StringBuilder idxName = new StringBuilder();
-
- for (Map.Entry<String, Boolean> field : idx.getFields().entrySet()) {
- idxName.append(field.getKey());
-
- idxName.append('_');
- idxName.append(field.getValue() ? "asc_" : "desc_");
- }
-
- for (int i = 0; i < idxName.length(); i++) {
- char ch = idxName.charAt(i);
-
- if (Character.isWhitespace(ch))
- idxName.setCharAt(i, '_');
- else
- idxName.setCharAt(i, Character.toLowerCase(ch));
- }
-
- idxName.append("idx");
-
- return idxName.toString();
- }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java b/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java
index 750d3e1..555a006 100644
--- a/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java
+++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java
@@ -16,6 +16,9 @@
*/
package org.apache.ignite.cache;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
@@ -38,6 +41,7 @@ public class QueryIndex implements Serializable {
private String name;
/** */
+ @GridToStringInclude
private LinkedHashMap<String, Boolean> fields;
/** */
@@ -260,4 +264,9 @@ public class QueryIndex implements Serializable {
public void setInlineSize(int inlineSize) {
this.inlineSize = inlineSize;
}
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(QueryIndex.class, this);
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
index 2308a10..a2f7cc8 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
@@ -2096,7 +2096,7 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
@Nullable ClassProperty parent) {
if (U.isJdk(cls) || QueryUtils.isGeometryClass(cls)) {
if (parent == null && !key && QueryUtils.isSqlType(cls)) { // We have to index primitive _val.
- String idxName = QueryUtils._VAL + "_idx";
+ String idxName = cls.getSimpleName() + "_" + QueryUtils._VAL + "_idx";
type.addIndex(idxName, QueryUtils.isGeometryClass(cls) ?
QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED);
@@ -2527,6 +2527,11 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
}
/** {@inheritDoc} */
+ @Override public String name() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
@Override public Collection<String> fields() {
Collection<String> res = new ArrayList<>(fields.size());
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java b/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java
index be9a1d6..98edf0f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java
@@ -58,7 +58,10 @@ public interface GridComponent {
MARSHALLER_PROC,
/** */
- BINARY_PROC
+ BINARY_PROC,
+
+ /** Query processor. */
+ QUERY_PROC
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
index 6fefb68..8462e5f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
@@ -564,6 +564,13 @@ public interface GridKernalContext extends Iterable<GridComponent> {
public ExecutorService getQueryExecutorService();
/**
+ * Executor service that is in charge of processing schema change messages.
+ *
+ * @return Executor service that is in charge of processing schema change messages.
+ */
+ public ExecutorService getSchemaExecutorService();
+
+ /**
* Gets exception registry.
*
* @return Exception registry.
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
index 664e47c..213cf86 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
@@ -340,6 +340,10 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
/** */
@GridToStringExclude
+ protected ExecutorService schemaExecSvc;
+
+ /** */
+ @GridToStringExclude
private Map<String, Object> attrs = new HashMap<>();
/** */
@@ -396,8 +400,8 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
* @param idxExecSvc Indexing executor service.
* @param callbackExecSvc Callback executor service.
* @param qryExecSvc Query executor service.
+ * @param schemaExecSvc Schema executor service.
* @param plugins Plugin providers.
- * @throws IgniteCheckedException In case of error.
*/
@SuppressWarnings("TypeMayBeWeakened")
protected GridKernalContextImpl(
@@ -419,6 +423,7 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
@Nullable ExecutorService idxExecSvc,
IgniteStripedThreadPoolExecutor callbackExecSvc,
ExecutorService qryExecSvc,
+ ExecutorService schemaExecSvc,
List<PluginProvider> plugins
) {
assert grid != null;
@@ -442,6 +447,7 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
this.idxExecSvc = idxExecSvc;
this.callbackExecSvc = callbackExecSvc;
this.qryExecSvc = qryExecSvc;
+ this.schemaExecSvc = schemaExecSvc;
marshCtx = new MarshallerContextImpl(plugins);
@@ -987,6 +993,11 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
}
/** {@inheritDoc} */
+ @Override public ExecutorService getSchemaExecutorService() {
+ return schemaExecSvc;
+ }
+
+ /** {@inheritDoc} */
@Override public IgniteExceptionRegistry exceptionRegistry() {
return IgniteExceptionRegistry.get();
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/GridTopic.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/GridTopic.java b/modules/core/src/main/java/org/apache/ignite/internal/GridTopic.java
index 7acc070..c382999 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/GridTopic.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/GridTopic.java
@@ -108,7 +108,10 @@ public enum GridTopic {
TOPIC_HADOOP_MSG,
/** */
- TOPIC_METADATA_REQ;
+ TOPIC_METADATA_REQ,
+
+ /** */
+ TOPIC_SCHEMA;
/** Enum values. */
private static final GridTopic[] VALS = values();
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
index 9b41b58..922dd55 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
@@ -311,6 +311,10 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
/** */
@GridToStringExclude
+ private ObjectName schemaExecSvcMBean;
+
+ /** */
+ @GridToStringExclude
private ObjectName stripedExecSvcMBean;
/** Kernal start timestamp. */
@@ -694,6 +698,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
* @param idxExecSvc Indexing executor service.
* @param callbackExecSvc Callback executor service.
* @param qryExecSvc Query executor service.
+ * @param schemaExecSvc Schema executor service.
* @param errHnd Error handler to use for notification about startup problems.
* @throws IgniteCheckedException Thrown in case of any errors.
*/
@@ -714,6 +719,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
@Nullable ExecutorService idxExecSvc,
IgniteStripedThreadPoolExecutor callbackExecSvc,
ExecutorService qryExecSvc,
+ ExecutorService schemaExecSvc,
GridAbsClosure errHnd
)
throws IgniteCheckedException
@@ -828,6 +834,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
idxExecSvc,
callbackExecSvc,
qryExecSvc,
+ schemaExecSvc,
plugins
);
@@ -1019,7 +1026,9 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
// Register MBeans.
registerKernalMBean();
registerLocalNodeMBean();
- registerExecutorMBeans(execSvc, sysExecSvc, p2pExecSvc, mgmtExecSvc, restExecSvc, qryExecSvc);
+ registerExecutorMBeans(execSvc, sysExecSvc, p2pExecSvc, mgmtExecSvc, restExecSvc, qryExecSvc,
+ schemaExecSvc);
+
registerStripedExecutorMBean(stripedExecSvc);
// Lifecycle bean notifications.
@@ -1611,11 +1620,12 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
}
/**
- * @param execSvc
- * @param sysExecSvc
- * @param p2pExecSvc
- * @param mgmtExecSvc
- * @param restExecSvc
+ * @param execSvc Public executor service.
+ * @param sysExecSvc System executor service.
+ * @param p2pExecSvc P2P executor service.
+ * @param mgmtExecSvc Management executor service.
+ * @param restExecSvc Query executor service.
+ * @param schemaExecSvc Schema executor service.
* @throws IgniteCheckedException If failed.
*/
private void registerExecutorMBeans(ExecutorService execSvc,
@@ -1623,12 +1633,14 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
ExecutorService p2pExecSvc,
ExecutorService mgmtExecSvc,
ExecutorService restExecSvc,
- ExecutorService qryExecSvc) throws IgniteCheckedException {
+ ExecutorService qryExecSvc,
+ ExecutorService schemaExecSvc) throws IgniteCheckedException {
pubExecSvcMBean = registerExecutorMBean(execSvc, "GridExecutionExecutor");
sysExecSvcMBean = registerExecutorMBean(sysExecSvc, "GridSystemExecutor");
mgmtExecSvcMBean = registerExecutorMBean(mgmtExecSvc, "GridManagementExecutor");
p2PExecSvcMBean = registerExecutorMBean(p2pExecSvc, "GridClassLoadingExecutor");
qryExecSvcMBean = registerExecutorMBean(qryExecSvc, "GridQueryExecutor");
+ schemaExecSvcMBean = registerExecutorMBean(schemaExecSvc, "GridSchemaExecutor");
ConnectorConfiguration clientCfg = cfg.getConnectorConfiguration();
@@ -2151,6 +2163,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
unregisterMBean(locNodeMBean) &
unregisterMBean(restExecSvcMBean) &
unregisterMBean(qryExecSvcMBean) &
+ unregisterMBean(schemaExecSvcMBean) &
unregisterMBean(stripedExecSvcMBean)
))
errOnStop = false;
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java b/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
index 61e93cf..2eda01c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
@@ -1527,6 +1527,9 @@ public class IgnitionEx {
/** Query executor service. */
private ThreadPoolExecutor qryExecSvc;
+ /** Query executor service. */
+ private ThreadPoolExecutor schemaExecSvc;
+
/** Grid state. */
private volatile IgniteState state = STOPPED;
@@ -1845,6 +1848,16 @@ public class IgnitionEx {
qryExecSvc.allowCoreThreadTimeOut(true);
+ schemaExecSvc = new IgniteThreadPoolExecutor(
+ "schema",
+ cfg.getIgniteInstanceName(),
+ 2,
+ 2,
+ DFLT_THREAD_KEEP_ALIVE_TIME,
+ new LinkedBlockingQueue<Runnable>());
+
+ schemaExecSvc.allowCoreThreadTimeOut(true);
+
// Register Ignite MBean for current grid instance.
registerFactoryMbean(myCfg.getMBeanServer());
@@ -1872,6 +1885,7 @@ public class IgnitionEx {
idxExecSvc,
callbackExecSvc,
qryExecSvc,
+ schemaExecSvc,
new CA() {
@Override public void apply() {
startLatch.countDown();
@@ -2464,6 +2478,10 @@ public class IgnitionEx {
qryExecSvc = null;
+ U.shutdownNow(getClass(), schemaExecSvc, log);
+
+ schemaExecSvc = null;
+
U.shutdownNow(getClass(), stripedExecSvc, log);
stripedExecSvc = null;
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
index b615c35..83fc3b5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
@@ -97,6 +97,7 @@ import static org.apache.ignite.internal.managers.communication.GridIoPolicy.IGF
import static org.apache.ignite.internal.managers.communication.GridIoPolicy.MANAGEMENT_POOL;
import static org.apache.ignite.internal.managers.communication.GridIoPolicy.P2P_POOL;
import static org.apache.ignite.internal.managers.communication.GridIoPolicy.PUBLIC_POOL;
+import static org.apache.ignite.internal.managers.communication.GridIoPolicy.SCHEMA_POOL;
import static org.apache.ignite.internal.managers.communication.GridIoPolicy.SERVICE_POOL;
import static org.apache.ignite.internal.managers.communication.GridIoPolicy.SYSTEM_POOL;
import static org.apache.ignite.internal.managers.communication.GridIoPolicy.UTILITY_CACHE_POOL;
@@ -700,6 +701,7 @@ public class GridIoManager extends GridManagerAdapter<CommunicationSpi<Serializa
case IGFS_POOL:
case DATA_STREAMER_POOL:
case QUERY_POOL:
+ case SCHEMA_POOL:
case SERVICE_POOL:
{
if (msg.isOrdered())
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
index b972a31..17e4a01 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
@@ -137,6 +137,7 @@ import org.apache.ignite.internal.processors.igfs.IgfsFragmentizerResponse;
import org.apache.ignite.internal.processors.igfs.IgfsSyncMessage;
import org.apache.ignite.internal.processors.marshaller.MissingMappingRequestMessage;
import org.apache.ignite.internal.processors.marshaller.MissingMappingResponseMessage;
+import org.apache.ignite.internal.processors.query.schema.message.SchemaOperationStatusMessage;
import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryCancelRequest;
import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryFailResponse;
import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryNextPageRequest;
@@ -177,6 +178,11 @@ public class GridIoMessageFactory implements MessageFactory {
Message msg = null;
switch (type) {
+ case -53:
+ msg = new SchemaOperationStatusMessage();
+
+ break;
+
case -52:
msg = new GridIntList();
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java
index e848633..13bc4c4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java
@@ -55,6 +55,9 @@ public class GridIoPolicy {
/** Pool for service proxy executions. */
public static final byte SERVICE_POOL = 11;
+ /** Schema pool. */
+ public static final byte SCHEMA_POOL = 12;
+
/**
* Defines the range of reserved pools that are not available for plugins.
* @param key The key.
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
index 77e832e..0958208 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
@@ -346,7 +346,8 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
req.startCacheConfiguration(),
req.cacheType(),
false,
- req.deploymentId());
+ req.deploymentId(),
+ req.schema());
DynamicCacheDescriptor old = registeredCaches.put(cacheId, desc);
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CachePartitionExchangeWorkerTask.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CachePartitionExchangeWorkerTask.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CachePartitionExchangeWorkerTask.java
index ca99511..ad0dcc9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CachePartitionExchangeWorkerTask.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CachePartitionExchangeWorkerTask.java
@@ -21,9 +21,5 @@ package org.apache.ignite.internal.processors.cache;
* Cache partition exchange worker task marker interface.
*/
public interface CachePartitionExchangeWorkerTask {
- /**
- * @return {@code True} if task denotes standard exchange task, {@code false} if this is a custom task which
- * must be executed from within exchange thread.
- */
- boolean isExchange();
+ // No-op.
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheChangeRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheChangeRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheChangeRequest.java
index 1e5ab88..9d2563d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheChangeRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheChangeRequest.java
@@ -17,16 +17,18 @@
package org.apache.ignite.internal.processors.cache;
-import java.io.Serializable;
-import java.util.UUID;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.NearCacheConfiguration;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.query.QuerySchema;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.lang.IgniteUuid;
import org.jetbrains.annotations.Nullable;
+import java.io.Serializable;
+import java.util.UUID;
+
/**
* Cache start/stop request.
*/
@@ -83,6 +85,9 @@ public class DynamicCacheChangeRequest implements Serializable {
/** Reset lost partitions flag. */
private boolean resetLostPartitions;
+ /** Dynamic schema. */
+ private QuerySchema schema;
+
/** */
private transient boolean exchangeNeeded;
@@ -353,6 +358,20 @@ public class DynamicCacheChangeRequest implements Serializable {
return rcvdFrom;
}
+ /**
+ * @return Dynamic schema.
+ */
+ public QuerySchema schema() {
+ return schema;
+ }
+
+ /**
+ * @param schema Dynamic schema.
+ */
+ public void schema(QuerySchema schema) {
+ this.schema = schema != null ? schema.copy() : null;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(DynamicCacheChangeRequest.class, this, "cacheName", cacheName());
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java
index 5938785..92a7af3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java
@@ -24,6 +24,8 @@ import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.plugin.CachePluginManager;
+import org.apache.ignite.internal.processors.query.QuerySchema;
+import org.apache.ignite.internal.processors.query.schema.message.SchemaFinishDiscoveryMessage;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.S;
@@ -84,6 +86,12 @@ public class DynamicCacheDescriptor {
/** */
private transient AffinityTopologyVersion clientCacheStartVer;
+ /** Mutex to control schema. */
+ private final Object schemaMux = new Object();
+
+ /** Current schema. */
+ private QuerySchema schema;
+
/**
* @param ctx Context.
* @param cacheCfg Cache configuration.
@@ -91,12 +99,15 @@ public class DynamicCacheDescriptor {
* @param template {@code True} if this is template configuration.
* @param deploymentId Deployment ID.
*/
+ @SuppressWarnings("unchecked")
public DynamicCacheDescriptor(GridKernalContext ctx,
CacheConfiguration cacheCfg,
CacheType cacheType,
boolean template,
- IgniteUuid deploymentId) {
+ IgniteUuid deploymentId,
+ QuerySchema schema) {
assert cacheCfg != null;
+ assert schema != null;
this.cacheCfg = cacheCfg;
this.cacheType = cacheType;
@@ -106,6 +117,10 @@ public class DynamicCacheDescriptor {
pluginMgr = new CachePluginManager(ctx, cacheCfg);
cacheId = CU.cacheId(cacheCfg.getName());
+
+ synchronized (schemaMux) {
+ this.schema = schema.copy();
+ }
}
/**
@@ -319,6 +334,39 @@ public class DynamicCacheDescriptor {
this.clientCacheStartVer = clientCacheStartVer;
}
+ /**
+ * @return Schema.
+ */
+ public QuerySchema schema() {
+ synchronized (schemaMux) {
+ return schema.copy();
+ }
+ }
+
+ /**
+ * Set schema
+ *
+ * @param schema Schema.
+ */
+ public void schema(QuerySchema schema) {
+ assert schema != null;
+
+ synchronized (schemaMux) {
+ this.schema = schema.copy();
+ }
+ }
+
+ /**
+ * Try applying finish message.
+ *
+ * @param msg Message.
+ */
+ public void schemaChangeFinish(SchemaFinishDiscoveryMessage msg) {
+ synchronized (schemaMux) {
+ schema.finish(msg);
+ }
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(DynamicCacheDescriptor.class, this, "cacheName", U.maskName(cacheCfg.getName()));
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java
index 2066342..1eab04e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java
@@ -33,8 +33,8 @@ import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersionedEntryEx;
import org.apache.ignite.internal.processors.dr.GridDrType;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
import org.apache.ignite.internal.util.lang.GridTuple3;
-import org.apache.ignite.internal.util.typedef.T2;
import org.jetbrains.annotations.Nullable;
/**
@@ -902,6 +902,17 @@ public interface GridCacheEntryEx {
throws IgniteCheckedException, GridCacheEntryRemovedException;
/**
+ * Update index from within entry lock, passing key, value, and expiration time to provided closure.
+ *
+ * @param clo Closure to apply to key, value, and expiration time.
+ * @param link Link.
+ * @throws IgniteCheckedException If failed.
+ * @throws GridCacheEntryRemovedException If entry was removed.
+ */
+ public void updateIndex(SchemaIndexCacheVisitorClosure clo, long link) throws IgniteCheckedException,
+ GridCacheEntryRemovedException;
+
+ /**
* @return Expire time, without accounting for transactions or removals.
*/
public long rawExpireTime();
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
index f4d4258..ddec684 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
@@ -63,6 +63,7 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersionEx;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersionedEntryEx;
import org.apache.ignite.internal.processors.dr.GridDrType;
import org.apache.ignite.internal.util.IgniteTree;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
import org.apache.ignite.internal.util.lang.GridClosureException;
import org.apache.ignite.internal.util.lang.GridMetadataAwareAdapter;
import org.apache.ignite.internal.util.lang.GridTuple;
@@ -3374,6 +3375,22 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
}
/** {@inheritDoc} */
+ @Override public void updateIndex(SchemaIndexCacheVisitorClosure clo, long link) throws IgniteCheckedException,
+ GridCacheEntryRemovedException {
+ synchronized (this) {
+ if (isInternal())
+ return;
+
+ checkObsolete();
+
+ unswap(false);
+
+ if (val != null)
+ clo.apply(key, partition(), val, ver, expireTimeUnlocked(), link);
+ }
+ }
+
+ /** {@inheritDoc} */
@Override public <K, V> EvictableEntry<K, V> wrapEviction() {
return new CacheEvictableEntryImpl<>(this);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/2edb935c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
index 2775aa7..4775ea1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
@@ -78,6 +78,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.Gri
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxManager;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.schema.SchemaNodeLeaveExchangeWorkerTask;
import org.apache.ignite.internal.processors.timeout.GridTimeoutObject;
import org.apache.ignite.internal.util.GridListSet;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
@@ -309,6 +310,10 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
if (log.isDebugEnabled())
log.debug("Do not start exchange for discovery event: " + evt);
}
+
+ // Notify indexing engine about node leave so that we can re-map coordinator accordingly.
+ if (evt.type() == EVT_NODE_LEFT || evt.type() == EVT_NODE_FAILED)
+ exchWorker.addCustomTask(new SchemaNodeLeaveExchangeWorkerTask(evt.eventNode()));
}
finally {
leaveBusy();
@@ -752,17 +757,6 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
}
/**
- * Add custom task.
- *
- * @param task Task.
- */
- public void addCustomTask(CachePartitionExchangeWorkerTask task) {
- assert !task.isExchange();
-
- exchWorker.addCustomTask(task);
- }
-
- /**
* @param evt Discovery event.
* @return Affinity topology version.
*/
@@ -1536,6 +1530,16 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
}
/**
+ * Check if provided task from exchange queue is exchange task.
+ *
+ * @param task Task.
+ * @return {@code True} if this is exchange task.
+ */
+ private static boolean isExchangeTask(CachePartitionExchangeWorkerTask task) {
+ return task instanceof GridDhtPartitionsExchangeFuture;
+ }
+
+ /**
* @param exchTopVer Exchange topology version.
*/
private void dumpPendingObjects(@Nullable AffinityTopologyVersion exchTopVer) {
@@ -1682,7 +1686,7 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
void addCustomTask(CachePartitionExchangeWorkerTask task) {
assert task != null;
- assert !task.isExchange();
+ assert !isExchangeTask(task);
futQ.offer(task);
}
@@ -1693,6 +1697,8 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
* @param task Task.
*/
void processCustomTask(CachePartitionExchangeWorkerTask task) {
+ assert !isExchangeTask(task);
+
try {
cctx.cache().processCustomExchangeTask(task);
}
@@ -1707,7 +1713,7 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
boolean hasPendingExchange() {
if (!futQ.isEmpty()) {
for (CachePartitionExchangeWorkerTask task : futQ) {
- if (task.isExchange())
+ if (isExchangeTask(task))
return true;
}
}
@@ -1722,7 +1728,7 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
U.warn(log, "Pending exchange futures:");
for (CachePartitionExchangeWorkerTask task: futQ) {
- if (task.isExchange())
+ if (isExchangeTask(task))
U.warn(log, ">>> " + task);
}
}
@@ -1773,7 +1779,7 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
if (task == null)
continue; // Main while loop.
- if (!task.isExchange()) {
+ if (!isExchangeTask(task)) {
processCustomTask(task);
continue;