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

jooger 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 d250e635e35 IGNITE-25560  Sql.  Partition awareness cover extension 
(#6418)
d250e635e35 is described below

commit d250e635e35b9bc5196c52a210c472f2c54b3206
Author: Max Zhuravkov <[email protected]>
AuthorDate: Wed Aug 20 09:38:09 2025 +0300

    IGNITE-25560  Sql.  Partition awareness cover extension (#6418)
---
 .../sql/engine/prepare/PrepareServiceImpl.java     |  81 +++++++---
 .../sql/engine/prepare/RelWithSources.java         |  67 +++++++++
 .../PartitionAwarenessMetadata.java                |   3 +-
 .../PartitionAwarenessMetadataExtractor.java       | 163 ++++++++++++++++++---
 .../ignite/internal/sql/engine/util/Cloner.java    |  42 ++++--
 .../sql/engine/exec/mapping/MappingTestRunner.java |  20 ++-
 .../planner/PartitionPruningMetadataTest.java      |   6 +-
 .../sql/engine/planner/PartitionPruningTest.java   |  22 +++
 .../PartitionAwarenessMetadataTest.java            | 148 +++++++++++++++----
 9 files changed, 462 insertions(+), 90 deletions(-)

diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PrepareServiceImpl.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PrepareServiceImpl.java
index 395973ad106..0f24814d15d 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PrepareServiceImpl.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/PrepareServiceImpl.java
@@ -26,7 +26,6 @@ import static 
org.apache.ignite.internal.sql.engine.util.Commons.fastQueryOptimi
 import static 
org.apache.ignite.internal.thread.ThreadOperation.NOTHING_ALLOWED;
 import static org.apache.ignite.lang.ErrorGroups.Sql.EXECUTION_CANCELLED_ERR;
 
-import it.unimi.dsi.fastutil.ints.IntObjectPair;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -453,14 +452,27 @@ public class PrepareServiceImpl implements PrepareService 
{
                     IgniteKeyValueGet kvGet = (IgniteKeyValueGet) optimizedRel;
 
                     return new KeyValueGetPlan(
-                            nextPlanId(), catalogVersion, kvGet, 
resultSetMetadata,
-                            parameterMetadata, relWithMetadata.paMetadata, 
relWithMetadata.ppMetadata
+                            nextPlanId(), 
+                            catalogVersion, 
+                            kvGet, 
+                            resultSetMetadata,
+                            parameterMetadata,
+                            relWithMetadata.paMetadata, 
+                            relWithMetadata.ppMetadata
                     );
                 }
 
                 var plan = new MultiStepPlan(
-                        nextPlanId(), SqlQueryType.QUERY, optimizedRel, 
resultSetMetadata, parameterMetadata, catalogVersion, 
-                        relWithMetadata.numTables, fastPlan, 
relWithMetadata.paMetadata, relWithMetadata.ppMetadata
+                        nextPlanId(), 
+                        SqlQueryType.QUERY, 
+                        optimizedRel, 
+                        resultSetMetadata, 
+                        parameterMetadata, 
+                        catalogVersion, 
+                        relWithMetadata.numSources, 
+                        fastPlan, 
+                        relWithMetadata.paMetadata,
+                        relWithMetadata.ppMetadata
                 );
 
                 logPlan(parsedResult.originalQuery(), plan);
@@ -523,13 +535,25 @@ public class PrepareServiceImpl implements PrepareService 
{
         ExplainablePlan plan;
         if (optimizedRel instanceof IgniteKeyValueModify) {
             plan = new KeyValueModifyPlan(
-                    nextPlanId(), ctx.catalogVersion(), (IgniteKeyValueModify) 
optimizedRel, DML_METADATA,
-                    parameterMetadata, null, null
+                    nextPlanId(),
+                    ctx.catalogVersion(), 
+                    (IgniteKeyValueModify) optimizedRel,
+                    DML_METADATA,
+                    parameterMetadata, 
+                    relWithMetadata.paMetadata,
+                    relWithMetadata.ppMetadata
             );
         } else {
             plan = new MultiStepPlan(
-                    nextPlanId(), SqlQueryType.DML, optimizedRel, 
DML_METADATA, parameterMetadata, ctx.catalogVersion(), 
-                    relWithMetadata.numTables, null, 
relWithMetadata.paMetadata, relWithMetadata.ppMetadata
+                    nextPlanId(),
+                    SqlQueryType.DML,
+                    optimizedRel, DML_METADATA,
+                    parameterMetadata,
+                    ctx.catalogVersion(), 
+                    relWithMetadata.numSources, 
+                    null, 
+                    relWithMetadata.paMetadata,
+                    relWithMetadata.ppMetadata
             );
         }
 
@@ -584,13 +608,26 @@ public class PrepareServiceImpl implements PrepareService 
{
                     IgniteKeyValueModify kvModify = (IgniteKeyValueModify) 
optimizedRel;
 
                     plan = new KeyValueModifyPlan(
-                            nextPlanId(), catalogVersion, kvModify, 
DML_METADATA,
-                            parameterMetadata, relWithMetadata.paMetadata, 
relWithMetadata.ppMetadata
+                            nextPlanId(), 
+                            catalogVersion,
+                            kvModify,
+                            DML_METADATA,
+                            parameterMetadata, 
+                            relWithMetadata.paMetadata, 
+                            relWithMetadata.ppMetadata
                     );
                 } else {
                     plan = new MultiStepPlan(
-                            nextPlanId(), SqlQueryType.DML, optimizedRel, 
DML_METADATA, parameterMetadata, catalogVersion,
-                            relWithMetadata.numTables, null, 
relWithMetadata.paMetadata, relWithMetadata.ppMetadata
+                            nextPlanId(), 
+                            SqlQueryType.DML,
+                            optimizedRel,
+                            DML_METADATA, 
+                            parameterMetadata, 
+                            catalogVersion,
+                            relWithMetadata.numSources, 
+                            null, 
+                            relWithMetadata.paMetadata, 
+                            relWithMetadata.ppMetadata
                     );
                 }
 
@@ -757,16 +794,16 @@ public class PrepareServiceImpl implements PrepareService 
{
         // cluster keeps a lot of cached stuff that won't be used anymore.
         // In order let GC collect that, let's reattach tree to an empty 
cluster
         // before storing tree in plan cache
-        IntObjectPair<IgniteRel> pair = 
Cloner.cloneAndAssignSourceId(igniteRel, Commons.emptyCluster());
-        int numTables = pair.firstInt();
-        IgniteRel rel = pair.right();
-
-        PartitionAwarenessMetadata partitionAwarenessMetadata =
-                PartitionAwarenessMetadataExtractor.getMetadata(rel);
+        RelWithSources reWithSources = 
Cloner.cloneAndAssignSourceId(igniteRel, Commons.emptyCluster());
+        int numTables = reWithSources.sources().size();
+        IgniteRel rel = reWithSources.root();
 
         PartitionPruningMetadata partitionPruningMetadata = new 
PartitionPruningMetadataExtractor()
                 .go(rel);
 
+        PartitionAwarenessMetadata partitionAwarenessMetadata =
+                PartitionAwarenessMetadataExtractor.getMetadata(reWithSources, 
partitionPruningMetadata);
+
         return new RelWithMetadata(rel, numTables, partitionAwarenessMetadata, 
partitionPruningMetadata);
     }
 
@@ -861,16 +898,16 @@ public class PrepareServiceImpl implements PrepareService 
{
         final IgniteRel rel;
         final @Nullable PartitionAwarenessMetadata paMetadata;
         final @Nullable PartitionPruningMetadata ppMetadata;
-        final int numTables;
+        final int numSources;
 
         RelWithMetadata(
                 IgniteRel rel,
-                int numTables,
+                int numSources,
                 @Nullable PartitionAwarenessMetadata paMetadata, 
                 @Nullable PartitionPruningMetadata ppMetadata
         ) {
             this.rel = rel;
-            this.numTables = numTables;
+            this.numSources = numSources;
             this.paMetadata = paMetadata;
             this.ppMetadata = ppMetadata;
         }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/RelWithSources.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/RelWithSources.java
new file mode 100644
index 00000000000..866b6ef8b4c
--- /dev/null
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/RelWithSources.java
@@ -0,0 +1,67 @@
+/*
+ * 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.sql.engine.prepare;
+
+import it.unimi.dsi.fastutil.ints.IntSet;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import org.apache.ignite.internal.sql.engine.rel.IgniteRel;
+
+/**
+ * Relation with sources.
+ */
+public final class RelWithSources {
+
+    private final IgniteRel rel;
+
+    private final Long2ObjectMap<IgniteRel> sourceRels;
+
+    private final IntSet modifiedTables;
+
+    /**
+     * Constructor.
+     *
+     * @param rel Relation.
+     * @param sourceRels Source relations.
+     * @param modifiedTables Modified tables.
+     */
+    public RelWithSources(IgniteRel rel, Long2ObjectMap<IgniteRel> sourceRels, 
IntSet modifiedTables) {
+        this.rel = rel;
+        this.sourceRels = sourceRels;
+        this.modifiedTables = modifiedTables;
+    }
+
+    /** Plan. */
+    public IgniteRel root() {
+        return rel;
+    }
+
+    /** Returns tables modified by the plan. */
+    public IntSet modifiedTables() {
+        return modifiedTables;
+    }
+
+    /** Returns a source relation by the given id. */
+    public IgniteRel get(long sourceId) {
+        return sourceRels.get(sourceId);
+    }
+
+    /** Returns a map of source relations. */
+    public Long2ObjectMap<IgniteRel> sources() {
+        return sourceRels;
+    }
+}
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/partitionawareness/PartitionAwarenessMetadata.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/partitionawareness/PartitionAwarenessMetadata.java
index 49e836b12bd..bb9c6512105 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/partitionawareness/PartitionAwarenessMetadata.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/partitionawareness/PartitionAwarenessMetadata.java
@@ -110,7 +110,8 @@ public final class PartitionAwarenessMetadata {
     public String toString() {
         return S.toString(PartitionAwarenessMetadata.class, this,
                 "indexes", Arrays.toString(indexes),
-                "hash", Arrays.toString(hash)
+                "hash", Arrays.toString(hash),
+                "directTxMode", directTxMode
         );
     }
 }
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 899f9110518..a0de72232fd 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,19 +17,27 @@
 
 package org.apache.ignite.internal.sql.engine.prepare.partitionawareness;
 
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
 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.rex.RexUtil;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.ImmutableIntList;
+import org.apache.ignite.internal.sql.engine.prepare.RelWithSources;
+import 
org.apache.ignite.internal.sql.engine.prepare.pruning.PartitionPruningColumns;
+import 
org.apache.ignite.internal.sql.engine.prepare.pruning.PartitionPruningMetadata;
 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.rel.IgniteRel;
+import org.apache.ignite.internal.sql.engine.rel.IgniteTableFunctionScan;
 import org.apache.ignite.internal.sql.engine.schema.IgniteTable;
+import org.apache.ignite.internal.sql.engine.sql.fun.IgniteSqlOperatorTable;
 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;
@@ -67,15 +75,22 @@ public class PartitionAwarenessMetadataExtractor {
     /**
      * Extracts partition awareness metadata from the given plan.
      *
-     * @param rel Plan.
+     * @param relationWithSources Relation with sources.
+     * @param partitionPruningMetadata Partition-pruning metadata.
      * @return Metadata.
      */
-    @Nullable
-    public static PartitionAwarenessMetadata getMetadata(IgniteRel rel) {
+    public static @Nullable PartitionAwarenessMetadata getMetadata(
+            RelWithSources relationWithSources,
+            @Nullable PartitionPruningMetadata partitionPruningMetadata
+    ) {
+        IgniteRel rel = relationWithSources.root();
+
         if (rel instanceof IgniteKeyValueGet) {
             return getMetadata((IgniteKeyValueGet) rel);
         } else if (rel instanceof IgniteKeyValueModify) {
             return getMetadata((IgniteKeyValueModify) rel);
+        } else if (partitionPruningMetadata != null) {
+            return tryConvertPartitionPruningMetadata(relationWithSources, 
partitionPruningMetadata);
         } else {
             return null;
         }
@@ -128,8 +143,6 @@ public class PartitionAwarenessMetadataExtractor {
         int[] indexes = new int[colocationKeys.size()];
         IntArrayList hashFields = new IntArrayList(colocationKeys.size() / 2);
 
-        int hashPos = -1;
-
         for (int i = 0; i < colocationKeys.size(); i++) {
             int colIdx = colocationKeys.get(i);
             RexNode expr;
@@ -141,34 +154,140 @@ public class PartitionAwarenessMetadataExtractor {
                 expr = expressions.get(keyIdx);
             }
 
-            if (expr instanceof RexDynamicParam) {
-                RexDynamicParam dynamicParam = (RexDynamicParam) expr;
-                indexes[i] = dynamicParam.getIndex();
-            } else if (expr instanceof RexLiteral) {
-                RexLiteral expr0 = (RexLiteral) expr;
+            boolean added = addToMetadata(expr, indexes, i, hashFields);
+            if (!added) {
+                return null;
+            }
+        }
+
+        int[] hash = hashFields.toIntArray();
+
+        return new PartitionAwarenessMetadata(igniteTable.id(), indexes, hash, 
directTxMode);
+    }
+
+    private static @Nullable PartitionAwarenessMetadata 
tryConvertPartitionPruningMetadata(
+            RelWithSources relationWithSources,
+            PartitionPruningMetadata metadata
+    ) {
+        // Partition awareness metadata is created once per source table,
+        // so we do not consider plan that have more then 1 source.
+        if (metadata.data().size() != 1) {
+            return null;
+        }
 
-                // depends on supplied zoneId, it can`t be cached
-                if (expr0.getTypeName() == 
SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) {
-                    return null;
-                }
+        Long2ObjectMap.Entry<PartitionPruningColumns> entry = metadata.data()
+                .long2ObjectEntrySet()
+                .iterator().next();
 
-                indexes[i] = hashPos--;
+        long sourceId = entry.getLongKey();
+        IgniteRel sourceRel = relationWithSources.get(sourceId);
+        assert sourceRel != null;
 
-                Class<?> internalType = Primitives.wrap((Class<?>) 
Commons.typeFactory().getJavaClass(expr0.getType()));
-                Object val = RexUtils.literalValue(FaultyContext.INSTANCE, 
expr0, internalType);
+        RelOptTable optTable = sourceRel.getTable();
+        assert optTable != null;
+
+        IgniteTable igniteTable = optTable.unwrap(IgniteTable.class);
+        assert igniteTable != null;
 
-                NativeType nativeType = 
IgniteTypeFactory.relDataTypeToNative(expr0.getType());
+        // Partition pruning (PP) metadata includes information to identify 
all possible partitions. 
+        // However, partition awareness restricts execution to a single 
partition, 
+        // so we should reject PP metadata that has more than one set of 
columns.
+        //
+        // Ignore PP with correlated variables as well, because some queries 
+        // can access additional partitions.
+        PartitionPruningColumns columns = entry.getValue();
+        if (columns.columns().size() != 1 || 
columns.containCorrelatedVariables()) {
+            return null;
+        }
 
-                val = TypeUtils.fromInternal(val, nativeType.spec());
+        boolean dml = 
relationWithSources.modifiedTables().contains(igniteTable.id());
+        long numSources = numberOfModifyAndSourceRels(relationWithSources);
 
-                hashFields.add(ColocationUtils.hash(val, nativeType));
-            } else {
+        // Accept queries that have exactly one source rel.
+        if (!dml && numSources != 1) {
+            return null;
+        }
+
+        // Accept DMLs that have a ModifyNode and a single source rel.
+        if (dml && numSources != 2) {
+            return null;
+        }
+
+        // Choose appropriate tx mode.
+        DirectTxMode directTxMode = dml ? DirectTxMode.NOT_SUPPORTED : 
DirectTxMode.SUPPORTED;
+
+        ImmutableIntList colocationKeys = igniteTable.distribution().getKeys();
+        int[] indexes = new int[colocationKeys.size()];
+        IntArrayList hashFields = new IntArrayList(colocationKeys.size());
+        Int2ObjectMap<RexNode> cols = columns.columns().get(0);
+
+        for (Int2ObjectMap.Entry<RexNode> colEntry : 
cols.int2ObjectEntrySet()) {
+            RexNode colExpr = colEntry.getValue();
+            int colIdx = colEntry.getIntKey();
+            int i = colocationKeys.indexOf(colIdx);
+            assert i >= 0 : "Invalid colocation column index: " + 
cols.keySet();
+
+            boolean added = addToMetadata(colExpr, indexes, i, hashFields);
+            if (!added) {
                 return null;
             }
         }
 
-        int[] hash = hashFields.toArray(new int[0]);
+        int[] hash = hashFields.toIntArray();
 
         return new PartitionAwarenessMetadata(igniteTable.id(), indexes, hash, 
directTxMode);
     }
+
+    private static long numberOfModifyAndSourceRels(RelWithSources 
relationWithSources) {
+        Long2ObjectMap<IgniteRel> sources = relationWithSources.sources();
+        // When counting the number of source relations ignore safe table 
functions as they these function 
+        // produce the same result and do not affect / are not affected by 
data distribution.
+        return sources.values().stream()
+                .filter(r -> {
+                    if (!(r instanceof IgniteTableFunctionScan)) {
+                        return true;
+                    } else {
+                        // Only allow to use the SYSTEM_RANGE table function,
+                        // since that function always produces the same results
+                        // and does not require any external dependencies to 
run.
+                        IgniteTableFunctionScan scan = 
(IgniteTableFunctionScan) r;
+                        return !RexUtil.isCallTo(scan.getCall(), 
IgniteSqlOperatorTable.SYSTEM_RANGE);
+                    }
+                })
+                .count();
+    }
+
+    private static boolean addToMetadata(RexNode colExpr, int[] indexes, int 
i, IntArrayList hashFields) {
+        if (colExpr instanceof RexDynamicParam) {
+            RexDynamicParam dynamicParam = (RexDynamicParam) colExpr;
+            indexes[i] = dynamicParam.getIndex();
+
+            return true;
+        } else if (colExpr instanceof RexLiteral) {
+            RexLiteral literal = (RexLiteral) colExpr;
+
+            // depends on supplied zoneId, it can`t be cached
+            if (literal.getTypeName() == 
SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) {
+                return false;
+            }
+
+            int hashValue = computeHash(literal);
+            hashFields.add(hashValue);
+            indexes[i] = -hashFields.size();
+
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private static int computeHash(RexLiteral literal) {
+        Class<?> internalType = Primitives.wrap((Class<?>) 
Commons.typeFactory().getJavaClass(literal.getType()));
+        Object val = RexUtils.literalValue(FaultyContext.INSTANCE, literal, 
internalType);
+
+        NativeType nativeType = 
IgniteTypeFactory.relDataTypeToNative(literal.getType());
+        Object internalVal = TypeUtils.fromInternal(val, nativeType.spec());
+
+        return ColocationUtils.hash(internalVal, nativeType);
+    }
 }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Cloner.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Cloner.java
index 80245a23908..6468d623440 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Cloner.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Cloner.java
@@ -17,18 +17,24 @@
 
 package org.apache.ignite.internal.sql.engine.util;
 
-import it.unimi.dsi.fastutil.ints.IntObjectImmutablePair;
-import it.unimi.dsi.fastutil.ints.IntObjectPair;
+import it.unimi.dsi.fastutil.ints.IntArraySet;
+import it.unimi.dsi.fastutil.ints.IntSet;
+import it.unimi.dsi.fastutil.ints.IntSets;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptTable;
 import org.apache.ignite.internal.sql.engine.prepare.IgniteRelShuttle;
+import org.apache.ignite.internal.sql.engine.prepare.RelWithSources;
 import org.apache.ignite.internal.sql.engine.rel.IgniteRel;
+import org.apache.ignite.internal.sql.engine.rel.IgniteTableModify;
 import org.apache.ignite.internal.sql.engine.rel.SourceAwareIgniteRel;
+import org.apache.ignite.internal.sql.engine.schema.IgniteTable;
 
 /**
- * Utility class to clone relational tree and optionally replace assigned 
{@link RelOptCluster cluster}
- * to another one.
+ * Utility class to clone relational tree and optionally replace assigned 
{@link RelOptCluster cluster} to another one.
  */
 public class Cloner {
     private final RelOptCluster cluster;
@@ -61,19 +67,22 @@ public class Cloner {
      * @param cluster Cluster.
      * @return The number of source relations in the given plan and the plan 
itself.
      */
-    public static IntObjectPair<IgniteRel> cloneAndAssignSourceId(IgniteRel 
root, RelOptCluster cluster) {
+    public static RelWithSources cloneAndAssignSourceId(IgniteRel root, 
RelOptCluster cluster) {
         CloneAndAssignIds assigner = new CloneAndAssignIds(cluster);
         IgniteRel result = assigner.visit(root);
-        int numSources = assigner.sourceIndex;
 
-        return new IntObjectImmutablePair<>(numSources, result);
+        return new RelWithSources(result, assigner.sources, 
assigner.modifiedTables);
     }
 
     private static class CloneAndAssignIds extends IgniteRelShuttle {
 
         private final RelOptCluster cluster;
 
-        private int sourceIndex;
+        private long sourceIndex;
+
+        private final Long2ObjectMap<IgniteRel> sources = new 
Long2ObjectOpenHashMap<>();
+
+        private IntSet modifiedTables = IntSets.emptySet();
 
         private CloneAndAssignIds(RelOptCluster cluster) {
             this.cluster = cluster;
@@ -85,14 +94,29 @@ public class Cloner {
                 // Take into account only actual source relations.
                 SourceAwareIgniteRel src = (SourceAwareIgniteRel) rel;
 
-                int sourceId = sourceIndex++;
+                long sourceId = sourceIndex++;
                 IgniteRel relWithSourceId = src.clone(sourceId);
 
+                if (rel instanceof IgniteTableModify) {
+                    RelOptTable relOptTable = rel.getTable();
+                    assert relOptTable != null;
+
+                    IgniteTable table = relOptTable.unwrap(IgniteTable.class);
+                    assert table != null;
+
+                    if (modifiedTables.isEmpty()) {
+                        modifiedTables = new IntArraySet(1);
+                    }
+                    modifiedTables.add(table.id());
+                }
+
                 for (int i = 0; i < rel.getInputs().size(); i++) {
                     IgniteRel childNode = visit((IgniteRel) rel.getInput(i));
                     relWithSourceId.replaceInput(i, childNode);
                 }
 
+                sources.put(sourceId, rel);
+
                 return relWithSourceId;
             } else {
                 List<IgniteRel> newChildren = new 
ArrayList<>(rel.getInputs().size());
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/mapping/MappingTestRunner.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/mapping/MappingTestRunner.java
index dbbd8ef964f..680dfa0d178 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/mapping/MappingTestRunner.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/mapping/MappingTestRunner.java
@@ -21,7 +21,6 @@ import static 
org.apache.ignite.internal.lang.IgniteStringFormatter.format;
 import static org.apache.ignite.internal.testframework.IgniteTestUtils.await;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import it.unimi.dsi.fastutil.ints.IntObjectPair;
 import java.io.IOException;
 import java.io.UncheckedIOException;
 import java.nio.charset.StandardCharsets;
@@ -47,6 +46,7 @@ import org.apache.ignite.internal.sql.engine.SqlQueryType;
 import org.apache.ignite.internal.sql.engine.prepare.MultiStepPlan;
 import org.apache.ignite.internal.sql.engine.prepare.ParameterMetadata;
 import org.apache.ignite.internal.sql.engine.prepare.PlanId;
+import org.apache.ignite.internal.sql.engine.prepare.RelWithSources;
 import org.apache.ignite.internal.sql.engine.prepare.pruning.PartitionPruner;
 import 
org.apache.ignite.internal.sql.engine.prepare.pruning.PartitionPrunerImpl;
 import 
org.apache.ignite.internal.sql.engine.prepare.pruning.PartitionPruningMetadata;
@@ -179,14 +179,22 @@ final class MappingTestRunner {
             ResultSetMetadataImpl resultSetMetadata = new 
ResultSetMetadataImpl(Collections.emptyList());
             ParameterMetadata parameterMetadata = new 
ParameterMetadata(Collections.emptyList());
 
-            IntObjectPair<IgniteRel> pair = Cloner.cloneAndAssignSourceId(rel, 
rel.getCluster());
-            rel = pair.second();
+            RelWithSources relWithSources = Cloner.cloneAndAssignSourceId(rel, 
rel.getCluster());
+            rel = relWithSources.root();
 
             PartitionPruningMetadata partitionPruningMetadata = new 
PartitionPruningMetadataExtractor().go(rel);
 
-            MultiStepPlan multiStepPlan = new MultiStepPlan(new 
PlanId(UUID.randomUUID(), 1), sqlQueryType, rel,
-                    resultSetMetadata, parameterMetadata, 
schema.catalogVersion(),
-                    pair.firstInt(), null, null, partitionPruningMetadata
+            MultiStepPlan multiStepPlan = new MultiStepPlan(
+                    new PlanId(UUID.randomUUID(), 1),
+                    sqlQueryType, 
+                    rel,
+                    resultSetMetadata,
+                    parameterMetadata, 
+                    schema.catalogVersion(),
+                    relWithSources.sources().size(),
+                    null, 
+                    null, 
+                    partitionPruningMetadata
             );
 
             String actualText =
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PartitionPruningMetadataTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PartitionPruningMetadataTest.java
index 6afd3c22d2c..d9dd7fbf256 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PartitionPruningMetadataTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PartitionPruningMetadataTest.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.sql.engine.planner;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import it.unimi.dsi.fastutil.ints.IntObjectPair;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -32,6 +31,7 @@ import org.apache.calcite.sql.SqlExplainFormat;
 import org.apache.calcite.sql.SqlExplainLevel;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.ignite.internal.sql.engine.framework.TestBuilders;
+import org.apache.ignite.internal.sql.engine.prepare.RelWithSources;
 import 
org.apache.ignite.internal.sql.engine.prepare.pruning.PartitionPruningColumns;
 import 
org.apache.ignite.internal.sql.engine.prepare.pruning.PartitionPruningMetadata;
 import 
org.apache.ignite.internal.sql.engine.prepare.pruning.PartitionPruningMetadataExtractor;
@@ -616,8 +616,8 @@ public class PartitionPruningMetadataTest extends 
AbstractPlannerTest {
 
     private void extractMetadataAndCheck(IgniteRel rel, List<String> 
columnNames, List<String> expectedMetadata) {
         PartitionPruningMetadataExtractor extractor = new 
PartitionPruningMetadataExtractor();
-        IntObjectPair<IgniteRel> pair = Cloner.cloneAndAssignSourceId(rel, 
rel.getCluster());
-        PartitionPruningMetadata actual = extractor.go(pair.second());
+        RelWithSources relWithSoucres = Cloner.cloneAndAssignSourceId(rel, 
rel.getCluster());
+        PartitionPruningMetadata actual = extractor.go(relWithSoucres.root());
 
         List<String> actualMetadata;
 
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PartitionPruningTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PartitionPruningTest.java
index 4a55c24c274..48b38305175 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PartitionPruningTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/PartitionPruningTest.java
@@ -35,6 +35,7 @@ import 
org.apache.ignite.internal.sql.engine.schema.IgniteIndex.Collation;
 import org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
 import org.apache.ignite.internal.sql.engine.schema.IgniteTable;
 import org.apache.ignite.internal.type.NativeTypes;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -209,6 +210,27 @@ public class PartitionPruningTest extends 
AbstractPlannerTest {
         assertEquals("[[0=2, 1=1], [0=4, 1=3]]", 
PartitionPruningColumns.canonicalForm(cols).toString());
     }
 
+    @Test
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-26203";)
+    public void testInsertFromSelect() throws Exception {
+        IgniteTable table = TestBuilders.table()
+                .name("T")
+                .addKeyColumn("C1", NativeTypes.INT32)
+                .addKeyColumn("C2", NativeTypes.INT32)
+                .addColumn("C3", NativeTypes.INT32, true)
+                .distribution(TestBuilders.affinity(List.of(1, 0), 1, 2))
+                .build();
+
+        PartitionPruningMetadata actual = extractMetadata(
+                "INSERT INTO t SELECT * FROM t WHERE c2=1 and c1=2",
+                table
+        );
+
+        PartitionPruningColumns cols = actual.get(1);
+        assertNotNull(cols, "No metadata for source=1");
+        assertEquals("[[0=2, 1=1]]", 
PartitionPruningColumns.canonicalForm(cols).toString());
+    }
+
     @Test
     public void testCorrelatedQuery() throws Exception {
         IgniteTable table1 = TestBuilders.table()
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 6f7e60928b0..bb585dabf58 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
@@ -35,6 +35,7 @@ import org.apache.ignite.internal.sql.SqlCommon;
 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.ExplainablePlan;
 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;
@@ -98,11 +99,12 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
             "KILL TRANSACTION '1'",
             "CREATE TABLE x (a INT, b INT, PRIMARY KEY (a))",
             "SELECT * FROM table(system_range(1, 10)) WHERE x = 1",
-            "SELECT count(*) FROM t WHERE c1=?",
-            "UPDATE t SET c2=1 WHERE c1=?",
+            "SELECT (SELECT MIN(c1) FROM t WHERE c1=k1) FROM t2 WHERE k1=?",
+            "SELECT (SELECT COUNT(*) FROM t2 WHERE k2=c2) FROM t WHERE t.c1=1",
     })
     public void noMetadata(String query) {
         node.initSchema("CREATE TABLE t (c1 INT PRIMARY KEY, c2 INT)");
+        node.initSchema("CREATE TABLE t2 (k1 INT PRIMARY KEY, k2 INT)");
 
         QueryPlan plan = node.prepare(query);
         PartitionAwarenessMetadata metadata = 
plan.partitionAwarenessMetadata();
@@ -112,12 +114,10 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
     @ParameterizedTest
     @MethodSource("simpleKeyMetadata")
     public void simpleKey(String query, PartitionAwarenessMetadata expected) {
-        node.initSchema("CREATE TABLE t (c1 INT PRIMARY KEY, c2 INT)");
+        node.initSchema("CREATE TABLE t (c1 INT PRIMARY KEY, c2 INT, c3 INT)");
 
         QueryPlan plan = node.prepare(query);
-        PartitionAwarenessMetadata metadata = 
plan.partitionAwarenessMetadata();
-
-        expectMetadata(expected, metadata);
+        expectMetadata(plan, expected);
     }
 
     private static Stream<Arguments> simpleKeyMetadata() {
@@ -133,9 +133,9 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
                 Arguments.of("SELECT * FROM t WHERE c2=? and c1=? and c1=?", 
meta(1)),
 
                 // KV PUT
-                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 VALUES(?, ?, 0)", 
metaTrackingRequired(0)),
+                Arguments.of("INSERT INTO t VALUES(1, ?, 0)", 
metaTrackingRequired(new int[]{-1}, new int[]{1})),
+                Arguments.of("INSERT INTO t VALUES(1+1, ?, 0)", 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})),
@@ -143,7 +143,41 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
                 // KV DELETE
                 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}))
+                Arguments.of("DELETE FROM t WHERE c1=1+1", 
metaTrackingRequired(new int[]{-1}, new int[]{2})),
+
+                // SELECT 
+                Arguments.of("SELECT * FROM t WHERE c1=?", meta(0)),
+                Arguments.of("SELECT * FROM t WHERE c1=? AND c2=?", meta(0)),
+                Arguments.of("SELECT c1, c2 FROM t WHERE c1=? UNION SELECT 1, 
x FROM SYSTEM_RANGE(1, 100)", meta(0)),
+                Arguments.of("SELECT c1, c2 FROM t JOIN (SELECT x FROM 
SYSTEM_RANGE(1, 100)) r ON r.x=c2 WHERE c1 = ? ", meta(0)),
+                Arguments.of("SELECT c1, c2 FROM t JOIN (SELECT x FROM 
SYSTEM_RANGE(1, 100)) r ON r.x=c2 WHERE c1 = 1 ", 
+                        meta(new int[]{-1}, new int[]{1})),
+
+                // Can refer multiple partitions - skip
+                Arguments.of("SELECT * FROM t WHERE c1=1 or c1=2", null),
+                Arguments.of("SELECT * FROM t WHERE c1 IN (?, 2)", null),
+                Arguments.of("SELECT * FROM t WHERE c1=? AND c2 IN (SELECT * 
FROM SYSTEM_RANGE(1, 100))", meta(0)),
+
+                // UPDATE
+                Arguments.of("UPDATE t SET c2=1 WHERE c1=?", meta(new 
int[]{0}, new int[0], DirectTxMode.NOT_SUPPORTED)),
+                Arguments.of("UPDATE t SET c2=? WHERE c1=1", meta(new 
int[]{-1}, new int[]{1}, DirectTxMode.NOT_SUPPORTED)),
+                Arguments.of("UPDATE t SET c2=? WHERE c1=1 AND c3 IN (SELECT * 
FROM SYSTEM_RANGE(1, 100))",
+                        meta(new int[]{-1}, new int[]{1}, 
DirectTxMode.NOT_SUPPORTED)),
+
+                // Can refer multiple partitions - skip
+                Arguments.of("UPDATE t SET c2=1 WHERE c1=1 or c1=2", null),
+                Arguments.of("UPDATE t SET c2=? WHERE c1 IN (?, 2)", null),
+
+                // INSERT  
+                // TODO https://issues.apache.org/jira/browse/IGNITE-26203 No 
partition pruning metadata for INSERT INTO ... SELECT
+                // Arguments.of("INSERT INTO t SELECT 1 as c1, 2 as c2, x as 
c3 FROM SYSTEM_RANGE(1, 100)",
+                //        meta(new int[]{-1}, new int[]{1}), 
DirectTxMode.NOT_SUPPORTED),
+
+                // DELETE
+                Arguments.of("DELETE FROM t WHERE c1=1 AND c2 IN (SELECT * 
FROM SYSTEM_RANGE(1, 100)) ",
+                        meta(new int[]{-1}, new int[]{1}, 
DirectTxMode.NOT_SUPPORTED)),
+                // Can refer multiple partitions - skip
+                Arguments.of("DELETE FROM t WHERE c1=1 or c1=2", null)
         );
     }
 
@@ -153,9 +187,7 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
         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);
+        expectMetadata(plan, expected);
     }
 
     private static Stream<Arguments> tsKeyMetadata() {
@@ -165,7 +197,7 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
                 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 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)
         );
     }
@@ -176,9 +208,7 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
         node.initSchema("CREATE TABLE t (c1 INT, c2 INT, c3 INT, PRIMARY KEY 
(c3, c2)) COLOCATE BY (c3)");
 
         QueryPlan plan = node.prepare(query);
-        PartitionAwarenessMetadata metadata = 
plan.partitionAwarenessMetadata();
-
-        expectMetadata(expected, metadata);
+        expectMetadata(plan, expected);
     }
 
     private static Stream<Arguments> shortKeyMetadata() {
@@ -191,12 +221,12 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
                 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 c3=3", meta(new int[]{-1}, 
new int[]{3})),
                 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 (?, ?, ?)",  
metaTrackingRequired(2)),
+                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)),
 
@@ -208,9 +238,35 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
                 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("DELETE FROM t WHERE c3=3", null),
-                Arguments.of("DELETE FROM t WHERE c2=? and c3=3", 
metaTrackingRequired(new int[]{-1}, new int[]{3}))
+                Arguments.of("DELETE FROM t WHERE c2=? and c3=3",
+                        meta(new int[]{-1}, new int[]{3}, 
DirectTxMode.SUPPORTED_TRACKING_REQUIRED)),
+
+                // SELECT
+                Arguments.of("SELECT * FROM t WHERE c3=3 AND c1 IN (SELECT * 
FROM SYSTEM_RANGE(1, 100))",
+                        meta(new int[]{-1}, new int[]{3})
+                ),
+
+                // UPDATE
+                Arguments.of("UPDATE t SET c1=? WHERE c3=? and c2=?",
+                        meta(new int[]{1}, new int[0], 
DirectTxMode.NOT_SUPPORTED)
+                ),
+                Arguments.of("UPDATE t SET c1=? WHERE c3=1 and c2=?",
+                        meta(new int[]{-1}, new int[]{1}, 
DirectTxMode.NOT_SUPPORTED)
+                ),
+                Arguments.of("UPDATE t SET c1=? WHERE c3=1 and c2 IN (SELECT * 
FROM SYSTEM_RANGE(1, 100))",
+                        meta(new int[]{-1}, new int[]{1}, 
DirectTxMode.NOT_SUPPORTED)
+                ),
+
+                // INSERT
+                // TODO https://issues.apache.org/jira/browse/IGNITE-26203 No 
partition pruning metadata for INSERT INTO ... SELECT
+                // Arguments.of("INSERT INTO t SELECT 1 as c1, 2 as c2, 3 as 
c3 FROM SYSTEM_RANGE(1, 100)",
+                //        meta(new int[]{-1}, new int[]{3}, 
DirectTxMode.NOT_SUPPORTED)
+                // ),
+
+                // DELETE
+                Arguments.of("DELETE FROM t WHERE c3=3", meta(new int[]{-1}, 
new int[]{3}, DirectTxMode.NOT_SUPPORTED)),
+                Arguments.of("DELETE FROM t WHERE c3=3 and c2 IN (SELECT * 
FROM SYSTEM_RANGE(1, 100))",
+                        meta(new int[]{-1}, new int[]{3}, 
DirectTxMode.NOT_SUPPORTED))
         );
     }
 
@@ -220,9 +276,7 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
         node.initSchema("CREATE TABLE t (c1 INT, c2 INT, c3 INT, c4 INT, 
PRIMARY KEY(c1, c2, c3)) COLOCATE BY (c3, c1, c2)");
 
         QueryPlan plan = node.prepare(query);
-        PartitionAwarenessMetadata metadata = 
plan.partitionAwarenessMetadata();
-
-        expectMetadata(expected, metadata);
+        expectMetadata(plan, expected);
     }
 
     private static Stream<Arguments> compoundKeyMetadata() {
@@ -252,7 +306,7 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
                 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),
+                Arguments.of("INSERT INTO t VALUES (1, 2, 3, 4)", 
metaTrackingRequired(new int[]{-1, -2, -3}, new int[]{3, 1, 2})),
 
                 // KV DELETE
                 Arguments.of("DELETE FROM t WHERE c1=? and c2=? and c3=?", 
metaTrackingRequired(2, 0, 1)),
@@ -262,7 +316,34 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
                 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}))
+
+                // SELECT 
+                Arguments.of("SELECT * FROM t WHERE c1=? and c2=? and c3=?",
+                        meta(new int[]{2, 0, 1}, new int[]{}, 
DirectTxMode.SUPPORTED)
+                ),
+                Arguments.of("SELECT * FROM t WHERE c1=1 and c2=? and c3=?",
+                        meta(new int[]{1, -1, 0}, new int[]{1}, 
DirectTxMode.SUPPORTED)
+                ),
+                Arguments.of("SELECT * FROM t WHERE c1=1 and c2=2 and c3=?",
+                        meta(new int[]{0, -1, -2}, new int[]{1, 2}, 
DirectTxMode.SUPPORTED)
+                ),
+                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}, 
DirectTxMode.SUPPORTED)
+                ),
+                // Multiple partitions - skip
+                Arguments.of("SELECT * FROM t WHERE (c1=? and c2=? and c3=?) 
OR (c1=1 and c2=2 and c3=3)", null),
+                // Not keys specified - no partition pruning metadata
+                Arguments.of("SELECT * FROM t WHERE c2=? and c3=?", null),
+
+                // INSERT
+                // TODO https://issues.apache.org/jira/browse/IGNITE-26203 No 
partition pruning metadata for INSERT INTO ... SELECT
+                // Arguments.of("INSERT INTO t SELECT * FROM t WHERE c1=1 and 
c2=2 and c3=3", null)
+
+                // UPDATE
+                Arguments.of("UPDATE t SET c4=? WHERE c1=? and c2=? and c3=?",
+                        meta(new int[]{3, 1, 2}, new int[]{}, 
DirectTxMode.NOT_SUPPORTED)
+                ),
+                Arguments.of("UPDATE t SET c4=? WHERE c2=? and c3=3", null)
         );
     }
 
@@ -290,6 +371,19 @@ public class PartitionAwarenessMetadataTest extends 
BaseIgniteAbstractTest {
         return meta(dynamicParams, new int[0], 
DirectTxMode.SUPPORTED_TRACKING_REQUIRED);
     }
 
+    private void expectMetadata(QueryPlan plan, PartitionAwarenessMetadata 
expected) {
+        PartitionAwarenessMetadata metadata = 
plan.partitionAwarenessMetadata();
+
+        if (plan instanceof ExplainablePlan) {
+            ExplainablePlan explainablePlan = (ExplainablePlan) plan;
+            log.info("Plan:\n{}", explainablePlan.explain());
+        } else {
+            log.info("Plan:{}", plan.getClass().getName());
+        }
+
+        expectMetadata(expected, metadata);
+    }
+
     private static void expectMetadata(PartitionAwarenessMetadata expected, 
@Nullable PartitionAwarenessMetadata actual) {
         if (expected == null) {
             assertNull(actual, "Metadata should not be present");

Reply via email to