You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ag...@apache.org on 2019/08/01 13:15:20 UTC
[ignite] branch master updated: IGNITE-10654 Print warn message in
case of index creating with already existing fields.
This is an automated email from the ASF dual-hosted git repository.
agura pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new c41466f IGNITE-10654 Print warn message in case of index creating with already existing fields.
c41466f is described below
commit c41466f480aca524f7a9da2891d1988c0e82bc43
Author: zstan <st...@gmail.com>
AuthorDate: Thu Aug 1 16:14:50 2019 +0300
IGNITE-10654 Print warn message in case of index creating with already existing fields.
Signed-off-by: Andrey Gura <ag...@apache.org>
---
.../processors/query/QueryIndexDescriptorImpl.java | 2 +-
.../processors/query/h2/opt/GridH2Table.java | 76 +++-
.../cache/IgniteDynamicSqlRestoreTest.java | 2 +-
.../processors/cache/index/BasicIndexTest.java | 487 ++++++++++++++++++++-
4 files changed, 560 insertions(+), 7 deletions(-)
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 1b85af5..d8b972c 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
@@ -127,7 +127,7 @@ public class QueryIndexDescriptorImpl implements GridQueryIndexDescriptor {
if (descending) {
if (descendings == null)
- descendings = new HashSet<>();
+ descendings = new HashSet<>();
descendings.add(field);
}
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 250fe2d..a4dfa16 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
@@ -29,10 +29,15 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteInterruptedException;
import org.apache.ignite.cache.query.QueryRetryException;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheContextInfo;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
@@ -48,6 +53,7 @@ import org.apache.ignite.internal.processors.query.h2.database.H2IndexType;
import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndex;
import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndexBase;
import org.apache.ignite.internal.processors.query.h2.database.IndexInformation;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.spi.indexing.IndexingQueryCacheFilter;
@@ -163,6 +169,10 @@ public class GridH2Table extends TableBase {
/** Table statistics. */
private volatile TableStatistics tblStats;
+ /** Logger. */
+ @GridToStringExclude
+ private IgniteLogger log;
+
/**
* Creates table.
*
@@ -231,6 +241,12 @@ public class GridH2Table extends TableBase {
// Init stats with the dummy values. This prevents us from scanning index with backup filter when
// topology may not be initialized yet.
tblStats = new TableStatistics(0, 0);
+
+ if (desc != null && desc.context() != null) {
+ GridKernalContext ctx = desc.context().kernalContext();
+
+ log = ctx.log(getClass());
+ }
}
/**
@@ -858,6 +874,52 @@ public class GridH2Table extends TableBase {
}
/**
+ * Checks index presence, return {@link Index} if index with same name or same fields and search direction already
+ * exist or {@code null} othervise.
+ *
+ * @param curIdx Index to check.
+ * @return Index if equal or subset index exist.
+ * @throws IgniteCheckedException If failed.
+ */
+ private @Nullable Index checkIndexPresence(Index curIdx) throws IgniteCheckedException {
+ IndexColumn[] curColumns = curIdx.getIndexColumns();
+
+ Index registredIdx = null;
+
+ for (Index idx : idxs) {
+ if (!(idx instanceof H2TreeIndex))
+ continue;
+
+ if (F.eq(curIdx.getName(), idx.getName()))
+ throw new IgniteCheckedException("Index already exists: " + idx.getName());
+
+ IndexColumn[] idxColumns = idx.getIndexColumns();
+
+ for (int i = 0; i < Math.min(idxColumns.length, curColumns.length); ++i) {
+ IndexColumn idxCol = idxColumns[i];
+ IndexColumn curCol = curColumns[i];
+
+ // pk attach at the end of listed fields.
+ if (curCol.column.getColumnId() == 0 && registredIdx != null)
+ continue;
+
+ if (H2Utils.equals(idxCol, curCol) && idxCol.sortType == curCol.sortType)
+ registredIdx = idx;
+ else {
+ registredIdx = null;
+
+ break;
+ }
+ }
+
+ if (registredIdx != null)
+ return registredIdx;
+ }
+
+ return null;
+ }
+
+ /**
* Add index that is in an intermediate state and is still being built, thus is not used in queries until it is
* promoted.
*
@@ -872,9 +934,17 @@ public class GridH2Table extends TableBase {
try {
ensureNotDestroyed();
- for (Index oldIdx : idxs) {
- if (F.eq(oldIdx.getName(), idx.getName()))
- throw new IgniteCheckedException("Index already exists: " + idx.getName());
+ Index idxExist = checkIndexPresence(idx);
+
+ if (idxExist != null) {
+ String idxCols = Stream.of(idxExist.getIndexColumns())
+ .map(k -> k.columnName).collect(Collectors.joining(", "));
+
+ U.warn(log, "Index with the given set or subset of columns already exists " +
+ "(consider dropping either new or existing index) [cacheName=" + cacheInfo.name() + ", " +
+ "schemaName=" + getSchema().getName() + ", tableName=" + getName() +
+ ", newIndexName=" + idx.getName() + ", existingIndexName=" + idxExist.getName() +
+ ", existingIndexColumns=[" + idxCols + "]]");
}
Index oldTmpIdx = tmpIdxs.put(idx.getName(), (GridH2IndexBase)idx);
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicSqlRestoreTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicSqlRestoreTest.java
index 8163a76..6ee3e53 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicSqlRestoreTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicSqlRestoreTest.java
@@ -199,7 +199,7 @@ public class IgniteDynamicSqlRestoreTest extends GridCommonAbstractTest implemen
" ADDRESS VARCHAR,\n" +
" LANG VARCHAR,\n" +
" BIRTH_DATE TIMESTAMP,\n" +
- " CONSTRAINT PK_PESON PRIMARY KEY (FIRST_NAME,LAST_NAME,ADDRESS,LANG)\n" +
+ " CONSTRAINT PK_PERSON PRIMARY KEY (FIRST_NAME,LAST_NAME,ADDRESS,LANG)\n" +
" ) WITH \"key_type=PersonKeyType, CACHE_NAME=PersonCache, value_type=PersonValueType, AFFINITY_KEY=FIRST_NAME,template=PARTITIONED,backups=1\"");
try (PreparedStatement stmt = conn.prepareStatement(
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/BasicIndexTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/BasicIndexTest.java
index dae6dcd..553d10a 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/BasicIndexTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/BasicIndexTest.java
@@ -26,10 +26,13 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cache.QueryEntity;
import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.DataRegionConfiguration;
@@ -38,15 +41,34 @@ import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager;
+import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.internal.processors.query.QueryUtils;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.ListeningTestLogger;
+import org.apache.ignite.testframework.LogListener;
+import org.jetbrains.annotations.Nullable;
import org.junit.Test;
+import static org.apache.ignite.internal.processors.query.h2.opt.H2TableScanIndex.SCAN_INDEX_NAME_SUFFIX;
+
/**
* A set of basic tests for caches with indexes.
*/
public class BasicIndexTest extends AbstractIndexingCommonTest {
+ /** Default client name. */
+ private static final String CLIENT_NAME = "client";
+
+ /** {@code True} If index need to be created throught static config. */
+ private static boolean createIdx = true;
+
+ /** {@code True} If cache nedd to be created throught static config. */
+ private static boolean createStaticCache = true;
+
+ /** Default table name. */
+ private static final String TEST_TBL_NAME = "PUBLIC.TEST_TABLE";
+
/** */
private Collection<QueryIndex> indexes = Collections.emptyList();
@@ -59,6 +81,12 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
/** */
private int gridCount = 1;
+ /** Server listening logger. */
+ private ListeningTestLogger srvLog;
+
+ /** Client listening logger. */
+ private ListeningTestLogger clientLog;
+
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
assertNotNull(inlineSize);
@@ -70,6 +98,17 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
igniteCfg.setConsistentId(igniteInstanceName);
+ if (igniteInstanceName.startsWith(CLIENT_NAME)) {
+ igniteCfg.setClientMode(true);
+
+ if (clientLog != null)
+ igniteCfg.setGridLogger(clientLog);
+ }
+ else {
+ if (srvLog != null)
+ igniteCfg.setGridLogger(srvLog);
+ }
+
LinkedHashMap<String, String> fields = new LinkedHashMap<>();
fields.put("keyStr", String.class.getName());
fields.put("keyLong", Long.class.getName());
@@ -78,7 +117,11 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
fields.put("valLong", Long.class.getName());
fields.put("valPojo", Pojo.class.getName());
+ if (!createIdx)
+ indexes = Collections.emptyList();
+
CacheConfiguration<Key, Val> ccfg = new CacheConfiguration<Key, Val>(DEFAULT_CACHE_NAME)
+ .setAffinity(new RendezvousAffinityFunction(false, 32))
.setQueryEntities(Collections.singleton(
new QueryEntity()
.setKeyType(Key.class.getName())
@@ -86,15 +129,17 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
.setFields(fields)
.setKeyFields(new HashSet<>(Arrays.asList("keyStr", "keyLong", "keyPojo")))
.setIndexes(indexes)
+ .setAliases(Collections.singletonMap(QueryUtils.KEY_FIELD_NAME, "pk_id"))
))
.setSqlIndexMaxInlineSize(inlineSize);
- igniteCfg.setCacheConfiguration(ccfg);
+ if (createStaticCache)
+ igniteCfg.setCacheConfiguration(ccfg);
if (isPersistenceEnabled) {
igniteCfg.setDataStorageConfiguration(new DataStorageConfiguration()
.setDefaultDataRegionConfiguration(
- new DataRegionConfiguration().setPersistenceEnabled(true)
+ new DataRegionConfiguration().setPersistenceEnabled(true).setMaxSize(10 * 1024 * 1024)
)
);
}
@@ -121,6 +166,8 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
cleanPersistenceDir();
+ srvLog = clientLog = null;
+
super.afterTest();
}
@@ -209,6 +256,442 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
}
}
+ /**
+ * Tests mixed dynamic and static caches with indexes creation.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testDynamicIdxOnStaticCacheWithIdxWithoutPersistence() throws Exception {
+ runDynamicIdxOnStaticCacheWithIdx(false);
+ }
+
+ /**
+ * Tests mixed dynamic and static caches with indexes creation.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testDynamicIdxOnStaticCacheWithIdxWithPersistence() throws Exception {
+ runDynamicIdxOnStaticCacheWithIdx(true);
+ }
+
+ /** */
+ private void runDynamicIdxOnStaticCacheWithIdx(boolean persistEnabled) throws Exception {
+ isPersistenceEnabled = persistEnabled;
+
+ inlineSize = 10;
+
+ createIdx = false;
+
+ indexes = Collections.singletonList(new QueryIndex("valStr"));
+
+ IgniteEx ig0 = startGrid(0);
+
+ createIdx = true;
+
+ startGrid(1);
+
+ if (persistEnabled)
+ ig0.cluster().active(true);
+
+ IgniteCache<Key, Val> cache = grid(0).cache(DEFAULT_CACHE_NAME);
+
+ populateCache();
+
+ String plan = cache.query(new SqlFieldsQuery("explain select * from Val where valStr between 0 and ?")
+ .setArgs(100)).getAll().get(0).get(0).toString();
+
+ assertTrue(plan, plan.contains(SCAN_INDEX_NAME_SUFFIX));
+
+ stopAllGrids();
+
+ if (persistEnabled)
+ cleanPersistenceDir();
+
+ createStaticCache = false;
+
+ ig0 = startGrid(0);
+
+ if (persistEnabled)
+ ig0.cluster().active(true);
+
+ ig0.getOrCreateCache(DEFAULT_CACHE_NAME);
+
+ populateCache();
+
+ createStaticCache = true;
+
+ try {
+ startGrid(1);
+
+ fail("Exception wasn't thrown");
+ }
+ catch (IgniteCheckedException e) {
+ // no op.
+ }
+ }
+
+ /**
+ * Tests dynamic indexes creation with equal fields.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testEqualFieldsDynamicIndexesWithoutPersistence() throws Exception {
+ runEqualFieldsDynamicIndexes(false);
+ }
+
+ /**
+ * Tests dynamic indexes creation with equal fields.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testEqualFieldsDynamicIndexesWithPersistence() throws Exception {
+ runEqualFieldsDynamicIndexes(true);
+ }
+
+ /** */
+ private void runEqualFieldsDynamicIndexes(boolean persistEnabled) throws Exception {
+ isPersistenceEnabled = persistEnabled;
+
+ indexes = Collections.singletonList(new QueryIndex("valStr"));
+
+ inlineSize = 10;
+
+ srvLog = new ListeningTestLogger(false, log);
+
+ clientLog = new ListeningTestLogger(false, log);
+
+ String msg1 = "Index with the given set or subset of columns already exists";
+
+ LogListener lsnr = LogListener.matches(msg1).andMatches(Pattern.compile(".*newIndexName=idx[0-9]")).build();
+
+ LogListener staticCachesLsnr = LogListener.matches(msg1).build();
+
+ srvLog.registerListener(staticCachesLsnr);
+
+ IgniteEx ig0 = startGrid(0);
+
+ if (persistEnabled)
+ ig0.cluster().active(true);
+
+ IgniteCache<Key, Val> cache = grid(0).cache(DEFAULT_CACHE_NAME);
+
+ populateCache();
+
+ cache.query(new SqlFieldsQuery("create index \"idx0\" on Val(valStr)"));
+
+ assertTrue(staticCachesLsnr.check());
+
+ srvLog.unregisterListener(staticCachesLsnr);
+
+ srvLog.registerListener(lsnr);
+
+ cache.query(new SqlFieldsQuery("create index \"idx1\" on Val(valStr, valLong)"));
+
+ cache.query(new SqlFieldsQuery("create index \"idx2\" on Val(valStr desc, valLong)"));
+
+ assertFalse(lsnr.check());
+
+ cache.query(new SqlFieldsQuery("create index \"idx3\" on Val(valStr, valLong)"));
+
+ cache.query(new SqlFieldsQuery("create index \"idx4\" on Val(valLong)"));
+
+ assertTrue(lsnr.check());
+
+ srvLog.unregisterListener(lsnr);
+
+ IgniteEx client = startGrid(CLIENT_NAME);
+
+ cache = client.cache(DEFAULT_CACHE_NAME);
+
+ LogListener lsnrIdx5 = LogListener.matches(msg1).andMatches("idx5").build();
+
+ srvLog.registerListener(lsnrIdx5);
+
+ cache.query(new SqlFieldsQuery("create index \"idx5\" on Val(valStr desc, valLong)"));
+
+ assertTrue(lsnrIdx5.check());
+
+ LogListener lsnrIdx7 = LogListener.matches(msg1).andMatches("idx7").build();
+
+ srvLog.registerListener(lsnrIdx7);
+
+ cache.query(new SqlFieldsQuery("create index \"idx6\" on Val(valLong)"));
+
+ cache.query(new SqlFieldsQuery("create index \"idx7\" on Val(keyStr, keyLong, keyPojo, valLong)"));
+
+ assertFalse(lsnrIdx7.check());
+ }
+
+ /** */
+ private boolean checkIdxUsed(GridQueryProcessor qryProc, @Nullable String idxName, String tblName, String... reqFlds) {
+ String sql = "explain select * from " + tblName + " where ";
+
+ for (int i = 0; i < reqFlds.length; ++i)
+ sql += reqFlds[i] + " > 0 " + ((i < reqFlds.length - 1) ? " and " : "");
+
+ String plan = qryProc.querySqlFields(new SqlFieldsQuery(sql), true)
+ .getAll().get(0).get(0).toString().toUpperCase();
+
+ return idxName != null ? (!plan.contains(SCAN_INDEX_NAME_SUFFIX) && plan.contains(idxName.toUpperCase())) : !plan.contains(SCAN_INDEX_NAME_SUFFIX);
+ }
+
+ /** */
+ private boolean checkIdxAlreadyExistLog(GridQueryProcessor qryProc, String idxName, String tblName, String... reqFlds) {
+ String msg0 = "Index with the given set or subset of columns already exists";
+
+ String sql = "create index \"" + idxName + "\" on " + tblName + "(";
+
+ for (int i = 0; i < reqFlds.length; ++i)
+ sql += reqFlds[i] + ((i < reqFlds.length - 1) ? ", " : ")");
+
+ LogListener lsnrIdx = LogListener.matches(msg0).andMatches(idxName).build();
+
+ srvLog.registerListener(lsnrIdx);
+
+ qryProc.querySqlFields(new SqlFieldsQuery(sql), true).getAll();
+
+ return lsnrIdx.check();
+ }
+
+ /** */
+ private void populateTable(GridQueryProcessor qryProc, String tblName, int consPkFldsNum, String... reqFlds) {
+ assert consPkFldsNum <= reqFlds.length;
+
+ String sql = "CREATE TABLE " + tblName + " (";
+
+ String sqlIns = "INSERT INTO " + tblName + " (";
+
+ for (int i = 0; i < reqFlds.length; ++i) {
+ sql += reqFlds[i] + " VARCHAR, ";
+
+ sqlIns += reqFlds[i] + ((i < reqFlds.length - 1) ? ", " : ") values (");
+ }
+
+ if (consPkFldsNum > 0) {
+ sql += " CONSTRAINT PK_PERSON PRIMARY KEY (";
+
+ for (int i = 0; i < consPkFldsNum; ++i)
+ sql += reqFlds[i] + ((i < consPkFldsNum - 1) ? ", " : "))");
+ }
+ else
+ sql += ")";
+
+ qryProc.querySqlFields(new SqlFieldsQuery(sql), true);
+
+ for (int i = 0; i < 10; ++i) {
+ String s0 = sqlIns;
+
+ for (int f = 0; f < reqFlds.length; ++f)
+ s0 += i + ((f < reqFlds.length - 1) ? ", " : ")");
+
+ qryProc.querySqlFields(new SqlFieldsQuery(s0), true).getAll();
+ }
+ }
+
+ /**
+ * Checks index usage for full coverage.
+ */
+ @Test
+ public void testAllTableFieldsCoveredByIdx() throws Exception {
+ inlineSize = 10;
+
+ srvLog = new ListeningTestLogger(false, log);
+
+ IgniteEx ig0 = startGrid(0);
+
+ GridQueryProcessor qryProc = ig0.context().query();
+
+ populateTable(qryProc, TEST_TBL_NAME, 2, "FIRST_NAME", "LAST_NAME",
+ "ADDRESS", "LANG");
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "LANG"));
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "LAST_NAME"));
+
+ assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME"));
+
+ assertTrue(checkIdxUsed(qryProc, "_key_PK", TEST_TBL_NAME, "FIRST_NAME",
+ "LAST_NAME", "LANG", "ADDRESS"));
+
+ assertTrue(checkIdxAlreadyExistLog(
+ qryProc, "idx1", TEST_TBL_NAME, "FIRST_NAME", "LAST_NAME"));
+
+ String sqlIdx2 = String.format("create index \"idx2\" on %s(LANG, ADDRESS)", TEST_TBL_NAME);
+
+ qryProc.querySqlFields(new SqlFieldsQuery(sqlIdx2), true).getAll();
+
+ assertTrue(checkIdxUsed(qryProc, "idx2", TEST_TBL_NAME, "FIRST_NAME",
+ "LAST_NAME", "LANG", "ADDRESS"));
+
+ assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME",
+ "LAST_NAME", "ADDRESS", "LANG"));
+ // first idx fields not belongs to request fields.
+ assertTrue(checkIdxUsed(qryProc, "idx2", TEST_TBL_NAME, "ADDRESS",
+ "LAST_NAME"));
+ }
+
+ /**
+ * Checks index usage for partial coverage.
+ * Last field not participate in any index.
+ */
+ @Test
+ public void testPartialTableFieldsCoveredByIdx() throws Exception {
+ inlineSize = 10;
+
+ String msg0 = "Index with the given set or subset of columns already exists";
+
+ srvLog = new ListeningTestLogger(false, log);
+
+ IgniteEx ig0 = startGrid(0);
+
+ GridQueryProcessor qryProc = ig0.context().query();
+
+ String cacheName = QueryUtils.createTableCacheName("PUBLIC", "TEST_TABLE");
+
+ populateTable(qryProc, TEST_TBL_NAME, 2, "FIRST_NAME", "LAST_NAME",
+ "ADDRESS", "LANG", "GENDER");
+
+ IgniteCache<Object, Object> jcache = ig0.cache(cacheName);
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "LANG"));
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "LAST_NAME"));
+
+ assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME"));
+
+ assertTrue(checkIdxUsed(qryProc, "_key_PK", TEST_TBL_NAME, "FIRST_NAME",
+ "LAST_NAME", "LANG", "ADDRESS"));
+
+ assertTrue(checkIdxAlreadyExistLog(
+ qryProc, "idx1", TEST_TBL_NAME, "FIRST_NAME", "LAST_NAME"));
+
+ String sqlIdx2 = String.format("create index \"idx2\" on %s(LANG, ADDRESS)", TEST_TBL_NAME);
+
+ qryProc.querySqlFields(new SqlFieldsQuery(sqlIdx2), true).getAll();
+ // _key_PK used.
+ assertFalse(checkIdxUsed(qryProc, "idx2", TEST_TBL_NAME, "FIRST_NAME",
+ "LAST_NAME", "LANG", "ADDRESS"));
+ // _key_PK used.
+ assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME",
+ "LAST_NAME", "LANG", "ADDRESS"));
+
+ assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME",
+ "LAST_NAME", "ADDRESS", "LANG"));
+ // first idx fields not belongs to request fields.
+ assertFalse(checkIdxUsed(qryProc, "idx2", TEST_TBL_NAME, "ADDRESS",
+ "LAST_NAME"));
+
+ assertFalse(checkIdxAlreadyExistLog(
+ qryProc, "idx3", TEST_TBL_NAME, "ADDRESS", "LANG"));
+
+ assertTrue(checkIdxAlreadyExistLog(
+ qryProc, "idx4", TEST_TBL_NAME, "FIRST_NAME", "LAST_NAME", "ADDRESS", "LANG"));
+
+ LogListener lsnrIdx4 = LogListener.matches(msg0).andMatches("_key_PK").build();
+
+ srvLog.registerListener(lsnrIdx4);
+
+ String sqlIdx5 = String.format("create index \"idx5\" on %s(FIRST_NAME, LAST_NAME, LANG, ADDRESS)", TEST_TBL_NAME);
+
+ jcache.query(new SqlFieldsQuery(sqlIdx5)).getAll();
+
+ assertTrue(lsnrIdx4.check());
+ }
+
+ /**
+ * Tests different fields sequence in indexes.
+ */
+ @Test
+ public void testCreateIdxWithDifferentIdxFldsSeq() throws Exception {
+ inlineSize = 10;
+
+ srvLog = new ListeningTestLogger(false, log);
+
+ IgniteEx ig0 = startGrid(0);
+
+ IgniteEx client = startGrid(CLIENT_NAME);
+
+ GridQueryProcessor qryProc = ig0.context().query();
+
+ populateTable(qryProc, TEST_TBL_NAME, 1, "c1", "c2", "c3", "c4", "c5");
+
+ assertFalse(checkIdxAlreadyExistLog(
+ qryProc, "idx1", TEST_TBL_NAME, "c1", "c2", "c3", "c4", "c5"));
+
+ assertFalse(checkIdxAlreadyExistLog(
+ qryProc, "idx2", TEST_TBL_NAME, "c1", "c3", "c4", "c5"));
+
+ assertTrue(checkIdxAlreadyExistLog(
+ qryProc, "idx3", TEST_TBL_NAME, "c1", "c2"));
+
+ assertTrue(checkIdxAlreadyExistLog(
+ qryProc, "idx4", TEST_TBL_NAME, "c1", "c3"));
+
+ assertFalse(checkIdxAlreadyExistLog(
+ qryProc, "idx5", TEST_TBL_NAME, "c1", "c4", "c5"));
+
+ GridQueryProcessor qryProcCl = client.context().query();
+
+ assertTrue(checkIdxAlreadyExistLog(
+ qryProcCl, "idx6", TEST_TBL_NAME, "c1", "c2"));
+ }
+
+ /**
+ * Tests different fields sequence in indexes.
+ * Last field not participate in any index.
+ */
+ @Test
+ public void testIndexWithDifferentFldsReqPartialFldsInIdx() throws Exception {
+ inlineSize = 10;
+
+ IgniteEx ig0 = startGrid(0);
+
+ GridQueryProcessor qryProc = ig0.context().query();
+
+ populateTable(qryProc, TEST_TBL_NAME, 2, "FIRST_NAME", "LAST_NAME",
+ "ADDRESS", "LANG", "GENDER");
+
+ String sqlIdx1 = String.format("create index \"idx1\" on %s(LANG, LAST_NAME, ADDRESS, FIRST_NAME)", TEST_TBL_NAME);
+
+ qryProc.querySqlFields(new SqlFieldsQuery(sqlIdx1), true).getAll();
+
+ assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME", "LAST_NAME", "LANG"));
+
+ assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME", "LAST_NAME", "ADDRESS"));
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "LAST_NAME", "ADDRESS"));
+ }
+
+ /**
+ * Tests different fields sequence in indexes.
+ * All fields covered by indexes.
+ */
+ @Test
+ public void testIndexWithDifferentFldsReqAllFldsInIdx() throws Exception {
+ inlineSize = 10;
+
+ IgniteEx ig0 = startGrid(0);
+
+ GridQueryProcessor qryProc = ig0.context().query();
+
+ populateTable(qryProc, TEST_TBL_NAME, 2, "FIRST_NAME", "LAST_NAME",
+ "ADDRESS", "LANG");
+
+ String sqlIdx1 = String.format("create index \"idx1\" on %s(LANG, LAST_NAME, ADDRESS, FIRST_NAME)", TEST_TBL_NAME);
+
+ qryProc.querySqlFields(new SqlFieldsQuery(sqlIdx1), true).getAll();
+
+ assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME", "LAST_NAME", "LANG"));
+
+ assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME", "LAST_NAME", "ADDRESS"));
+
+ assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "LAST_NAME", "ADDRESS"));
+ }
+
/** */
@Test
public void testNoIndexesWithPersistence() throws Exception {