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

korlov 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 2cb493871f IGNITE-24134 Sql. Improve performance of KV plan in SQL 
(#4986)
2cb493871f is described below

commit 2cb493871f4f0617c60197e922d621a4b37b44b5
Author: korlov42 <[email protected]>
AuthorDate: Mon Dec 30 14:56:02 2024 +0200

    IGNITE-24134 Sql. Improve performance of KV plan in SQL (#4986)
---
 .../sql/engine/ItSqlUsesKeyValueGetTest.java       |   2 +-
 .../engine/ItSqlUsesSelectCountOptimizedTest.java  |   4 +-
 ...tCastToIntWithoutFastQueryOptimizationTest.java |   7 +
 .../internal/sql/engine/prepare/CacheKey.java      |  19 +-
 .../sql/engine/prepare/KeyValueGetPlan.java        | 191 ++++++++++++++-------
 .../sql/engine/prepare/KeyValueModifyPlan.java     |  58 +++++--
 .../ignite/internal/sql/engine/util/Commons.java   |  20 ++-
 .../sql/engine/planner/DmlPlannerTest.java         |   8 +
 .../sql/engine/planner/DynamicParametersTest.java  |   7 +
 .../sql/engine/planner/ImplicitCastsTest.java      |   9 +
 .../planner/PartitionPruningMetadataTest.java      |   9 +
 .../sql/engine/planner/SelectCountPlannerTest.java |   3 +
 .../internal/sql/engine/util/QueryCheckerTest.java |   6 +
 13 files changed, 263 insertions(+), 80 deletions(-)

diff --git 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlUsesKeyValueGetTest.java
 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlUsesKeyValueGetTest.java
index f4d8d30168..dd8f4b7f7f 100644
--- 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlUsesKeyValueGetTest.java
+++ 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlUsesKeyValueGetTest.java
@@ -32,7 +32,7 @@ public class ItSqlUsesKeyValueGetTest extends 
BaseSqlIntegrationTest {
     private static final int TABLE_SIZE = 10;
 
     @BeforeAll
-    @SuppressWarnings({"ConcatenationWithEmptyString", "resource"})
+    @SuppressWarnings("ConcatenationWithEmptyString")
     static void initSchema() {
         CLUSTER.aliveNode().sql().executeScript(""
                 + "CREATE TABLE simple_key (id INT PRIMARY KEY, val INT);"
diff --git 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlUsesSelectCountOptimizedTest.java
 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlUsesSelectCountOptimizedTest.java
index 9897fa2f58..42f14884f1 100644
--- 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlUsesSelectCountOptimizedTest.java
+++ 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlUsesSelectCountOptimizedTest.java
@@ -44,7 +44,8 @@ public class ItSqlUsesSelectCountOptimizedTest extends 
BaseSqlIntegrationTest {
     @BeforeEach
     @AfterEach
     public void resetFastOpt() {
-        System.setProperty("FAST_QUERY_OPTIMIZATION_ENABLED", 
String.valueOf(Commons.fastQueryOptimizationEnabled()));
+        System.clearProperty("FAST_QUERY_OPTIMIZATION_ENABLED");
+        Commons.resetFastQueryOptimizationFlag();
     }
 
     @Test
@@ -119,6 +120,7 @@ public class ItSqlUsesSelectCountOptimizedTest extends 
BaseSqlIntegrationTest {
         // Disable fast query optimization
         // TODO: https://issues.apache.org/jira/browse/IGNITE-22821 replace 
with feature toggle
         System.setProperty("FAST_QUERY_OPTIMIZATION_ENABLED", "false");
+        Commons.resetFastQueryOptimizationFlag();
 
         assertQuery("SELECT COUNT(*) FROM test")
                 .matches(QueryChecker.containsSubPlan("Aggregate"))
diff --git 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/ItCastToIntWithoutFastQueryOptimizationTest.java
 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/ItCastToIntWithoutFastQueryOptimizationTest.java
index 343fa13807..1e57d91f1c 100644
--- 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/ItCastToIntWithoutFastQueryOptimizationTest.java
+++ 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/ItCastToIntWithoutFastQueryOptimizationTest.java
@@ -19,6 +19,8 @@ package org.apache.ignite.internal.sql.engine.datatypes;
 
 import org.apache.ignite.internal.sql.engine.util.Commons;
 import org.apache.ignite.internal.testframework.WithSystemProperty;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
 
 /**
  * Set of tests to ensure correctness of CAST expression to INTEGER
@@ -29,4 +31,9 @@ import 
org.apache.ignite.internal.testframework.WithSystemProperty;
  */
 @WithSystemProperty(key = "FAST_QUERY_OPTIMIZATION_ENABLED", value = "false")
 public class ItCastToIntWithoutFastQueryOptimizationTest extends 
ItCastToIntTest {
+    @BeforeAll
+    @AfterAll
+    public static void resetFlag() {
+        Commons.resetFastQueryOptimizationFlag();
+    }
 }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/CacheKey.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/CacheKey.java
index 6a58ce4321..1b7f97c6a4 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/CacheKey.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/CacheKey.java
@@ -39,6 +39,8 @@ public class CacheKey {
 
     private final Object[] paramTypes;
 
+    private int hashCode = 0;
+
     /**
      * Constructor.
      *
@@ -87,11 +89,16 @@ public class CacheKey {
 
     @Override
     public int hashCode() {
-        int result = catalogVersion;
-        result = 31 * result + schemaName.hashCode();
-        result = 31 * result + query.hashCode();
-        result = 31 * result + (contextKey != null ? contextKey.hashCode() : 
0);
-        result = 31 * result + Arrays.deepHashCode(paramTypes);
-        return result;
+        if (hashCode == 0) {
+            int result = catalogVersion;
+            result = 31 * result + schemaName.hashCode();
+            result = 31 * result + query.hashCode();
+            result = 31 * result + (contextKey != null ? contextKey.hashCode() 
: 0);
+            result = 31 * result + Arrays.deepHashCode(paramTypes);
+
+            hashCode = result;
+        }
+
+        return hashCode;
     }
 }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/KeyValueGetPlan.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/KeyValueGetPlan.java
index 7f136e084b..a6f699f19c 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/KeyValueGetPlan.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/KeyValueGetPlan.java
@@ -17,13 +17,15 @@
 
 package org.apache.ignite.internal.sql.engine.prepare;
 
+import static org.apache.ignite.internal.sql.engine.util.Commons.cast;
+
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
 import java.util.function.BiFunction;
-import java.util.function.Function;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
@@ -70,13 +72,10 @@ public class KeyValueGetPlan implements ExplainablePlan, 
ExecutablePlan {
     private final ResultSetMetadata meta;
     private final ParameterMetadata parameterMetadata;
 
-    KeyValueGetPlan(
-            PlanId id,
-            int catalogVersion,
-            IgniteKeyValueGet lookupNode,
-            ResultSetMetadata meta,
-            ParameterMetadata parameterMetadata
-    ) {
+    private volatile Performable<?> operation;
+
+    KeyValueGetPlan(PlanId id, int catalogVersion, IgniteKeyValueGet 
lookupNode, ResultSetMetadata meta,
+            ParameterMetadata parameterMetadata) {
         this.id = id;
         this.catalogVersion = catalogVersion;
         this.lookupNode = lookupNode;
@@ -128,66 +127,129 @@ public class KeyValueGetPlan implements ExplainablePlan, 
ExecutablePlan {
         return lookupNode;
     }
 
-    @Override
-    public <RowT> AsyncCursor<InternalSqlRow> execute(
-            ExecutionContext<RowT> ctx,
-            InternalTransaction tx,
-            ExecutableTableRegistry tableRegistry,
-            @Nullable QueryPrefetchCallback firstPageReadyCallback
-    ) {
+    private <RowT> Performable<RowT> operation(ExecutionContext<RowT> ctx, 
ExecutableTableRegistry tableRegistry) {
+        Performable<RowT> operation = cast(this.operation);
+
+        if (operation != null) {
+            return operation;
+        }
+
         IgniteTable sqlTable = table();
-        ExecutableTable execTable = tableRegistry.getTable(catalogVersion, 
sqlTable.id());
+        ExecutableTable executableTable = 
tableRegistry.getTable(catalogVersion, sqlTable.id());
+        ScannableTable scannableTable = executableTable.scannableTable();
 
         ImmutableBitSet requiredColumns = lookupNode.requiredColumns();
         RexNode filterExpr = lookupNode.condition();
         List<RexNode> projectionExpr = lookupNode.projects();
-        List<RexNode> keyExpressions = lookupNode.keyExpressions();
 
         RelDataType rowType = sqlTable.getRowType(Commons.typeFactory(), 
requiredColumns);
 
-        SqlRowProvider<RowT> keySupplier = ctx.expressionFactory()
-                .rowSource(keyExpressions);
-        SqlPredicate<RowT> filter = filterExpr == null ? null : 
ctx.expressionFactory()
-                .predicate(filterExpr, rowType);
-        SqlProjection<RowT> projection = projectionExpr == null ? null : 
ctx.expressionFactory()
-                .project(projectionExpr, rowType);
+        SqlPredicate<RowT> filter = filterExpr == null ? null : 
ctx.expressionFactory().predicate(filterExpr, rowType);
+        SqlProjection<RowT> projection = projectionExpr == null ? null : 
ctx.expressionFactory().project(projectionExpr, rowType);
 
         RowHandler<RowT> rowHandler = ctx.rowHandler();
         RowSchema rowSchema = 
TypeUtils.rowSchemaFromRelTypes(RelOptUtil.getFieldTypeList(rowType));
         RowFactory<RowT> rowFactory = rowHandler.factory(rowSchema);
 
+        List<RexNode> keyExpressions = lookupNode.keyExpressions();
+        SqlRowProvider<RowT> keySupplier = 
ctx.expressionFactory().rowSource(keyExpressions);
+
         RelDataType resultType = lookupNode.getRowType();
         BiFunction<Integer, Object, Object> internalTypeConverter = 
TypeUtils.resultTypeConverter(ctx, resultType);
 
-        ScannableTable scannableTable = execTable.scannableTable();
-        Function<RowT, Iterator<InternalSqlRow>> postProcess = row -> {
-            if (row == null) {
-                return Collections.emptyIterator();
-            }
-
-            if (filter != null && !filter.test(ctx, row)) {
-                return Collections.emptyIterator();
-            }
-
-            if (projection != null) {
-                row = projection.project(ctx, row);
-            }
-
-            return List.<InternalSqlRow>of(
-                    new InternalSqlRowImpl<>(row, rowHandler, 
internalTypeConverter)
-            ).iterator();
-        };
-
-        CompletableFuture<RowT> lookupResult = scannableTable.primaryKeyLookup(
-                ctx, tx, rowFactory, keySupplier.get(ctx), 
requiredColumns.toBitSet()
-        );
-
-        CompletableFuture<Iterator<InternalSqlRow>> result;
-        if (projection == null && filter == null) {
-            // no arbitrary computations, should be safe to proceed execution 
on
-            // thread that completes the future
-            result = lookupResult.thenApply(postProcess);
-        } else {
+        operation = filter == null && projection == null ? new 
SimpleLookupExecution<>(scannableTable, rowHandler, rowFactory,
+                keySupplier, requiredColumns.toBitSet(), internalTypeConverter)
+                : new FilterableProjectableLookupExecution<>(scannableTable, 
rowHandler, rowFactory, keySupplier,
+                        filter, projection, requiredColumns.toBitSet(), 
internalTypeConverter);
+
+        this.operation = operation;
+
+        return operation;
+    }
+
+    @Override
+    public <RowT> AsyncCursor<InternalSqlRow> execute(
+            ExecutionContext<RowT> ctx,
+            InternalTransaction tx,
+            ExecutableTableRegistry tableRegistry,
+            @Nullable QueryPrefetchCallback firstPageReadyCallback
+    ) {
+        Performable<RowT> operation = operation(ctx, tableRegistry);
+
+        CompletableFuture<Iterator<InternalSqlRow>> result = 
operation.perform(ctx, tx);
+
+        if (firstPageReadyCallback != null) {
+            result.whenComplete((res, err) -> 
firstPageReadyCallback.onPrefetchComplete(err));
+        }
+
+        ctx.scheduleTimeout(result);
+
+        return new AsyncWrapper<>(result, Runnable::run);
+    }
+
+    private static class SimpleLookupExecution<RowT> extends Performable<RowT> 
{
+        private final ScannableTable table;
+        private final RowHandler<RowT> rowHandler;
+        private final RowFactory<RowT> tableRowFactory;
+        private final SqlRowProvider<RowT> keySupplier;
+        private final BitSet requiredColumns;
+        private final BiFunction<Integer, Object, Object> 
internalTypeConverter;
+
+        private SimpleLookupExecution(ScannableTable table, RowHandler<RowT> 
rowHandler, RowFactory<RowT> tableRowFactory,
+                SqlRowProvider<RowT> keySupplier, BitSet requiredColumns, 
BiFunction<Integer, Object, Object> internalTypeConverter) {
+            this.table = table;
+            this.rowHandler = rowHandler;
+            this.tableRowFactory = tableRowFactory;
+            this.keySupplier = keySupplier;
+            this.requiredColumns = requiredColumns;
+            this.internalTypeConverter = internalTypeConverter;
+        }
+
+        @Override
+        CompletableFuture<Iterator<InternalSqlRow>> 
perform(ExecutionContext<RowT> ctx, InternalTransaction tx) {
+            RowT key = keySupplier.get(ctx);
+            return table.primaryKeyLookup(ctx, tx, tableRowFactory, key, 
requiredColumns).thenApply(row -> {
+                if (row == null) {
+                    return Collections.emptyIterator();
+                }
+
+                return List.<InternalSqlRow>of(new InternalSqlRowImpl<>(row, 
rowHandler, internalTypeConverter)).iterator();
+            });
+        }
+    }
+
+    private static class FilterableProjectableLookupExecution<RowT> extends 
Performable<RowT> {
+        private final ScannableTable table;
+        private final RowHandler<RowT> rowHandler;
+        private final RowFactory<RowT> tableRowFactory;
+        private final SqlRowProvider<RowT> keySupplier;
+        private final @Nullable SqlPredicate<RowT> filter;
+        private final @Nullable SqlProjection<RowT> projection;
+        private final @Nullable BitSet requiredColumns;
+        private final BiFunction<Integer, Object, Object> 
internalTypeConverter;
+
+        private FilterableProjectableLookupExecution(
+                ScannableTable table,
+                RowHandler<RowT> rowHandler,
+                RowFactory<RowT> tableRowFactory,
+                SqlRowProvider<RowT> keySupplier,
+                @Nullable SqlPredicate<RowT> filter,
+                @Nullable SqlProjection<RowT> projection,
+                @Nullable BitSet requiredColumns,
+                BiFunction<Integer, Object, Object> internalTypeConverter
+        ) {
+            this.table = table;
+            this.rowHandler = rowHandler;
+            this.tableRowFactory = tableRowFactory;
+            this.keySupplier = keySupplier;
+            this.filter = filter;
+            this.projection = projection;
+            this.requiredColumns = requiredColumns;
+            this.internalTypeConverter = internalTypeConverter;
+        }
+
+        @Override
+        CompletableFuture<Iterator<InternalSqlRow>> 
perform(ExecutionContext<RowT> ctx, InternalTransaction tx) {
             Executor executor = task -> ctx.execute(task::run, error -> {
                 // this executor is used to process future chain, so any 
unhandled exception
                 // should be wrapped with CompletionException and returned as 
a result, implying
@@ -196,16 +258,27 @@ public class KeyValueGetPlan implements ExplainablePlan, 
ExecutablePlan {
                 LOG.error("Unexpected error", error);
             });
 
-            result = lookupResult.thenApplyAsync(postProcess, executor);
-        }
+            RowT key = keySupplier.get(ctx);
+            return table.primaryKeyLookup(ctx, tx, tableRowFactory, key, 
requiredColumns).thenApplyAsync(row -> {
+                if (row == null) {
+                    return Collections.emptyIterator();
+                }
 
-        if (firstPageReadyCallback != null) {
-            result.whenComplete((res, err) -> 
firstPageReadyCallback.onPrefetchComplete(err));
-        }
+                if (filter != null && !filter.test(ctx, row)) {
+                    return Collections.emptyIterator();
+                }
 
-        ctx.scheduleTimeout(result);
+                if (projection != null) {
+                    row = projection.project(ctx, row);
+                }
 
-        return new AsyncWrapper<>(result, Runnable::run);
+                return List.<InternalSqlRow>of(new InternalSqlRowImpl<>(row, 
rowHandler, internalTypeConverter)).iterator();
+            }, executor);
+        }
+    }
+
+    private abstract static class Performable<RowT> {
+        abstract CompletableFuture<Iterator<InternalSqlRow>> 
perform(ExecutionContext<RowT> ctx, @Nullable InternalTransaction tx);
     }
 
     public int catalogVersion() {
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/KeyValueModifyPlan.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/KeyValueModifyPlan.java
index fe7a76f2a1..432a154fec 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/KeyValueModifyPlan.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/KeyValueModifyPlan.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.sql.engine.prepare;
 
+import static org.apache.ignite.internal.sql.engine.util.Commons.cast;
+
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
@@ -54,6 +56,8 @@ public class KeyValueModifyPlan implements ExplainablePlan, 
ExecutablePlan {
     private final ResultSetMetadata meta;
     private final ParameterMetadata parameterMetadata;
 
+    private volatile InsertExecution<?> operation;
+
     KeyValueModifyPlan(
             PlanId id,
             int catalogVersion,
@@ -112,13 +116,13 @@ public class KeyValueModifyPlan implements 
ExplainablePlan, ExecutablePlan {
         return modifyNode;
     }
 
-    @Override
-    public <RowT> AsyncCursor<InternalSqlRow> execute(
-            ExecutionContext<RowT> ctx,
-            InternalTransaction tx,
-            ExecutableTableRegistry tableRegistry,
-            @Nullable QueryPrefetchCallback firstPageReadyCallback
-    ) {
+    private <RowT> InsertExecution<RowT> operation(ExecutionContext<RowT> ctx, 
ExecutableTableRegistry tableRegistry) {
+        InsertExecution<RowT> operation = cast(this.operation);
+
+        if (operation != null) {
+            return operation;
+        }
+
         IgniteTable sqlTable = table();
         ExecutableTable execTable = tableRegistry.getTable(catalogVersion, 
sqlTable.id());
 
@@ -127,11 +131,25 @@ public class KeyValueModifyPlan implements 
ExplainablePlan, ExecutablePlan {
         SqlRowProvider<RowT> rowSupplier = ctx.expressionFactory()
                 .rowSource(expressions);
 
-        UpdatableTable updatableTable = execTable.updatableTable();
+        UpdatableTable table = execTable.updatableTable();
+
+        operation = new InsertExecution<>(table, rowSupplier);
+
+        this.operation = operation;
+
+        return operation;
+    }
 
-        CompletableFuture<Iterator<InternalSqlRow>> result = 
updatableTable.insert(
-                tx, ctx, rowSupplier.get(ctx)
-        ).thenApply(none -> List.<InternalSqlRow>of(new 
InternalSqlRowSingleLong(1L)).iterator());
+    @Override
+    public <RowT> AsyncCursor<InternalSqlRow> execute(
+            ExecutionContext<RowT> ctx,
+            InternalTransaction tx,
+            ExecutableTableRegistry tableRegistry,
+            @Nullable QueryPrefetchCallback firstPageReadyCallback
+    ) {
+        InsertExecution<RowT> operation = operation(ctx, tableRegistry);
+
+        CompletableFuture<Iterator<InternalSqlRow>> result = 
operation.perform(ctx, tx);
 
         if (firstPageReadyCallback != null) {
             result.whenComplete((res, err) -> 
firstPageReadyCallback.onPrefetchComplete(err));
@@ -142,6 +160,24 @@ public class KeyValueModifyPlan implements 
ExplainablePlan, ExecutablePlan {
         return new AsyncWrapper<>(result, Runnable::run);
     }
 
+    private static class InsertExecution<RowT> {
+        private final UpdatableTable table;
+        private final SqlRowProvider<RowT> rowSupplier;
+
+        private InsertExecution(
+                UpdatableTable table,
+                SqlRowProvider<RowT> rowSupplier
+        ) {
+            this.table = table;
+            this.rowSupplier = rowSupplier;
+        }
+
+        CompletableFuture<Iterator<InternalSqlRow>> 
perform(ExecutionContext<RowT> ctx, InternalTransaction tx) {
+            return table.insert(tx, ctx, rowSupplier.get(ctx))
+                    .thenApply(none -> List.<InternalSqlRow>of(new 
InternalSqlRowSingleLong(1L)).iterator());
+        }
+    }
+
     public int catalogVersion() {
         return catalogVersion;
     }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
index 8f31786abb..a316bb9248 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
@@ -122,6 +122,7 @@ import org.codehaus.commons.compiler.CompilerFactoryFactory;
 import org.codehaus.commons.compiler.IClassBodyEvaluator;
 import org.codehaus.commons.compiler.ICompilerFactory;
 import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
 
 /**
  * Utility methods.
@@ -191,6 +192,8 @@ public final class Commons {
             .traitDefs(DISTRIBUTED_TRAITS_SET)
             .build();
 
+    private static volatile @Nullable Boolean fastOptimizationsEnabled = null;
+
     private Commons() {
     }
 
@@ -771,8 +774,21 @@ public final class Commons {
      * @return A {@code true} if fast path optimizations are enabled, {@code 
false} otherwise.
      */
     public static boolean fastQueryOptimizationEnabled() {
-        // TODO: https://issues.apache.org/jira/browse/IGNITE-22821 replace 
with feature toggle
-        return 
IgniteSystemProperties.getBoolean("FAST_QUERY_OPTIMIZATION_ENABLED", true);
+        Boolean enabled = fastOptimizationsEnabled;
+
+        if (enabled == null) {
+            // TODO: https://issues.apache.org/jira/browse/IGNITE-22821 
replace with feature toggle
+            enabled = 
IgniteSystemProperties.getBoolean("FAST_QUERY_OPTIMIZATION_ENABLED", true);
+
+            fastOptimizationsEnabled = enabled;
+        }
+
+        return enabled;
+    }
+
+    @TestOnly
+    public static void resetFastQueryOptimizationFlag() {
+        fastOptimizationsEnabled = null;
     }
 
     /**
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DmlPlannerTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DmlPlannerTest.java
index a5d17c143e..62fa935a32 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DmlPlannerTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DmlPlannerTest.java
@@ -43,6 +43,8 @@ import 
org.apache.ignite.internal.testframework.IgniteTestUtils;
 import org.apache.ignite.internal.testframework.WithSystemProperty;
 import org.apache.ignite.internal.type.NativeType;
 import org.apache.ignite.internal.type.NativeTypes;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -52,6 +54,12 @@ import org.junit.jupiter.params.provider.MethodSource;
  */
 @WithSystemProperty(key = "FAST_QUERY_OPTIMIZATION_ENABLED", value = "false")
 public class DmlPlannerTest extends AbstractPlannerTest {
+    @BeforeAll
+    @AfterAll
+    public static void resetFlag() {
+        Commons.resetFastQueryOptimizationFlag();
+    }
+
     /**
      * Test for INSERT .. VALUES when table has a single distribution.
      */
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DynamicParametersTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DynamicParametersTest.java
index c3e81ce48a..25d7d208ee 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DynamicParametersTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DynamicParametersTest.java
@@ -36,6 +36,8 @@ import org.apache.ignite.internal.type.NativeType;
 import org.apache.ignite.internal.type.NativeTypes;
 import org.apache.ignite.internal.type.VarlenNativeType;
 import org.jetbrains.annotations.Nullable;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.DynamicTest;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestFactory;
@@ -45,6 +47,11 @@ import org.junit.jupiter.api.TestFactory;
  */
 @WithSystemProperty(key = "FAST_QUERY_OPTIMIZATION_ENABLED", value = "false")
 public class DynamicParametersTest extends AbstractPlannerTest {
+    @BeforeAll
+    @AfterAll
+    public static void resetFlag() {
+        Commons.resetFastQueryOptimizationFlag();
+    }
 
     /**
      * This test case triggers "Conversion to relational algebra failed to 
preserve datatypes" assertion,
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ImplicitCastsTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ImplicitCastsTest.java
index 697885ca03..4b0edf1232 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ImplicitCastsTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ImplicitCastsTest.java
@@ -50,11 +50,14 @@ import 
org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
 import org.apache.ignite.internal.sql.engine.schema.IgniteTable;
 import org.apache.ignite.internal.sql.engine.trait.IgniteDistributions;
 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.NativeTypeValues;
 import org.apache.ignite.internal.sql.engine.util.StatementChecker;
 import org.apache.ignite.internal.testframework.WithSystemProperty;
 import org.apache.ignite.internal.type.NativeTypes;
 import org.jetbrains.annotations.Nullable;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.DynamicTest;
 import org.junit.jupiter.api.TestFactory;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -153,6 +156,12 @@ public class ImplicitCastsTest extends AbstractPlannerTest 
{
         return result.stream();
     }
 
+    @BeforeAll
+    @AfterAll
+    public static void resetFlag() {
+        Commons.resetFastQueryOptimizationFlag();
+    }
+
     /** MergeSort join - casts are pushed down to children. **/
     @ParameterizedTest
     @MethodSource("joinColumnTypes")
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 0816a75197..8a038dd0d2 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
@@ -40,8 +40,11 @@ import 
org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
 import org.apache.ignite.internal.sql.engine.schema.IgniteTable;
 import org.apache.ignite.internal.sql.engine.schema.TableDescriptor;
 import org.apache.ignite.internal.sql.engine.trait.IgniteDistributions;
+import org.apache.ignite.internal.sql.engine.util.Commons;
 import org.apache.ignite.internal.testframework.WithSystemProperty;
 import org.apache.ignite.internal.type.NativeTypes;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.EnumSource;
 
@@ -110,6 +113,12 @@ public class PartitionPruningMetadataTest extends 
AbstractPlannerTest {
             .distribution(IgniteDistributions.affinity(List.of(0), 1, 2))
             .build());
 
+    @BeforeAll
+    @AfterAll
+    public static void resetFlag() {
+        Commons.resetFastQueryOptimizationFlag();
+    }
+
     /** Basic test cases for partition pruning metadata extractor, select 
case. */
     @ParameterizedTest(name = "SELECT: {0}")
     @EnumSource(TestCaseBasic.class)
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SelectCountPlannerTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SelectCountPlannerTest.java
index 3c60edd927..278dcdcd97 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SelectCountPlannerTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SelectCountPlannerTest.java
@@ -44,6 +44,7 @@ import 
org.apache.ignite.internal.sql.engine.prepare.ExplainPlan;
 import org.apache.ignite.internal.sql.engine.prepare.QueryPlan;
 import org.apache.ignite.internal.sql.engine.prepare.SelectCountPlan;
 import org.apache.ignite.internal.sql.engine.tx.QueryTransactionContext;
+import org.apache.ignite.internal.sql.engine.util.Commons;
 import org.apache.ignite.internal.testframework.SystemPropertiesExtension;
 import org.apache.ignite.internal.testframework.WithSystemProperty;
 import org.junit.jupiter.api.AfterAll;
@@ -78,6 +79,8 @@ public class SelectCountPlannerTest extends 
AbstractPlannerTest {
 
     @AfterEach
     void clearCatalog() {
+        Commons.resetFastQueryOptimizationFlag();
+
         int version = CLUSTER.catalogManager().latestCatalogVersion();
 
         List<CatalogCommand> commands = new ArrayList<>();
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/util/QueryCheckerTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/util/QueryCheckerTest.java
index 03e7240d61..f9a328a3f9 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/util/QueryCheckerTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/util/QueryCheckerTest.java
@@ -66,6 +66,12 @@ public class QueryCheckerTest extends BaseIgniteAbstractTest 
{
             .nodes(NODE_NAME)
             .build();
 
+    @BeforeAll
+    @AfterAll
+    public static void resetFlag() {
+        Commons.resetFastQueryOptimizationFlag();
+    }
+
     @BeforeAll
     static void startCluster() {
         CLUSTER.start();

Reply via email to