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,

Reply via email to