This is an automated email from the ASF dual-hosted git repository.
alexpl 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 996e1ad4361 IGNITE-17027 Fix index scan on column with mismatched data
type - Fixes #10274.
996e1ad4361 is described below
commit 996e1ad4361f0d9dc8974689ddf6931c56ab5083
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Thu Sep 29 12:20:07 2022 +0300
IGNITE-17027 Fix index scan on column with mismatched data type - Fixes
#10274.
Signed-off-by: Aleksey Plekhanov <[email protected]>
---
.../processors/query/h2/database/H2TreeIndex.java | 17 +++--
.../query/h2/database/H2ValueIndexKey.java | 78 --------------------
.../cache/index/IndexColumnTypeMismatchTest.java | 83 ++++++++++++++++++++++
.../IgniteBinaryCacheQueryTestSuite3.java | 2 +
4 files changed, 96 insertions(+), 84 deletions(-)
diff --git
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
index 79e5ef68017..0e7274b3267 100644
---
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
+++
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
@@ -76,6 +76,7 @@ import org.apache.ignite.internal.util.GridSpinBusyLock;
import org.apache.ignite.internal.util.lang.GridCursor;
import org.apache.ignite.internal.util.typedef.CIX2;
import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.internal.util.typedef.internal.LT;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.plugin.extensions.communication.Message;
@@ -277,19 +278,23 @@ public class H2TreeIndex extends H2TreeIndexBase {
IndexKeyType colType =
rowHnd.indexKeyDefinitions().get(i).idxType();
if (v == null)
- continue;
+ break;
// If it's possible to convert search row to index value type - do
it. In this case converted value
- // can be used for the inline search. Otherwise, wrap search row
into index key and exploit
- // comparison/convertion provided by H2. In this case indexed
value will be converted to search row type on
- // each comparison.
+ // can be used for the inline search.
+ // Otherwise, we can't use search row as index find bound, since
different types have different comparison
+ // rules (for example, '2' > '10' for strings and 2 < 10 for
integers). Best we can do here is leave search
+ // bound empty. In this case index scan by bounds can be extended
to full index scan and rows will be
+ // filtered out by original condition on H2 level.
if (colType.code() != v.getType()) {
if (Value.getHigherOrder(colType.code(), v.getType()) ==
colType.code())
v = v.convertTo(colType.code());
else {
- keys[i] = new
H2ValueIndexKey(rowDescriptor().context().cacheObjectContext(), tbl, v);
+ LT.warn(log, "Provided value can't be used as index search
bound due to column data type " +
+ "mismatch. This can lead to full index scans instead
of range index scans. [index=" +
+ idxName + ", colType=" + colType + ", valType=" +
IndexKeyType.forCode(v.getType()) + ']');
- continue;
+ break;
}
}
diff --git
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2ValueIndexKey.java
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2ValueIndexKey.java
deleted file mode 100644
index 2e6221d6c43..00000000000
---
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2ValueIndexKey.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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.database;
-
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
-import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
-import org.apache.ignite.internal.processors.cache.CacheObjectValueContext;
-import org.apache.ignite.internal.processors.query.h2.H2Utils;
-import org.h2.table.Table;
-import org.h2.value.Value;
-
-/**
- * Index key wrapped over H2 Value.
- */
-public class H2ValueIndexKey implements IndexKey {
- /** */
- private final CacheObjectValueContext coCtx;
-
- /** */
- private final Table table;
-
- /** */
- private final Value val;
-
- /** */
- public H2ValueIndexKey(CacheObjectValueContext coCtx, Table table, Value
val) {
- this.coCtx = coCtx;
- this.table = table;
- this.val = val;
- }
-
- /** {@inheritDoc} */
- @Override public Object key() {
- return val.getObject();
- }
-
- /** {@inheritDoc} */
- @Override public IndexKeyType type() {
- return IndexKeyType.forCode(val.getType());
- }
-
- /** {@inheritDoc} */
- @Override public int compare(IndexKey o) throws IgniteCheckedException {
- return compareValues(val, H2Utils.wrap(coCtx, o.key(),
o.type().code()));
- }
-
- /**
- * @param v1 First value.
- * @param v2 Second value.
- * @return Comparison result.
- */
- private int compareValues(Value v1, Value v2) {
- // Exploit convertion/comparison provided by H2.
- return Integer.signum(table.compareTypeSafe(v1, v2));
- }
-
- /** {@inheritDoc} */
- @Override public boolean isComparableTo(IndexKey k) {
- return true;
- }
-}
diff --git
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/IndexColumnTypeMismatchTest.java
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/IndexColumnTypeMismatchTest.java
new file mode 100644
index 00000000000..da29ad2c8dd
--- /dev/null
+++
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/IndexColumnTypeMismatchTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.List;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.testframework.ListeningTestLogger;
+import org.apache.ignite.testframework.LogListener;
+import org.junit.Test;
+
+/**
+ * Test query on index by search row with mismatched column type.
+ */
+public class IndexColumnTypeMismatchTest extends AbstractIndexingCommonTest {
+ /** */
+ private final ListeningTestLogger listeningLog = new
ListeningTestLogger(log);
+
+ /** */
+ private static final int ROW_COUNT = 100;
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String
igniteInstanceName) throws Exception {
+ return
super.getConfiguration(igniteInstanceName).setGridLogger(listeningLog);
+ }
+
+ /** */
+ @Test
+ public void testIndexColTypeMismatch() throws Exception {
+ LogListener lsnr = LogListener
+ .matches("Provided value can't be used as index search bound due
to column data type mismatch")
+ .times(1)
+ .build();
+
+ listeningLog.registerListener(lsnr);
+
+ IgniteEx ignite = startGrid(0);
+ String cacheName = "test";
+
+ sql(ignite, "CREATE TABLE test (id INTEGER, val VARCHAR, PRIMARY KEY
(id)) " +
+ "WITH \"CACHE_NAME=" + cacheName + "\"");
+
+ sql(ignite, "CREATE INDEX test_idx ON test (val)");
+
+ for (int i = 0; i < ROW_COUNT; i++)
+ sql(ignite, "INSERT INTO test VALUES (?, ?)", i, i);
+
+ for (int i = 0; i < ROW_COUNT; i++) {
+ // Use 'int' as a search row for 'string' index.
+ List<List<?>> res = sql(ignite, "SELECT val FROM test WHERE val =
?", i);
+
+ assertEquals(1, res.size());
+ assertEquals(String.valueOf(i), res.get(0).get(0));
+ }
+
+ List<List<?>> res = sql(ignite, "SELECT val FROM test WHERE val < ?",
50);
+
+ assertEquals(50, res.size());
+
+ assertTrue(lsnr.check());
+ }
+
+ /** */
+ private List<List<?>> sql(IgniteEx ign, String sql, Object... args) {
+ return ign.context().query().querySqlFields(new
SqlFieldsQuery(sql).setArgs(args), false).getAll();
+ }
+}
diff --git
a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite3.java
b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite3.java
index 98b67bc4ecc..f5f95b6c64a 100644
---
a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite3.java
+++
b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite3.java
@@ -88,6 +88,7 @@ import
org.apache.ignite.internal.processors.cache.index.H2RowCachePageEvictionT
import org.apache.ignite.internal.processors.cache.index.H2RowCacheSelfTest;
import
org.apache.ignite.internal.processors.cache.index.H2RowExpireTimeIndexSelfTest;
import org.apache.ignite.internal.processors.cache.index.IgniteDecimalSelfTest;
+import
org.apache.ignite.internal.processors.cache.index.IndexColumnTypeMismatchTest;
import org.apache.ignite.internal.processors.cache.index.LongIndexNameTest;
import
org.apache.ignite.internal.processors.cache.index.OptimizedMarshallerIndexNameTest;
import
org.apache.ignite.internal.processors.cache.index.PojoIndexLocalQueryTest;
@@ -201,6 +202,7 @@ import org.junit.runners.Suite;
BaseH2CompareQueryTest.class,
H2CompareBigQueryTest.class,
H2CompareBigQueryDistributedJoinsTest.class,
+ IndexColumnTypeMismatchTest.class,
// Cache query metrics.
CachePartitionedQueryMetricsDistributedSelfTest.class,