You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by tl...@apache.org on 2020/08/25 09:04:40 UTC
[ignite] branch master updated: IGNITE-13280 fix improper index
usage (#8067)
This is an automated email from the ASF dual-hosted git repository.
tledkov 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 31a45bf IGNITE-13280 fix improper index usage (#8067)
31a45bf is described below
commit 31a45bf23fcf5384364f3c931a24d282954af19a
Author: tledkov <tl...@gridgain.com>
AuthorDate: Tue Aug 25 12:04:18 2020 +0300
IGNITE-13280 fix improper index usage (#8067)
---
.../query/h2/database/H2TreeIndexBase.java | 2 +-
.../processors/query/h2/opt/GridH2IndexBase.java | 5 +-
.../processors/query/h2/opt/GridH2ProxyIndex.java | 8 +-
.../processors/query/h2/opt/H2IndexCostedBase.java | 234 +++++++++++++++++++++
.../processors/cache/index/BasicIndexTest.java | 146 +++++++++++--
.../query/h2/IgniteSqlQueryMinMaxTest.java | 5 +-
6 files changed, 369 insertions(+), 31 deletions(-)
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndexBase.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndexBase.java
index db07b96..53e93eb 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndexBase.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndexBase.java
@@ -63,7 +63,7 @@ public abstract class H2TreeIndexBase extends GridH2IndexBase {
HashSet<Column> allColumnsSet) {
long rowCnt = getRowCountApproximation();
- double baseCost = getCostRangeIndex(masks, rowCnt, filters, filter, sortOrder, false, allColumnsSet);
+ double baseCost = getCostRangeIndexEx(masks, rowCnt, filters, filter, sortOrder, false, allColumnsSet);
int mul = getDistributedMultiplier(ses, filters, filter);
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 8110c47..ad7e391 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
@@ -26,7 +26,6 @@ import org.apache.ignite.internal.processors.query.h2.opt.join.CollocationModel;
import org.apache.ignite.internal.processors.query.h2.opt.join.CollocationModelMultiplier;
import org.apache.ignite.spi.indexing.IndexingQueryCacheFilter;
import org.h2.engine.Session;
-import org.h2.index.BaseIndex;
import org.h2.index.IndexType;
import org.h2.message.DbException;
import org.h2.result.Row;
@@ -39,7 +38,7 @@ import org.jetbrains.annotations.NotNull;
/**
* Index base.
*/
-public abstract class GridH2IndexBase extends BaseIndex {
+public abstract class GridH2IndexBase extends H2IndexCostedBase {
/**
* Constructor.
*
@@ -49,7 +48,7 @@ public abstract class GridH2IndexBase extends BaseIndex {
* @param type Index type.
*/
protected GridH2IndexBase(GridH2Table tbl, String name, IndexColumn[] cols, IndexType type) {
- initBaseIndex(tbl, 0, name, cols, type);
+ super(tbl, name, cols, type);
}
/** {@inheritDoc} */
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ProxyIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ProxyIndex.java
index cbe58dd..5174277 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ProxyIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ProxyIndex.java
@@ -21,7 +21,6 @@ import java.util.HashSet;
import java.util.List;
import org.apache.ignite.internal.processors.query.h2.opt.join.ProxyDistributedLookupBatch;
import org.h2.engine.Session;
-import org.h2.index.BaseIndex;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexLookupBatch;
@@ -39,8 +38,7 @@ import org.h2.table.TableFilter;
* Allows to have 'free' index for alias columns
* Delegates the calls to underlying normal index
*/
-public class GridH2ProxyIndex extends BaseIndex {
-
+public class GridH2ProxyIndex extends H2IndexCostedBase {
/** Underlying normal index */
protected Index idx;
@@ -55,6 +53,8 @@ public class GridH2ProxyIndex extends BaseIndex {
String name,
List<IndexColumn> colsList,
Index idx) {
+ super(tbl, name, GridH2IndexBase.columnsArray(tbl, colsList),
+ IndexType.createNonUnique(false, false, idx instanceof SpatialIndex));
IndexColumn[] cols = colsList.toArray(new IndexColumn[colsList.size()]);
@@ -102,7 +102,7 @@ public class GridH2ProxyIndex extends BaseIndex {
@Override public double getCost(Session session, int[] masks, TableFilter[] filters, int filter, SortOrder sortOrder, HashSet<Column> allColumnsSet) {
long rowCnt = getRowCountApproximation();
- double baseCost = getCostRangeIndex(masks, rowCnt, filters, filter, sortOrder, false, allColumnsSet);
+ double baseCost = getCostRangeIndexEx(masks, rowCnt, filters, filter, sortOrder, false, allColumnsSet);
int mul = ((GridH2IndexBase)idx).getDistributedMultiplier(session, filters, filter);
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2IndexCostedBase.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2IndexCostedBase.java
new file mode 100644
index 0000000..b6e7ba6
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/H2IndexCostedBase.java
@@ -0,0 +1,234 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.HashSet;
+import org.apache.ignite.internal.util.typedef.F;
+import org.h2.engine.Constants;
+import org.h2.index.BaseIndex;
+import org.h2.index.IndexCondition;
+import org.h2.index.IndexType;
+import org.h2.result.SortOrder;
+import org.h2.table.Column;
+import org.h2.table.IndexColumn;
+import org.h2.table.TableFilter;
+
+/**
+ * Index base.
+ */
+public abstract class H2IndexCostedBase extends BaseIndex {
+ /**
+ * Constructor.
+ *
+ * @param tbl Table.
+ * @param name Index name.
+ * @param cols Indexed columns.
+ * @param type Index type.
+ */
+ protected H2IndexCostedBase(GridH2Table tbl, String name, IndexColumn[] cols, IndexType type) {
+ initBaseIndex(tbl, 0, name, cols, type);
+ }
+
+ /**
+ * Re-implement {@link BaseIndex#getCostRangeIndex} to support compatibility with old version.
+ */
+ protected long getCostRangeIndexEx(int[] masks, long rowCount,
+ TableFilter[] filters, int filter, SortOrder sortOrder,
+ boolean isScanIndex, HashSet<Column> allColumnsSet) {
+ rowCount += Constants.COST_ROW_OFFSET;
+
+ int totalSelectivity = 0;
+
+ long rowsCost = rowCount;
+
+ if (masks != null) {
+ int i = 0, len = columns.length;
+
+ while (i < len) {
+ Column column = columns[i++];
+
+ int index = column.getColumnId();
+ int mask = masks[index];
+
+ if ((mask & IndexCondition.EQUALITY) == IndexCondition.EQUALITY) {
+ if (i == len && getIndexType().isUnique()) {
+ rowsCost = 3;
+
+ break;
+ }
+
+ totalSelectivity = 100 - ((100 - totalSelectivity) *
+ (100 - column.getSelectivity()) / 100);
+
+ long distinctRows = rowCount * totalSelectivity / 100;
+
+ if (distinctRows <= 0)
+ distinctRows = 1;
+
+ rowsCost = Math.min(5 + Math.max(rowsCost / distinctRows, 1), rowsCost - (i > 0 ? 1 : 0));
+ }
+ else if ((mask & IndexCondition.RANGE) == IndexCondition.RANGE) {
+ rowsCost = Math.min(5 + rowsCost / 4, rowsCost - (i > 0 ? 1 : 0));
+
+ break;
+ }
+ else if ((mask & IndexCondition.START) == IndexCondition.START) {
+ rowsCost = Math.min(5 + rowsCost / 3, rowsCost - (i > 0 ? 1 : 0));
+
+ break;
+ }
+ else if ((mask & IndexCondition.END) == IndexCondition.END) {
+ rowsCost = Math.min(rowsCost / 3, rowsCost - (i > 0 ? 1 : 0));
+
+ break;
+ }
+ else
+ break;
+ }
+ }
+
+ // If the ORDER BY clause matches the ordering of this index,
+ // it will be cheaper than another index, so adjust the cost
+ // accordingly.
+ long sortingCost = 0;
+
+ if (sortOrder != null)
+ sortingCost = 100 + rowCount / 10;
+
+ if (sortOrder != null && !isScanIndex) {
+ boolean sortOrderMatches = true;
+ int coveringCount = 0;
+ int[] sortTypes = sortOrder.getSortTypes();
+
+ TableFilter tableFilter = filters == null ? null : filters[filter];
+
+ for (int i = 0, len = sortTypes.length; i < len; i++) {
+ if (i >= indexColumns.length) {
+ // We can still use this index if we are sorting by more
+ // than it's columns, it's just that the coveringCount
+ // is lower than with an index that contains
+ // more of the order by columns.
+ break;
+ }
+
+ Column col = sortOrder.getColumn(i, tableFilter);
+
+ if (col == null) {
+ sortOrderMatches = false;
+
+ break;
+ }
+
+ IndexColumn indexCol = indexColumns[i];
+
+ if (!col.equals(indexCol.column)) {
+ sortOrderMatches = false;
+
+ break;
+ }
+
+ int sortType = sortTypes[i];
+
+ if (sortType != indexCol.sortType) {
+ sortOrderMatches = false;
+
+ break;
+ }
+
+ coveringCount++;
+ }
+
+ if (sortOrderMatches) {
+ // "coveringCount" makes sure that when we have two
+ // or more covering indexes, we choose the one
+ // that covers more.
+ sortingCost = 100 - coveringCount;
+ }
+ }
+
+ TableFilter tableFilter;
+
+ boolean skipColumnsIntersection = false;
+
+ if (filters != null && (tableFilter = filters[filter]) != null && columns != null) {
+ skipColumnsIntersection = true;
+
+ ArrayList<IndexCondition> idxConds = tableFilter.getIndexConditions();
+
+ // Only pk with _key used.
+ if (F.isEmpty(idxConds))
+ skipColumnsIntersection = false;
+
+ for (IndexCondition cond : idxConds) {
+ if (cond.getColumn() == columns[0]) {
+ skipColumnsIntersection = false;
+
+ break;
+ }
+ }
+ }
+
+ // If we have two indexes with the same cost, and one of the indexes can
+ // satisfy the query without needing to read from the primary table
+ // (scan index), make that one slightly lower cost.
+ boolean needsToReadFromScanIndex = true;
+
+ if (!isScanIndex && allColumnsSet != null && !skipColumnsIntersection && !allColumnsSet.isEmpty()) {
+ boolean foundAllColumnsWeNeed = true;
+
+ for (Column c : allColumnsSet) {
+ boolean found = false;
+
+ for (Column c2 : columns) {
+ if (c == c2) {
+ found = true;
+
+ break;
+ }
+ }
+
+ if (!found) {
+ foundAllColumnsWeNeed = false;
+
+ break;
+ }
+ }
+
+ if (foundAllColumnsWeNeed)
+ needsToReadFromScanIndex = false;
+ }
+
+ long rc;
+
+ if (isScanIndex)
+ rc = rowsCost + sortingCost + 20;
+ else if (needsToReadFromScanIndex)
+ rc = rowsCost + rowsCost + sortingCost + 20;
+ else {
+ // The (20-x) calculation makes sure that when we pick a covering
+ // index, we pick the covering index that has the smallest number of
+ // columns (the more columns we have in index - the higher cost).
+ // This is faster because a smaller index will fit into fewer data
+ // blocks.
+ rc = rowsCost + sortingCost + columns.length;
+ }
+
+ return rc;
+ }
+}
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 7944e74..ee28b71 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
@@ -34,6 +34,7 @@ import org.apache.ignite.cache.QueryIndex;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cluster.ClusterState;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
@@ -51,6 +52,7 @@ import org.apache.ignite.testframework.junits.WithSystemProperty;
import org.jetbrains.annotations.Nullable;
import org.junit.Test;
+import static org.apache.ignite.internal.processors.query.h2.H2TableDescriptor.PK_IDX_NAME;
import static org.apache.ignite.internal.processors.query.h2.database.H2Tree.IGNITE_THROTTLE_INLINE_SIZE_CALCULATION;
import static org.apache.ignite.internal.processors.query.h2.opt.H2TableScanIndex.SCAN_INDEX_NAME_SUFFIX;
@@ -61,10 +63,10 @@ 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. */
+ /** {@code True} If index need to be created by static config. */
private static boolean createIdx = true;
- /** {@code True} If cache nedd to be created throught static config. */
+ /** {@code True} If cache need to be created by static config. */
private static boolean createStaticCache = true;
/** Default table name. */
@@ -303,7 +305,7 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
startGrid(1);
if (persistEnabled)
- ig0.cluster().active(true);
+ ig0.cluster().state(ClusterState.ACTIVE);
IgniteCache<Key, Val> cache = grid(0).cache(DEFAULT_CACHE_NAME);
@@ -324,7 +326,7 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
ig0 = startGrid(0);
if (persistEnabled)
- ig0.cluster().active(true);
+ ig0.cluster().state(ClusterState.ACTIVE);
ig0.getOrCreateCache(DEFAULT_CACHE_NAME);
@@ -370,9 +372,9 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
inlineSize = 10;
- srvLog = new ListeningTestLogger(false, log);
+ srvLog = new ListeningTestLogger(log);
- clientLog = new ListeningTestLogger(false, log);
+ clientLog = new ListeningTestLogger(log);
String msg1 = "Index with the given set or subset of columns already exists";
@@ -409,6 +411,11 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
cache.query(new SqlFieldsQuery("create index \"idx4\" on Val(valLong)"));
+ String plan = cache.query(new SqlFieldsQuery("explain select min(_key), max(_key) from Val")).getAll()
+ .get(0).get(0).toString().toUpperCase();
+
+ assertTrue(plan, plan.contains(PK_IDX_NAME.toUpperCase()));
+
assertTrue(lsnr.check());
srvLog.unregisterListener(lsnr);
@@ -446,6 +453,8 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
String plan = qryProc.querySqlFields(new SqlFieldsQuery(sql), true)
.getAll().get(0).get(0).toString().toUpperCase();
+ System.err.println(plan);
+
return idxName != null ? (!plan.contains(SCAN_INDEX_NAME_SUFFIX) && plan.contains(idxName.toUpperCase())) : !plan.contains(SCAN_INDEX_NAME_SUFFIX);
}
@@ -467,7 +476,13 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
return lsnrIdx.check();
}
- /** */
+ /**
+ * Create and fills the table.
+ *
+ * @param qryProc Query processor.
+ * @param tblName Table name.
+ * @param consPkFldsNum Number of fields to use in pk, 0 - default pk, positive\negative value shows fields iteration direction.
+ */
private void populateTable(GridQueryProcessor qryProc, String tblName, int consPkFldsNum, String... reqFlds) {
assert consPkFldsNum <= reqFlds.length;
@@ -476,16 +491,22 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
String sqlIns = "INSERT INTO " + tblName + " (";
for (int i = 0; i < reqFlds.length; ++i) {
- sql += reqFlds[i] + " VARCHAR, ";
+ sql += reqFlds[i] + " VARCHAR" + (consPkFldsNum == 0 && i == 0 ? " PRIMARY KEY, " : ", ");
sqlIns += reqFlds[i] + ((i < reqFlds.length - 1) ? ", " : ") values (");
}
- if (consPkFldsNum > 0) {
+ if (consPkFldsNum != 0) {
sql += " CONSTRAINT PK_PERSON PRIMARY KEY (";
- for (int i = 0; i < consPkFldsNum; ++i)
- sql += reqFlds[i] + ((i < consPkFldsNum - 1) ? ", " : "))");
+ if (consPkFldsNum > 0) {
+ for (int i = 0; i < consPkFldsNum; ++i)
+ sql += reqFlds[i] + ((i < consPkFldsNum - 1) ? ", " : "))");
+ }
+ else {
+ for (int i = -consPkFldsNum - 1; i >= 0; --i)
+ sql += reqFlds[i] + ((i > 0) ? ", " : "))");
+ }
}
else
sql += ")";
@@ -520,7 +541,7 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
public void testAllTableFieldsCoveredByIdx() throws Exception {
inlineSize = 10;
- srvLog = new ListeningTestLogger(false, log);
+ srvLog = new ListeningTestLogger(log);
IgniteEx ig0 = startGrid(0);
@@ -535,7 +556,7 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME"));
- assertTrue(checkIdxUsed(qryProc, "_key_PK", TEST_TBL_NAME, "FIRST_NAME",
+ assertTrue(checkIdxUsed(qryProc, PK_IDX_NAME, TEST_TBL_NAME, "FIRST_NAME",
"LAST_NAME", "LANG", "ADDRESS"));
assertTrue(checkIdxAlreadyExistLog(
@@ -550,9 +571,65 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME",
"LAST_NAME", "ADDRESS", "LANG"));
+ }
+
+ /**
+ * Checks index usage for full coverage.
+ */
+ @Test
+ public void testConditionsWithoutIndexes() throws Exception {
+ inlineSize = 10;
+
+ srvLog = new ListeningTestLogger(log);
+
+ IgniteEx ig0 = startGrid(0);
+
+ GridQueryProcessor qryProc = ig0.context().query();
+
+ populateTable(qryProc, TEST_TBL_NAME, 2, "FIRST_NAME", "LAST_NAME", "ADDRESS", "LANG");
+
+ String sqlIdx = String.format("create index \"idx1\" on %s(LANG, ADDRESS)", TEST_TBL_NAME);
+
+ qryProc.querySqlFields(new SqlFieldsQuery(sqlIdx), true).getAll();
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "LAST_NAME"));
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "ADDRESS"));
+
+ assertTrue(checkIdxUsed(qryProc, "idx1", TEST_TBL_NAME, "LANG"));
+
// first idx fields not belongs to request fields.
- assertTrue(checkIdxUsed(qryProc, "idx2", TEST_TBL_NAME, "ADDRESS",
- "LAST_NAME"));
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "ADDRESS", "LAST_NAME"));
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "ADDRESS", "ADDRESS"));
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "LAST_NAME", "ADDRESS"));
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "ADDRESS"));
+ }
+
+ /**
+ * Checks proxy index usage.
+ */
+ @Test
+ public void testConditionsWithoutIndexesUseProxy() throws Exception {
+ inlineSize = 10;
+
+ srvLog = new ListeningTestLogger(log);
+
+ IgniteEx ig0 = startGrid(0);
+
+ GridQueryProcessor qryProc = ig0.context().query();
+
+ populateTable(qryProc, TEST_TBL_NAME, 1, "ID", "VAL0", "VAL1");
+
+ String sqlIdx = String.format("create index \"idx1\" on %s(VAL0, VAL1)", TEST_TBL_NAME);
+
+ qryProc.querySqlFields(new SqlFieldsQuery(sqlIdx), true).getAll();
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "VAL1"));
+
+ assertTrue(checkIdxUsed(qryProc, "idx1", TEST_TBL_NAME, "VAL0"));
}
/**
@@ -584,7 +661,7 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME"));
- assertTrue(checkIdxUsed(qryProc, "_key_PK", TEST_TBL_NAME, "FIRST_NAME",
+ assertTrue(checkIdxUsed(qryProc, PK_IDX_NAME, TEST_TBL_NAME, "FIRST_NAME",
"LAST_NAME", "LANG", "ADDRESS"));
assertTrue(checkIdxAlreadyExistLog(
@@ -593,10 +670,11 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
String sqlIdx2 = String.format("create index \"idx2\" on %s(LANG, ADDRESS)", TEST_TBL_NAME);
qryProc.querySqlFields(new SqlFieldsQuery(sqlIdx2), true).getAll();
- // _key_PK used.
+
+ // PK_IDX_NAME 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"));
@@ -612,7 +690,7 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
assertTrue(checkIdxAlreadyExistLog(
qryProc, "idx4", TEST_TBL_NAME, "FIRST_NAME", "LAST_NAME", "ADDRESS", "LANG"));
- LogListener lsnrIdx4 = LogListener.matches(msg0).andMatches("_key_PK").build();
+ LogListener lsnrIdx4 = LogListener.matches(msg0).andMatches(PK_IDX_NAME).build();
srvLog.registerListener(lsnrIdx4);
@@ -624,7 +702,33 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
}
/**
- * Tests different fields sequence in indexes.
+ * Check three fields in pk index.
+ */
+ @Test
+ public void testCheckThreeFieldsInPk() throws Exception {
+ inlineSize = 10;
+
+ srvLog = new ListeningTestLogger(log);
+
+ IgniteEx ig0 = startGrid(0);
+
+ GridQueryProcessor qryProc = ig0.context().query();
+
+ populateTable(qryProc, TEST_TBL_NAME, 3, "c1", "c2", "c3", "c4", "c5", "c6");
+
+ assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "c1"));
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "c2"));
+
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "c3"));
+ }
+
+ /**
+ * Test composite indices with PK field in first place.
+ *
+ * There is no sense to create such indices:
+ * 1. PK index will be enough for equality condition on PK field.
+ * 2. None of these indices will be used for non-equality condition on PK field.
*/
@Test
public void testCreateIdxWithDifferentIdxFldsSeq() throws Exception {
@@ -784,7 +888,7 @@ public class BasicIndexTest extends AbstractIndexingCommonTest {
assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "FIRST_NAME", "LAST_NAME", "ADDRESS"));
- assertTrue(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "LAST_NAME", "ADDRESS"));
+ assertFalse(checkIdxUsed(qryProc, null, TEST_TBL_NAME, "LAST_NAME", "ADDRESS"));
}
/** */
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 9c299d3..e5319e2 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
@@ -169,8 +169,9 @@ public class IgniteSqlQueryMinMaxTest extends AbstractIndexingCommonTest {
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)).toLowerCase().contains("_key_pk"));
- assertTrue(((String)result.get(0).get(0)).toLowerCase().contains("direct lookup"));
+ String res = ((String)result.get(0).get(0)).toLowerCase();
+ assertTrue(res, res.contains("_key_pk"));
+ assertTrue(res, res.contains("direct lookup"));
}
}