This is an automated email from the ASF dual-hosted git repository.

zstan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 2e5f3cc39f1 IGNITE-25903 Sql. Support literals in PA metadata builder 
(#6406)
2e5f3cc39f1 is described below

commit 2e5f3cc39f1db1adc109ddefe49e4732abf6f26e
Author: Evgeniy Stanilovskiy <[email protected]>
AuthorDate: Thu Aug 14 11:13:28 2025 +0300

    IGNITE-25903 Sql. Support literals in PA metadata builder (#6406)
---
 .../internal/benchmark/BulkLoadBenchmark.java      |  28 +++-
 .../runner/app/client/ItThinClientSqlTest.java     |  59 +++++++
 .../ignite/internal/util/ColocationUtils.java      |  33 ++++
 .../PartitionAwarenessMetadataExtractor.java       |  35 ++++-
 .../ignite/internal/sql/engine/util/RexUtils.java  |  32 ++++
 .../planner/AbstractTpcQueryPlannerTest.java       |   4 +-
 .../PartitionAwarenessMetadataTest.java            | 171 ++++++++++++++-------
 7 files changed, 299 insertions(+), 63 deletions(-)

diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/benchmark/BulkLoadBenchmark.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/benchmark/BulkLoadBenchmark.java
index b574ad71f1b..a254a58c596 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/benchmark/BulkLoadBenchmark.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/benchmark/BulkLoadBenchmark.java
@@ -21,6 +21,7 @@ import static java.util.stream.Collectors.joining;
 import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
 import static org.apache.ignite.internal.util.IgniteUtils.closeAll;
 
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.IntStream;
 import org.apache.ignite.client.IgniteClient;
@@ -87,6 +88,27 @@ public class BulkLoadBenchmark extends 
AbstractMultiNodeBenchmark {
         state.upload(count, batchSize);
     }
 
+    @Override
+    protected void createTablesOnStartup() {
+        createTable(TABLE_NAME,
+                List.of(
+                        "ycsb_key int",
+                        "field1   int",
+                        "field2   varchar(100)",
+                        "field3   varchar(100)",
+                        "field4   varchar(100)",
+                        "field5   varchar(100)",
+                        "field6   varchar(100)",
+                        "field7   varchar(100)",
+                        "field8   varchar(100)",
+                        "field9   varchar(100)",
+                        "field10  varchar(100)"
+                ),
+                List.of("ycsb_key", "field1"),
+                List.of()
+        );
+    }
+
     /**
      * Benchmark's entry point.
      */
@@ -166,7 +188,7 @@ public class BulkLoadBenchmark extends 
AbstractMultiNodeBenchmark {
          */
         @Setup
         public void setUp() {
-            for (int i = 1; i < 11; i++) {
+            for (int i = 2; i < 11; i++) {
                 tuple.set("field" + i, FIELD_VAL);
             }
 
@@ -193,7 +215,7 @@ public class BulkLoadBenchmark extends 
AbstractMultiNodeBenchmark {
                     tx = client.transactions().begin();
                 }
 
-                kvView.put(tx, Tuple.create().set("ycsb_key", i), tuple);
+                kvView.put(tx, Tuple.create().set("ycsb_key", i).set("field1", 
1), tuple);
             }
         }
     }
@@ -202,7 +224,7 @@ public class BulkLoadBenchmark extends 
AbstractMultiNodeBenchmark {
         String insertQueryTemplate = "insert into {}({}, {}) values(?, {})";
 
         String fieldsQ = IntStream.range(1, 11).mapToObj(i -> "field" + 
i).collect(joining(","));
-        String valQ = IntStream.range(1, 11).mapToObj(i -> "'" + FIELD_VAL + 
"'").collect(joining(","));
+        String valQ = "1, " + IntStream.range(2, 11).mapToObj(i -> "'" + 
FIELD_VAL + "'").collect(joining(","));
 
         return format(insertQueryTemplate, TABLE_NAME, "ycsb_key", fieldsQ, 
valQ);
     }
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
index 51967dccbf8..ca14d6b4a75 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
@@ -30,6 +30,7 @@ import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
@@ -48,6 +49,7 @@ import org.apache.ignite.sql.ResultSetMetadata;
 import org.apache.ignite.sql.SqlException;
 import org.apache.ignite.sql.SqlRow;
 import org.apache.ignite.sql.Statement;
+import org.apache.ignite.sql.Statement.StatementBuilder;
 import org.apache.ignite.sql.async.AsyncResultSet;
 import org.apache.ignite.table.Table;
 import org.apache.ignite.table.mapper.Mapper;
@@ -394,6 +396,63 @@ public class ItThinClientSqlTest extends 
ItAbstractThinClientTest {
         assertEquals(1, res.currentPage().iterator().next().intValue(0));
     }
 
+    /** The purpose of this test is to check clients with different timeZone 
settings.
+     * In case when literal is a part of primary key and has a type: TIMESTAMP 
WITH LOCAL TIME ZONE - no
+     * partition awareness meta need to be calculated.
+     */
+    @Test
+    void testPartitionAwarenessNotExtractedForTsLiteral() {
+        IgniteClient client = client();
+        IgniteSql sql = client.sql();
+
+        sql.execute(null, "CREATE TABLE my_table (id int, ts TIMESTAMP WITH 
LOCAL TIME ZONE, val INT, "
+                + "PRIMARY KEY(id, ts))");
+
+        int count = 100;
+
+        for (int i = 0; i < count; i++) {
+            sql.execute(null, "INSERT INTO my_table VALUES (?, TIMESTAMP WITH 
LOCAL TIME ZONE '1970-01-01 00:00:00', ?)", i, i);
+        }
+
+        StatementBuilder builder = sql.statementBuilder();
+
+        String query = "SELECT * FROM my_table WHERE id = ? AND ts = TIMESTAMP 
WITH LOCAL TIME ZONE '1970-01-01 00:00:00'";
+
+        builder.query(query);
+        builder.timeZoneId(ZoneId.of("Asia/Nicosia"));
+        Statement stmt1 = builder.build();
+
+        builder = sql.statementBuilder();
+        builder.query(query);
+        builder.timeZoneId(ZoneId.of("UTC"));
+        Statement stmt2 = builder.build();
+
+        Transaction tx = null;
+        for (int i = 0; i < count; i++) {
+            if (i % 5 == 0) {
+                if (tx != null) {
+                    tx.commit();
+                }
+
+                tx = client.transactions().begin();
+            }
+
+            sql.execute(tx, stmt1, i);
+        }
+
+        for (int i = 0; i < count; i++) {
+            if (i % 5 == 0) {
+                if (tx != null) {
+                    tx.commit();
+                }
+
+                tx = client.transactions().begin();
+            }
+
+            sql.execute(tx, stmt2, i);
+        }
+    }
+
     @Test
     void testExplicitTransactionKvCase() {
         IgniteClient client = client();
diff --git 
a/modules/schema/src/main/java/org/apache/ignite/internal/util/ColocationUtils.java
 
b/modules/schema/src/main/java/org/apache/ignite/internal/util/ColocationUtils.java
index 0ecf6343a5e..a0e8adf546c 100644
--- 
a/modules/schema/src/main/java/org/apache/ignite/internal/util/ColocationUtils.java
+++ 
b/modules/schema/src/main/java/org/apache/ignite/internal/util/ColocationUtils.java
@@ -117,4 +117,37 @@ public class ColocationUtils {
                 throw new IllegalStateException("Unexpected type: " + 
type.spec());
         }
     }
+
+    /** Generates 32-bit hash from the given value with regard to provided 
type. */
+    public static int hash(@Nullable Object v, NativeType type) {
+        if (v == null) {
+            return HashCalculator.hashValue(null, 0, 0);
+        }
+
+        switch (type.spec()) {
+            case BOOLEAN:
+            case INT8:
+            case INT16:
+            case INT32:
+            case INT64:
+            case FLOAT:
+            case DOUBLE:
+            case UUID:
+            case STRING:
+            case BYTE_ARRAY:
+            case DATE:
+                return HashCalculator.hashValue(v, 0, 0);
+            case DECIMAL:
+                assert type instanceof DecimalNativeType;
+                return HashCalculator.hashValue(v, ((DecimalNativeType) 
type).scale(), 0);
+            case TIME:
+            case DATETIME:
+            case TIMESTAMP:
+                assert type instanceof TemporalNativeType;
+                return HashCalculator.hashValue(v, 0, ((TemporalNativeType) 
type).precision());
+
+            default:
+                throw new IllegalStateException("Unexpected type: " + 
type.spec());
+        }
+    }
 }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/partitionawareness/PartitionAwarenessMetadataExtractor.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/partitionawareness/PartitionAwarenessMetadataExtractor.java
index 9495ba411c0..d32a26aaf82 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/partitionawareness/PartitionAwarenessMetadataExtractor.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/partitionawareness/PartitionAwarenessMetadataExtractor.java
@@ -17,15 +17,26 @@
 
 package org.apache.ignite.internal.sql.engine.prepare.partitionawareness;
 
+import it.unimi.dsi.fastutil.ints.IntArrayList;
 import java.util.List;
 import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.rex.RexDynamicParam;
+import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.ImmutableIntList;
 import org.apache.ignite.internal.sql.engine.rel.IgniteKeyValueGet;
 import org.apache.ignite.internal.sql.engine.rel.IgniteKeyValueModify;
 import 
org.apache.ignite.internal.sql.engine.rel.IgniteKeyValueModify.Operation;
 import org.apache.ignite.internal.sql.engine.schema.IgniteTable;
+import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
+import org.apache.ignite.internal.sql.engine.util.Commons;
+import org.apache.ignite.internal.sql.engine.util.Primitives;
+import org.apache.ignite.internal.sql.engine.util.RexUtils;
+import org.apache.ignite.internal.sql.engine.util.RexUtils.FaultyContext;
+import org.apache.ignite.internal.sql.engine.util.TypeUtils;
+import org.apache.ignite.internal.type.NativeType;
+import org.apache.ignite.internal.util.ColocationUtils;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -97,7 +108,9 @@ public class PartitionAwarenessMetadataExtractor {
 
         // colocation key index to dynamic param index
         int[] indexes = new int[colocationKeys.size()];
-        int[] hash = new int[0];
+        IntArrayList hashFields = new IntArrayList(colocationKeys.size() / 2);
+
+        int hashPos = -1;
 
         for (int i = 0; i < colocationKeys.size(); i++) {
             int colIdx = colocationKeys.get(i);
@@ -113,11 +126,31 @@ public class PartitionAwarenessMetadataExtractor {
             if (expr instanceof RexDynamicParam) {
                 RexDynamicParam dynamicParam = (RexDynamicParam) expr;
                 indexes[i] = dynamicParam.getIndex();
+            } else if (expr instanceof RexLiteral) {
+                RexLiteral expr0 = (RexLiteral) expr;
+
+                // depends on supplied zoneId, it can`t be cached
+                if (expr0.getTypeName() == 
SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) {
+                    return null;
+                }
+
+                indexes[i] = hashPos--;
+
+                Class<?> internalType = Primitives.wrap((Class<?>) 
Commons.typeFactory().getJavaClass(expr0.getType()));
+                Object val = RexUtils.literalValue(FaultyContext.INSTANCE, 
expr0, internalType);
+
+                NativeType nativeType = 
IgniteTypeFactory.relDataTypeToNative(expr0.getType());
+
+                val = TypeUtils.fromInternal(val, nativeType.spec());
+
+                hashFields.add(ColocationUtils.hash(val, nativeType));
             } else {
                 return null;
             }
         }
 
+        int[] hash = hashFields.toArray(new int[0]);
+
         return new PartitionAwarenessMetadata(igniteTable.id(), indexes, hash, 
directTxMode);
     }
 }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
index 26c7f8202ee..9a3caecaaf4 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
@@ -58,7 +58,9 @@ import java.util.Set;
 import java.util.TimeZone;
 import org.apache.calcite.DataContext;
 import org.apache.calcite.DataContext.Variable;
+import org.apache.calcite.adapter.java.JavaTypeFactory;
 import org.apache.calcite.linq4j.Ord;
+import org.apache.calcite.linq4j.QueryProvider;
 import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptUtil;
@@ -84,6 +86,7 @@ import org.apache.calcite.rex.RexUnknownAs;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.rex.RexVisitor;
 import org.apache.calcite.rex.RexVisitorImpl;
+import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
@@ -1191,6 +1194,35 @@ public class RexUtils {
         return val;
     }
 
+    /** Context which triggers assertion if unexpected method is called. */
+    public static class FaultyContext implements DataContext {
+        public static final FaultyContext INSTANCE = new FaultyContext();
+
+        /** {@inheritDoc} */
+        @Override
+        public SchemaPlus getRootSchema() {
+            throw new AssertionError("should not be called");
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public JavaTypeFactory getTypeFactory() {
+            throw new AssertionError("should not be called");
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public QueryProvider getQueryProvider() {
+            throw new AssertionError("should not be called");
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public @Nullable Object get(String name) {
+            throw new AssertionError("Should not call: [" + name + "] from 
current context.");
+        }
+    }
+
     private static Object convertNumericLiteral(RelDataType dataType, Number 
value, Class<?> type) {
         Primitive primitive = Primitive.ofBoxOr(type);
         assert primitive != null || type == BigDecimal.class : "Neither 
primitive nor BigDecimal: " + type;
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AbstractTpcQueryPlannerTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AbstractTpcQueryPlannerTest.java
index 14db039d7e9..345c36d24c2 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AbstractTpcQueryPlannerTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AbstractTpcQueryPlannerTest.java
@@ -41,7 +41,7 @@ import java.util.regex.Pattern;
 import org.apache.ignite.internal.sql.engine.framework.TestBuilders;
 import org.apache.ignite.internal.sql.engine.framework.TestCluster;
 import org.apache.ignite.internal.sql.engine.framework.TestNode;
-import org.apache.ignite.internal.sql.engine.prepare.MultiStepPlan;
+import org.apache.ignite.internal.sql.engine.prepare.ExplainablePlan;
 import org.apache.ignite.internal.sql.engine.prepare.QueryPlan;
 import org.apache.ignite.internal.sql.engine.util.TpcScaleFactor;
 import org.apache.ignite.internal.sql.engine.util.TpcTable;
@@ -120,7 +120,7 @@ abstract class AbstractTpcQueryPlannerTest extends 
AbstractPlannerTest {
         int pos = 0;
 
         for (QueryPlan plan : plans) {
-            MultiStepPlan plan0 = (MultiStepPlan) plan;
+            ExplainablePlan plan0 = (ExplainablePlan) plan;
             String actualPlan = plan0.explain();
 
             if (planUpdater != null) {
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/partitionawareness/PartitionAwarenessMetadataTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/partitionawareness/PartitionAwarenessMetadataTest.java
index 304b192f1ce..6f7e60928b0 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/partitionawareness/PartitionAwarenessMetadataTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/prepare/partitionawareness/PartitionAwarenessMetadataTest.java
@@ -38,6 +38,8 @@ import 
org.apache.ignite.internal.sql.engine.framework.TestNode;
 import org.apache.ignite.internal.sql.engine.prepare.QueryPlan;
 import org.apache.ignite.internal.sql.engine.util.Commons;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.apache.ignite.internal.type.NativeTypes;
+import org.apache.ignite.internal.util.ColocationUtils;
 import org.jetbrains.annotations.Nullable;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
@@ -121,26 +123,50 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
     private static Stream<Arguments> simpleKeyMetadata() {
         return Stream.of(
                 // KV GET
-                Arguments.of("SELECT * FROM t WHERE c1=?", dynamicParams(0)),
-                Arguments.of("SELECT * FROM t WHERE c2=? and c1=?", 
dynamicParams(1)),
-                Arguments.of("SELECT * FROM t WHERE c1=1", null),
-                Arguments.of("SELECT * FROM t WHERE c1=1+1", null),
+                Arguments.of("SELECT * FROM t WHERE c1=?", meta(0)),
+                Arguments.of("SELECT * FROM t WHERE c2=? and c1=?", meta(1)),
+                Arguments.of("SELECT * FROM t WHERE c1=" + Long.MAX_VALUE, 
null),
+                Arguments.of("SELECT * FROM t WHERE c1=1", meta(new int[]{-1}, 
new int[]{1})),
+                Arguments.of("SELECT * FROM t WHERE c1=1+1", meta(new 
int[]{-1}, new int[]{2})),
                 // the first condition goes into key lookup other into a 
post-lookup filter.
-                Arguments.of("SELECT * FROM t WHERE c1=? and c1=?", 
dynamicParams(0)),
-                Arguments.of("SELECT * FROM t WHERE c2=? and c1=? and c1=?", 
dynamicParams(1)),
+                Arguments.of("SELECT * FROM t WHERE c1=? and c1=?", meta(0)),
+                Arguments.of("SELECT * FROM t WHERE c2=? and c1=? and c1=?", 
meta(1)),
 
                 // KV PUT
-                Arguments.of("INSERT INTO t VALUES(?, ?)", 
dynamicParamsTrackingRequired(0)),
-                Arguments.of("INSERT INTO t VALUES(1, ?)", null),
+                Arguments.of("INSERT INTO t VALUES(?, ?)", 
metaTrackingRequired(0)),
+                Arguments.of("INSERT INTO t VALUES(1, ?)", 
metaTrackingRequired(new int[]{-1}, new int[]{1})),
                 Arguments.of("INSERT INTO t VALUES(1+1, ?)", null),
-                Arguments.of("INSERT INTO t(c2, c1) VALUES(?, ?)", 
dynamicParamsTrackingRequired(1)),
-                Arguments.of("INSERT INTO t(c2, c1) VALUES(1, ?)", 
dynamicParamsTrackingRequired(0)),
-                Arguments.of("INSERT INTO t(c2, c1) VALUES(?, 1)", null),
+                Arguments.of("INSERT INTO t(c2, c1) VALUES(?, ?)", 
metaTrackingRequired(1)),
+                Arguments.of("INSERT INTO t(c2, c1) VALUES(1, ?)", 
metaTrackingRequired(0)),
+                Arguments.of("INSERT INTO t(c2, c1) VALUES(?, 1)", 
metaTrackingRequired(new int[]{-1}, new int[]{1})),
 
                 // KV DELETE
-                Arguments.of("DELETE FROM t WHERE c1=?", 
dynamicParamsTrackingRequired(0)),
-                Arguments.of("DELETE FROM t WHERE c1=1", null),
-                Arguments.of("DELETE FROM t WHERE c1=1+1", null)
+                Arguments.of("DELETE FROM t WHERE c1=?", 
metaTrackingRequired(0)),
+                Arguments.of("DELETE FROM t WHERE c1=1", 
metaTrackingRequired(new int[]{-1}, new int[]{1})),
+                Arguments.of("DELETE FROM t WHERE c1=1+1", 
metaTrackingRequired(new int[]{-1}, new int[]{2}))
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("tsKeyMetadata")
+    public void tsCompoundKey(String query, PartitionAwarenessMetadata 
expected) {
+        node.initSchema("CREATE TABLE t (c1 INT, c2 INT, c3 TIMESTAMP WITH 
LOCAL TIME ZONE, PRIMARY KEY (c3, c2))");
+
+        QueryPlan plan = node.prepare(query);
+        PartitionAwarenessMetadata metadata = 
plan.partitionAwarenessMetadata();
+
+        expectMetadata(expected, metadata);
+    }
+
+    private static Stream<Arguments> tsKeyMetadata() {
+        return Stream.of(
+                // KV GET
+                Arguments.of("SELECT * FROM t WHERE c3=? and c2=?", meta(0, 
1)),
+                Arguments.of("SELECT * FROM t WHERE c2=? and c3 = TIMESTAMP 
WITH LOCAL TIME ZONE '1970-01-01 00:00:00'", null),
+
+                // KV PUT
+                Arguments.of("INSERT INTO t VALUES (?, ?, ?)",  
metaTrackingRequired(2, 1)),
+                Arguments.of("INSERT INTO t (c1, c2, c3) VALUES (?, ?, 
TIMESTAMP WITH LOCAL TIME ZONE '1970-01-01 00:00:00')", null)
         );
     }
 
@@ -158,33 +184,33 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
     private static Stream<Arguments> shortKeyMetadata() {
         return Stream.of(
                 // KV GET
-                Arguments.of("SELECT * FROM t WHERE c3=? and c2=?", 
dynamicParams(0)),
-                Arguments.of("SELECT * FROM t WHERE c2=? and c3=?", 
dynamicParams(1)),
-                Arguments.of("SELECT * FROM t WHERE c1=? and c2=? and c3=?", 
dynamicParams(2)),
-                Arguments.of("SELECT * FROM t WHERE c3=? and c1=? and c2=?", 
dynamicParams(0)),
-                Arguments.of("SELECT * FROM t WHERE c3=? and c2=1", 
dynamicParams(0)),
-                Arguments.of("SELECT * FROM t WHERE c1=1 and c2=? and c3=?", 
dynamicParams(1)),
+                Arguments.of("SELECT * FROM t WHERE c3=? and c2=?", meta(0)),
+                Arguments.of("SELECT * FROM t WHERE c2=? and c3=?", meta(1)),
+                Arguments.of("SELECT * FROM t WHERE c1=? and c2=? and c3=?", 
meta(2)),
+                Arguments.of("SELECT * FROM t WHERE c3=? and c1=? and c2=?", 
meta(0)),
+                Arguments.of("SELECT * FROM t WHERE c3=? and c2=1", meta(0)),
+                Arguments.of("SELECT * FROM t WHERE c1=1 and c2=? and c3=?", 
meta(1)),
 
                 Arguments.of("SELECT * FROM t WHERE c3=3", null),
-                Arguments.of("SELECT * FROM t WHERE c2=? and c3=3", null),
-                Arguments.of("SELECT * FROM t WHERE c1=? and c2=? and c3=3", 
null),
+                Arguments.of("SELECT * FROM t WHERE c2=? and c3=3", meta(new 
int[]{-1}, new int[]{3})),
+                Arguments.of("SELECT * FROM t WHERE c1=? and c2=? and c3=3", 
meta(new int[]{-1}, new int[]{3})),
 
                 // KV PUT
-                Arguments.of("INSERT INTO t VALUES (?, ?, ?)",  
dynamicParamsTrackingRequired(2)),
-                Arguments.of("INSERT INTO t (c1, c2, c3) VALUES (?, ?, ?)", 
dynamicParamsTrackingRequired(2)),
-                Arguments.of("INSERT INTO t (c3, c1, c2) VALUES (?, ?, ?)", 
dynamicParamsTrackingRequired(0)),
+                Arguments.of("INSERT INTO t VALUES (?, ?, ?)",  
metaTrackingRequired(2)),
+                Arguments.of("INSERT INTO t (c1, c2, c3) VALUES (?, ?, ?)", 
metaTrackingRequired(2)),
+                Arguments.of("INSERT INTO t (c3, c1, c2) VALUES (?, ?, ?)", 
metaTrackingRequired(0)),
 
-                Arguments.of("INSERT INTO t (c1, c2, c3) VALUES (?, ?, 3)", 
null),
-                Arguments.of("INSERT INTO t (c1, c3, c2) VALUES (?, 3, ?)", 
null),
-                Arguments.of("INSERT INTO t (c3, c1, c2) VALUES (3, ?, ?)", 
null),
+                Arguments.of("INSERT INTO t (c1, c2, c3) VALUES (?, ?, 3)", 
metaTrackingRequired(new int[]{-1}, new int[]{3})),
+                Arguments.of("INSERT INTO t (c1, c3, c2) VALUES (?, 3, ?)", 
metaTrackingRequired(new int[]{-1}, new int[]{3})),
+                Arguments.of("INSERT INTO t (c3, c1, c2) VALUES (3, ?, ?)", 
metaTrackingRequired(new int[]{-1}, new int[]{3})),
 
                 // KV DELETE
-                Arguments.of("SELECT * FROM t WHERE c3=? and c2=?", 
dynamicParams(0)),
-                Arguments.of("SELECT * FROM t WHERE c2=? and c3=?", 
dynamicParams(1)),
-                Arguments.of("SELECT * FROM t WHERE c3=? and c2=1", 
dynamicParams(0)),
+                Arguments.of("DELETE FROM t WHERE c3=? and c2=?", 
metaTrackingRequired(0)),
+                Arguments.of("DELETE FROM t WHERE c2=? and c3=?", 
metaTrackingRequired(1)),
+                Arguments.of("DELETE FROM t WHERE c3=? and c2=1", 
metaTrackingRequired(0)),
 
-                Arguments.of("SELECT * FROM t WHERE c3=3", null),
-                Arguments.of("SELECT * FROM t WHERE c2=? and c3=3", null)
+                Arguments.of("DELETE FROM t WHERE c3=3", null),
+                Arguments.of("DELETE FROM t WHERE c2=? and c3=3", 
metaTrackingRequired(new int[]{-1}, new int[]{3}))
         );
     }
 
@@ -202,41 +228,66 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
     private static Stream<Arguments> compoundKeyMetadata() {
         return Stream.of(
                 // KV GET
-                Arguments.of("SELECT * FROM t WHERE c1=? and c2=? and c3=?", 
dynamicParams(2, 0, 1)),
-                Arguments.of("SELECT * FROM t WHERE c3=? and c1=? and c2=?", 
dynamicParams(0, 1, 2)),
-                Arguments.of("SELECT * FROM t WHERE c3=? and c2=? and c1=?", 
dynamicParams(0, 2, 1)),
-                Arguments.of("SELECT * FROM t WHERE c4=? and c1=? and c2=? and 
1=? and c3=?", dynamicParams(4, 1, 2)),
+                Arguments.of("SELECT * FROM t WHERE c1=? and c2=? and c3=?", 
meta(2, 0, 1)),
+                Arguments.of("SELECT * FROM t WHERE c3=? and c1=? and c2=?", 
meta(0, 1, 2)),
+                Arguments.of("SELECT * FROM t WHERE c3=? and c2=? and c1=?", 
meta(0, 2, 1)),
+
+                Arguments.of("SELECT * FROM t WHERE c3=1 and c1=? and c2=?", 
meta(new int[]{-1, 0, 1}, new int[]{1})),
+                Arguments.of("SELECT * FROM t WHERE c3=? and c1=1 and c2=?", 
meta(new int[]{0, -1, 1}, new int[]{1})),
+                Arguments.of("SELECT * FROM t WHERE c3=? and c1=? and c2=1", 
meta(new int[]{0, 1, -1}, new int[]{1})),
+
+                Arguments.of("SELECT * FROM t WHERE c3=1 and c1=1 and c2=?", 
meta(new int[]{-1, -2, 0}, new int[]{1, 1})),
+                Arguments.of("SELECT * FROM t WHERE c3=1 and c1=1 and c2=1", 
meta(new int[]{-1, -2, -3}, new int[]{1, 1, 1})),
+
+                Arguments.of("SELECT * FROM t WHERE c4=? and c1=? and c2=? and 
1=? and c3=?", meta(4, 1, 2)),
                 // duplicate condition goes to a post lookup filter.
-                Arguments.of("SELECT * FROM t WHERE c1=? and c2=? and c3=? and 
c2=?", dynamicParams(2, 0, 1)),
+                Arguments.of("SELECT * FROM t WHERE c1=? and c2=? and c3=? and 
c2=?", meta(2, 0, 1)),
 
-                Arguments.of("SELECT * FROM t WHERE c1=1 and c2=? and c3=?", 
null),
-                Arguments.of("SELECT * FROM t WHERE c1=? and c2=2 and c3=?", 
null),
-                Arguments.of("SELECT * FROM t WHERE c1=? and c2=? and c3=3", 
null),
-                Arguments.of("SELECT * FROM t WHERE c1=1 and c2=2 and c3=3", 
null),
+                Arguments.of("SELECT * FROM t WHERE c1=1 and c2=? and c3=?", 
meta(new int[]{1, -1, 0}, new int[]{1})),
+                Arguments.of("SELECT * FROM t WHERE c1=? and c2=2 and c3=?", 
meta(new int[]{1, 0, -1}, new int[]{2})),
+                Arguments.of("SELECT * FROM t WHERE c1=? and c2=? and c3=3", 
meta(new int[]{-1, 0, 1}, new int[]{3})),
+                Arguments.of("SELECT * FROM t WHERE c1=1 and c2=2 and c3=3", 
meta(new int[]{-1, -2, -3}, new int[]{3, 1, 2})),
 
                 // KV PUT
-                Arguments.of("INSERT INTO t VALUES (?, ?, ?, ?)",  
dynamicParamsTrackingRequired(2, 0, 1)),
-                Arguments.of("INSERT INTO t (c3, c2, c4, c1) VALUES (?, ?, ?, 
?)", dynamicParamsTrackingRequired(0, 3, 1)),
-                Arguments.of("INSERT INTO t (c3, c2, c4, c1) VALUES (?, ?, 1, 
?)", dynamicParamsTrackingRequired(0, 2, 1)),
+                Arguments.of("INSERT INTO t VALUES (1, ?, ?, ?)", 
metaTrackingRequired(new int[]{1, -1, 0}, new int[]{1})),
+                Arguments.of("INSERT INTO t (c3, c2, c4, c1) VALUES (?, ?, ?, 
?)", metaTrackingRequired(0, 3, 1)),
+                Arguments.of("INSERT INTO t (c3, c2, c4, c1) VALUES (?, ?, 1, 
?)", metaTrackingRequired(0, 2, 1)),
+                Arguments.of("INSERT INTO t VALUES (1, 2, 3, 4)", null),
 
                 // KV DELETE
-                Arguments.of("DELETE FROM t WHERE c1=? and c2=? and c3=?", 
dynamicParamsTrackingRequired(2, 0, 1)),
-                Arguments.of("DELETE FROM t WHERE c3=? and c1=? and c2=?", 
dynamicParamsTrackingRequired(0, 1, 2)),
-                Arguments.of("DELETE FROM t WHERE c3=? and c2=? and c1=?", 
dynamicParamsTrackingRequired(0, 2, 1)),
-
-                Arguments.of("DELETE FROM t WHERE c1=1 and c2=? and c3=?", 
null),
-                Arguments.of("DELETE FROM t WHERE c1=? and c2=2 and c3=?", 
null),
-                Arguments.of("DELETE FROM t WHERE c1=? and c2=? and c3=3", 
null),
-                Arguments.of("DELETE FROM t WHERE c1=1 and c2=2 and c3=3", 
null)
+                Arguments.of("DELETE FROM t WHERE c1=? and c2=? and c3=?", 
metaTrackingRequired(2, 0, 1)),
+                Arguments.of("DELETE FROM t WHERE c3=? and c1=? and c2=?", 
metaTrackingRequired(0, 1, 2)),
+                Arguments.of("DELETE FROM t WHERE c3=? and c2=? and c1=?", 
metaTrackingRequired(0, 2, 1)),
+
+                Arguments.of("DELETE FROM t WHERE c1=1 and c2=? and c3=?", 
metaTrackingRequired(new int[]{1, -1, 0}, new int[]{1})),
+                Arguments.of("DELETE FROM t WHERE c1=? and c2=2 and c3=?", 
metaTrackingRequired(new int[]{1, 0, -1}, new int[]{2})),
+                Arguments.of("DELETE FROM t WHERE c1=? and c2=? and c3=3", 
metaTrackingRequired(new int[]{-1, 0, 1}, new int[]{3})),
+                Arguments.of("DELETE FROM t WHERE c1=1 and c2=2 and c3=3", 
metaTrackingRequired(new int[]{-1, -2, -3}, new int[]{3, 1, 2}))
         );
     }
 
-    private static PartitionAwarenessMetadata dynamicParams(int... 
dynamicParams) {
-        return new PartitionAwarenessMetadata(1, dynamicParams, new int[0], 
DirectTxMode.SUPPORTED);
+    private static PartitionAwarenessMetadata meta(int... dynamicParams) {
+        return meta(dynamicParams, new int[0], DirectTxMode.SUPPORTED);
+    }
+
+    private static PartitionAwarenessMetadata meta(int[] dynamicParams, int[] 
toHash) {
+        return meta(dynamicParams, toHash, DirectTxMode.SUPPORTED);
+    }
+
+    private static PartitionAwarenessMetadata meta(int[] dynamicParams, int[] 
toHash, DirectTxMode mode) {
+        int[] hashes = new int[toHash.length];
+        for (int i = 0; i < toHash.length; ++i) {
+            hashes[i] = ColocationUtils.hash(toHash[i], NativeTypes.INT32);
+        }
+        return new PartitionAwarenessMetadata(1, dynamicParams, hashes, mode);
+    }
+
+    private static PartitionAwarenessMetadata metaTrackingRequired(int[] 
dynamicParams, int[] toHash) {
+        return meta(dynamicParams, toHash, 
DirectTxMode.SUPPORTED_TRACKING_REQUIRED);
     }
 
-    private static PartitionAwarenessMetadata 
dynamicParamsTrackingRequired(int... dynamicParams) {
-        return new PartitionAwarenessMetadata(1, dynamicParams, new int[0], 
DirectTxMode.SUPPORTED_TRACKING_REQUIRED);
+    private static PartitionAwarenessMetadata metaTrackingRequired(int... 
dynamicParams) {
+        return meta(dynamicParams, new int[0], 
DirectTxMode.SUPPORTED_TRACKING_REQUIRED);
     }
 
     private static void expectMetadata(PartitionAwarenessMetadata expected, 
@Nullable PartitionAwarenessMetadata actual) {
@@ -258,6 +309,12 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
                     
Arrays.stream(actual.indexes()).boxed().collect(Collectors.toList()),
                     "indexes"
             );
+
+            assertEquals(
+                    
Arrays.stream(expected.hash()).boxed().collect(Collectors.toList()),
+                    
Arrays.stream(actual.hash()).boxed().collect(Collectors.toList()),
+                    "hashes"
+            );
         }
     }
 }

Reply via email to