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

alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 2de78560a34 IGNITE-27397 SQL Calcite: Add dynamic configuration to 
disable a rule globally - Fixes #12595.
2de78560a34 is described below

commit 2de78560a345bbc45f095dac56b8f99f6c9e64d0
Author: Vladimir Steshin <[email protected]>
AuthorDate: Wed Dec 24 15:17:17 2025 +0300

    IGNITE-27397 SQL Calcite: Add dynamic configuration to disable a rule 
globally - Fixes #12595.
    
    Signed-off-by: Aleksey Plekhanov <[email protected]>
---
 .../query/calcite/CalciteQueryProcessor.java       |   8 +-
 .../calcite/DistributedCalciteConfiguration.java   |  97 +++++++-
 .../query/calcite/prepare/IgnitePlanner.java       |  43 +++-
 .../query/calcite/prepare/PrepareServiceImpl.java  |  22 +-
 .../CalciteQueryProcessorPropertiesTest.java       | 244 +++++++++++++++++++++
 .../ignite/testsuites/IntegrationTestSuite.java    |   2 +
 .../DistributedConfigurationProcessor.java         |   1 -
 .../query/DistributedSqlConfiguration.java         |  82 ++++---
 .../query/h2/DistributedIndexingConfiguration.java |  45 ++--
 9 files changed, 476 insertions(+), 68 deletions(-)

diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
index 0d357e0c3b0..5f5adbfdb03 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
@@ -398,6 +398,7 @@ public class CalciteQueryProcessor extends 
GridProcessorAdapter implements Query
     /** {@inheritDoc} */
     @Override public void onKernalStart(boolean active) {
         onStart(ctx,
+            distrCfg,
             executionSvc,
             mailboxRegistry,
             partSvc,
@@ -407,7 +408,8 @@ public class CalciteQueryProcessor extends 
GridProcessorAdapter implements Query
             mappingSvc,
             qryPlanCache,
             exchangeSvc,
-            qryReg
+            qryReg,
+            prepareSvc
         );
 
         started = true;
@@ -419,6 +421,7 @@ public class CalciteQueryProcessor extends 
GridProcessorAdapter implements Query
             started = false;
 
             onStop(
+                prepareSvc,
                 qryReg,
                 executionSvc,
                 mailboxRegistry,
@@ -428,7 +431,8 @@ public class CalciteQueryProcessor extends 
GridProcessorAdapter implements Query
                 taskExecutor,
                 mappingSvc,
                 qryPlanCache,
-                exchangeSvc
+                exchangeSvc,
+                distrCfg
             );
         }
     }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/DistributedCalciteConfiguration.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/DistributedCalciteConfiguration.java
index 9be7ba34f6c..6eb8c49c458 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/DistributedCalciteConfiguration.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/DistributedCalciteConfiguration.java
@@ -17,14 +17,109 @@
 
 package org.apache.ignite.internal.processors.query.calcite;
 
+import java.util.Objects;
+import java.util.stream.Stream;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.GridKernalContext;
+import 
org.apache.ignite.internal.processors.configuration.distributed.DistributePropertyListener;
+import 
org.apache.ignite.internal.processors.configuration.distributed.DistributedChangeableProperty;
+import 
org.apache.ignite.internal.processors.configuration.distributed.DistributedPropertyDispatcher;
+import 
org.apache.ignite.internal.processors.configuration.distributed.SimpleDistributedProperty;
 import org.apache.ignite.internal.processors.query.DistributedSqlConfiguration;
+import 
org.apache.ignite.internal.processors.query.calcite.prepare.QueryPlanCache;
+import org.apache.ignite.internal.processors.query.calcite.util.Commons;
+import org.apache.ignite.internal.processors.query.calcite.util.LifecycleAware;
+import org.apache.ignite.internal.processors.query.calcite.util.Service;
+import org.apache.ignite.internal.util.typedef.F;
+
+import static 
org.apache.ignite.internal.cluster.DistributedConfigurationUtils.setDefaultValue;
 
 /** Distributed Calcite-engine configuration. */
-public class DistributedCalciteConfiguration extends 
DistributedSqlConfiguration {
+public class DistributedCalciteConfiguration extends 
DistributedSqlConfiguration implements Service, LifecycleAware {
+    /** Globally disabled rules property name. */
+    public static final String DISABLED_RULES_PROPERTY_NAME = 
"sql.calcite.disabledRules";
+
+    /** Default value of the disabled rules. */
+    public static final String[] DFLT_DISABLED_RULES = new String[0];
+
+    /** Globally disabled rules. */
+    private volatile DistributedChangeableProperty<String[]> disabledRules;
+
+    /** */
+    private QueryPlanCache qryPlanCache;
+
     /** */
     public DistributedCalciteConfiguration(GridKernalContext ctx, IgniteLogger 
log) {
         super(ctx, log);
     }
+
+    /** {@inheritDoc} */
+    @Override public void onStart(GridKernalContext ctx) {
+        CalciteQueryProcessor proc = 
Objects.requireNonNull(Commons.lookupComponent(ctx, 
CalciteQueryProcessor.class));
+
+        assert proc != null;
+
+        qryPlanCache = proc.queryPlanCache();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onStop() {
+        // No-op.
+    }
+
+    /**
+     * @return Globally disabled planning rules.
+     * @see #DISABLED_RULES_PROPERTY_NAME
+     */
+    public String[] disabledRules() {
+        DistributedChangeableProperty<String[]> disabledRules = 
this.disabledRules;
+
+        String[] res = disabledRules == null ? DFLT_DISABLED_RULES : 
disabledRules.get();
+
+        return res != null ? res : DFLT_DISABLED_RULES;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void onReadyToRegister(DistributedPropertyDispatcher 
dispatcher) {
+        super.onReadyToRegister(dispatcher);
+
+        registerProperty(
+            dispatcher,
+            DISABLED_RULES_PROPERTY_NAME,
+            prop -> disabledRules = prop,
+            () -> new SimpleDistributedProperty<>(
+                DISABLED_RULES_PROPERTY_NAME,
+                str -> Stream.of(str.split(",")).map(String::trim).filter(s -> 
!s.isBlank()).toArray(String[]::new),
+                "Comma-separated list of Calcite's disabled planning rules. 
NOTE: cleans the planning cache on change."
+            ),
+            log
+        );
+
+        disabledRules.addListener(new DistributePropertyListener<>() {
+            @Override public void onUpdate(String name, String[] oldVal, 
String[] newVal) {
+                if (oldVal != null && F.compareArrays(oldVal, newVal) != 0) {
+                    if (qryPlanCache != null) {
+                        if (log.isInfoEnabled()) {
+                            log.info("Cleaning Calcite's cache plan by 
changing of the property '"
+                                + DISABLED_RULES_PROPERTY_NAME + "'.");
+                        }
+
+                        qryPlanCache.clear();
+                    }
+                }
+            }
+        });
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void onReadyToWrite() {
+        super.onReadyToWrite();
+
+        setDefaultValue(disabledRules, DFLT_DISABLED_RULES, log);
+    }
+
+    /** */
+    DistributedChangeableProperty<String[]> disabledRulesProperty() {
+        return disabledRules;
+    }
 }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java
index 4819eafed4c..553ca3ba89a 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java
@@ -21,6 +21,7 @@ import java.io.PrintWriter;
 import java.io.Reader;
 import java.io.StringWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -29,6 +30,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 import com.google.common.collect.ImmutableList;
 import org.apache.calcite.plan.Context;
 import org.apache.calcite.plan.RelOptCluster;
@@ -80,6 +83,7 @@ import org.apache.calcite.sql2rel.SqlToRelConverter;
 import org.apache.calcite.tools.FrameworkConfig;
 import org.apache.calcite.tools.Planner;
 import org.apache.calcite.tools.Program;
+import org.apache.calcite.tools.RuleSet;
 import org.apache.calcite.tools.RuleSets;
 import org.apache.calcite.tools.ValidationException;
 import org.apache.calcite.util.Pair;
@@ -724,16 +728,7 @@ public class IgnitePlanner implements Planner, 
RelOptTable.ViewExpander {
         if (F.isEmpty(disabledRuleNames))
             return;
 
-        ctx.addRulesFilter(rulesSet -> {
-            List<RelOptRule> newSet = new ArrayList<>();
-
-            for (RelOptRule r : rulesSet) {
-                if (!disabledRuleNames.contains(shortRuleName(r.toString())))
-                    newSet.add(r);
-            }
-
-            return RuleSets.ofList(newSet);
-        });
+        ctx.addRulesFilter(new DisabledRuleFilter(disabledRuleNames));
     }
 
     /** */
@@ -746,6 +741,34 @@ public class IgnitePlanner implements Planner, 
RelOptTable.ViewExpander {
         return ruleDesc.substring(0, pos);
     }
 
+    /** */
+    public static final class DisabledRuleFilter implements Function<RuleSet, 
RuleSet> {
+        /** */
+        private final Set<String> ruleNames;
+
+        /** */
+        public DisabledRuleFilter(Collection<String> ruleNames) {
+            this.ruleNames = ruleNames.stream().map(ruleName -> 
ruleName.trim().toUpperCase()).collect(Collectors.toSet());
+        }
+
+        /** */
+        public DisabledRuleFilter(String[] ruleNames) {
+            this(Arrays.asList(ruleNames));
+        }
+
+        /** {@inheritDoc} */
+        @Override public RuleSet apply(RuleSet rules) {
+            List<RelOptRule> newSet = new ArrayList<>();
+
+            for (RelOptRule r : rules) {
+                if 
(!ruleNames.contains(shortRuleName(r.toString()).toUpperCase()))
+                    newSet.add(r);
+            }
+
+            return RuleSets.ofList(newSet);
+        }
+    }
+
     /** */
     private static class VolcanoPlannerExt extends VolcanoPlanner {
         /** */
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PrepareServiceImpl.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PrepareServiceImpl.java
index 276d8e4ee32..af2ac9506bc 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PrepareServiceImpl.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PrepareServiceImpl.java
@@ -18,7 +18,7 @@
 package org.apache.ignite.internal.processors.query.calcite.prepare;
 
 import java.util.List;
-
+import java.util.Objects;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.type.RelDataType;
@@ -35,11 +35,15 @@ import 
org.apache.ignite.cache.query.QueryCancelledException;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import 
org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessor;
+import 
org.apache.ignite.internal.processors.query.calcite.DistributedCalciteConfiguration;
 import 
org.apache.ignite.internal.processors.query.calcite.prepare.ddl.DdlSqlToCommandConverter;
 import org.apache.ignite.internal.processors.query.calcite.rel.IgniteRel;
 import 
org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
 import 
org.apache.ignite.internal.processors.query.calcite.util.AbstractService;
+import org.apache.ignite.internal.processors.query.calcite.util.Commons;
 import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils;
+import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.T2;
 import org.jetbrains.annotations.Nullable;
 
@@ -58,6 +62,9 @@ public class PrepareServiceImpl extends AbstractService 
implements PrepareServic
     /** */
     private final PlanExtractor planExtractor;
 
+    /** */
+    private DistributedCalciteConfiguration distrCfg;
+
     /**
      * @param ctx Kernal.
      */
@@ -71,6 +78,12 @@ public class PrepareServiceImpl extends AbstractService 
implements PrepareServic
     /** {@inheritDoc} */
     @Override public void onStart(GridKernalContext ctx) {
         super.onStart(ctx);
+
+        CalciteQueryProcessor proc = 
Objects.requireNonNull(Commons.lookupComponent(ctx, 
CalciteQueryProcessor.class));
+
+        assert proc != null;
+
+        distrCfg = proc.distributedConfiguration();
     }
 
     /** {@inheritDoc} */
@@ -80,6 +93,13 @@ public class PrepareServiceImpl extends AbstractService 
implements PrepareServic
 
             ctx.planner().reset();
 
+            assert distrCfg != null;
+
+            String[] disbledRules = distrCfg.disabledRules();
+
+            if (!F.isEmpty(disbledRules))
+                ctx.addRulesFilter(new 
IgnitePlanner.DisabledRuleFilter(disbledRules));
+
             if (SqlKind.DDL.contains(sqlNode.getKind()))
                 return prepareDdl(sqlNode, ctx);
 
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessorPropertiesTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessorPropertiesTest.java
new file mode 100644
index 00000000000..02075e5becc
--- /dev/null
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessorPropertiesTest.java
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.calcite;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.function.Predicate;
+import javax.management.DynamicMBean;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.management.api.CommandMBean;
+import 
org.apache.ignite.internal.processors.configuration.distributed.DistributedChangeableProperty;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import 
org.apache.ignite.internal.processors.query.calcite.integration.AbstractBasicIntegrationTest;
+import 
org.apache.ignite.internal.processors.query.calcite.rule.logical.ExposeIndexRule;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+
+import static 
org.apache.ignite.internal.processors.query.calcite.QueryChecker.containsIndexScan;
+import static 
org.apache.ignite.internal.processors.query.calcite.QueryChecker.containsSubPlan;
+import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
+import static org.hamcrest.CoreMatchers.not;
+
+/** */
+public class CalciteQueryProcessorPropertiesTest extends 
AbstractBasicIntegrationTest {
+    /** {@inheritDoc} */
+    @Override protected int nodeCount() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        
changeDistributedProperty(DistributedCalciteConfiguration.DISABLED_RULES_PROPERTY_NAME,
 " ",
+            pVal -> F.compareArrays(pVal, new String[0]) == 0);
+    }
+
+    /** */
+    @Test
+    public void testDisableRuleDistributedPropertyCommand() throws Exception {
+        String propName = 
DistributedCalciteConfiguration.DISABLED_RULES_PROPERTY_NAME;
+
+        for (Ignite ig : G.allGrids()) {
+            DistributedChangeableProperty<String[]> prop = 
distributedProperty(ig, propName);
+
+            assertNotNull(prop);
+            assertNotNull(prop.get());
+            assertTrue(prop.get().length == 0);
+        }
+
+        // Check simple value.
+        changeDistributedProperty(propName, "ExposeIndexRule",
+            pVal -> F.compareArrays(new String[] {"ExposeIndexRule"}, pVal) == 
0);
+
+        // Check setting with spaces.
+        changeDistributedProperty(propName, ",, ExposeIndexRule   , ,\t, 
CorrelatedNestedLoopJoinRule  ",
+            pVal -> F.compareArrays(new String[] {"ExposeIndexRule", 
"CorrelatedNestedLoopJoinRule"}, pVal) == 0);
+    }
+
+    /**
+     * Tests the ability to disable planning rules globally.
+     *
+     * @see DistributedCalciteConfiguration#disabledRules()}.
+     */
+    @Test
+    public void testDisableRuleDistributedProperty() throws Exception {
+        try {
+            sql("create table test_tbl1 (c1 int, c2 int, c3 int)");
+            sql("create index idx12 on test_tbl1(c2)");
+            sql("create index idx13 on test_tbl1(c3)");
+
+            assertQuery(client, "select * from test_tbl1 order by c2")
+                .matches(containsIndexScan("PUBLIC", "TEST_TBL1", "IDX12"))
+                .matches(not(containsSubPlan("IgniteSort")))
+                .check();
+            assertQuery(client, "select * from test_tbl1 order by c3")
+                .matches(containsIndexScan("PUBLIC", "TEST_TBL1", "IDX13"))
+                .matches(not(containsSubPlan("IgniteSort")))
+                .check();
+
+            String propName = 
DistributedCalciteConfiguration.DISABLED_RULES_PROPERTY_NAME;
+
+            changeDistributedProperty(propName, 
ExposeIndexRule.INSTANCE.toString(),
+                pVal -> F.compareArrays(pVal, new String[] 
{ExposeIndexRule.INSTANCE.toString()}) == 0);
+
+            // Check that there are no index scans now.
+            assertQuery(client, "select * from test_tbl1 order by c2")
+                .matches(not(containsIndexScan("PUBLIC", "TEST_TBL1", 
"IDX12")))
+                .matches(containsSubPlan("IgniteSort"))
+                .check();
+            assertQuery(client, "select * from test_tbl1 order by c3")
+                .matches(not(containsIndexScan("PUBLIC", "TEST_TBL1", 
"IDX13")))
+                .matches(containsSubPlan("IgniteSort"))
+                .check();
+
+            changeDistributedProperty(propName, " ", pVal -> 
((String[])pVal).length == 0);
+
+            // Check that the index are available again.
+            assertQuery(client, "select * from test_tbl1 order by c2")
+                .matches(containsIndexScan("PUBLIC", "TEST_TBL1", "IDX12"))
+                .matches(not(containsSubPlan("IgniteSort")))
+                .check();
+            assertQuery(client, "select * from test_tbl1 order by c3")
+                .matches(containsIndexScan("PUBLIC", "TEST_TBL1", "IDX13"))
+                .matches(not(containsSubPlan("IgniteSort")))
+                .check();
+        }
+        finally {
+            sql("drop table if exists test_tbl1");
+        }
+    }
+
+    /** */
+    @Test
+    public void testDisabledRulesByHintAndGrlobalProperty() throws Exception {
+        String propName = 
DistributedCalciteConfiguration.DISABLED_RULES_PROPERTY_NAME;
+
+        try {
+            sql("create table test_tbl1 (c1 int, c2 int, c3 int)");
+            sql("create index idx12 on test_tbl1(c2)");
+
+            sql("create table test_tbl2 (c1 int, c2 int, c3 int)");
+            sql("create index idx22 on test_tbl2(c3)");
+
+            // Ensure the index is enabled by default.
+            assertQuery(client, "select * from test_tbl1 order by c2")
+                .matches(containsIndexScan("PUBLIC", "TEST_TBL1", "IDX12"))
+                .matches(not(containsSubPlan("IgniteSort")))
+                .check();
+
+            // Ensure the indices are active with MergeJoin by default.
+            assertQuery(client, "select /*+ MERGE_JOIN */ t1.c1,t2.c3 from 
test_tbl1 t1, test_tbl2 t2 where t1.c2=t2.c3")
+                .matches(containsSubPlan("IgniteMergeJoin"))
+                .matches(containsIndexScan("PUBLIC", "TEST_TBL1", "IDX12"))
+                .matches(containsIndexScan("PUBLIC", "TEST_TBL2", "IDX22"))
+                .check();
+
+            // The index scan rule is now disabled by the global property.
+            changeDistributedProperty(propName, 
ExposeIndexRule.INSTANCE.toString(),
+                pVal -> F.compareArrays(pVal, new String[] 
{ExposeIndexRule.INSTANCE.toString()}) == 0);
+
+            assertQuery(client, "select * from test_tbl1 order by c2")
+                .matches(not(containsIndexScan("PUBLIC", "TEST_TBL1", 
"IDX12")))
+                .matches(containsSubPlan("IgniteSort"))
+                .check();
+
+            // Test that global property overrides hint.
+            assertQuery(client, "select /*+ FORCE_INDEX(IDX12) */ * from 
test_tbl1 order by c2")
+                .matches(not(containsIndexScan("PUBLIC", "TEST_TBL1", 
"IDX12")))
+                .matches(containsSubPlan("IgniteSort"))
+                .check();
+
+            // MergeJoin is on, but the indices are disabled by the global 
rule.
+            assertQuery(client, "select /*+ MERGE_JOIN */ t1.c1,t2.c3 from 
test_tbl1 t1, test_tbl2 t2 where t1.c2=t2.c3")
+                .matches(containsSubPlan("IgniteMergeJoin"))
+                .matches(not(containsIndexScan("PUBLIC", "TEST_TBL1", 
"IDX12")))
+                .matches(not(containsIndexScan("PUBLIC", "TEST_TBL2", 
"IDX22")))
+                .check();
+
+            String[] disabledRules = new String[] {"MergeJoinConverter"};
+
+            changeDistributedProperty(propName, "MergeJoinConverter", pVal -> 
F.compareArrays(pVal, disabledRules) == 0);
+
+            // Ensure that hint and global property are able to work together. 
The MergeJoin enforcing hint disables
+            // other join types. But it won't run because its rule is disabled 
by the global property. So, the planner is
+            // unable to build plan.
+            GridTestUtils.assertThrows(
+                null,
+                () -> sql("select /*+ MERGE_JOIN */ t1.c1,t2.c3 from test_tbl1 
t1, test_tbl2 t2 where t1.c2=t2.c3"),
+                IgniteSQLException.class,
+                "Failed to plan query"
+            );
+        }
+        finally {
+            sql("drop table if exists test_tbl1");
+            sql("drop table if exists test_tbl2");
+        }
+    }
+
+    /** */
+    private static <T extends Serializable> DistributedChangeableProperty<T> 
distributedProperty(Ignite ig, String propName) {
+        return 
((IgniteEx)ig).context().distributedConfiguration().property(propName);
+    }
+
+    /** */
+    private <T extends Serializable> void changeDistributedProperty(
+        String propName,
+        Object val,
+        @Nullable Predicate<T> newValChacker
+    ) throws Exception {
+        DynamicMBean mbean = getMxBean(
+            client.context().igniteInstanceName(),
+            "management",
+            Collections.singletonList("Property"),
+            "Set",
+            DynamicMBean.class
+        );
+
+        Object[] beanCallParams = new Object[2];
+        String[] callParamsType = new String[2];
+
+        beanCallParams[0] = propName;
+        callParamsType[0] = String.class.getName();
+
+        beanCallParams[1] = val;
+        callParamsType[1] = val == null ? Void.class.getName() : 
val.getClass().getName();
+
+        mbean.invoke(CommandMBean.INVOKE, beanCallParams, callParamsType);
+
+        if (newValChacker == null)
+            return;
+
+        assertTrue(waitForCondition(
+            () -> {
+                for (Ignite ig : G.allGrids()) {
+                    DistributedChangeableProperty<T> prop0 = 
distributedProperty(ig, propName);
+
+                    if (!newValChacker.test(prop0.get()))
+                        return false;
+                }
+
+                return true;
+            },
+            getTestTimeout()
+        ));
+    }
+}
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
 
b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
index 47f7480afab..dfaca9b7827 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
@@ -20,6 +20,7 @@ package org.apache.ignite.testsuites;
 import 
org.apache.ignite.internal.processors.cache.DdlTransactionCalciteSelfTest;
 import 
org.apache.ignite.internal.processors.cache.QueryEntityValueColumnAliasTest;
 import 
org.apache.ignite.internal.processors.cache.SessionContextSqlFunctionTest;
+import 
org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessorPropertiesTest;
 import 
org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessorTest;
 import org.apache.ignite.internal.processors.query.calcite.CancelTest;
 import 
org.apache.ignite.internal.processors.query.calcite.IndexWithSameNameCalciteTest;
@@ -101,6 +102,7 @@ import org.junit.runners.Suite;
     OrToUnionRuleTest.class,
     ProjectScanMergeRuleTest.class,
     CalciteQueryProcessorTest.class,
+    CalciteQueryProcessorPropertiesTest.class,
     CalciteErrorHandlilngIntegrationTest.class,
     CalciteBasicSecondaryIndexIntegrationTest.class,
     CancelTest.class,
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/configuration/distributed/DistributedConfigurationProcessor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/configuration/distributed/DistributedConfigurationProcessor.java
index bc01987509a..3c849a18c6a 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/configuration/distributed/DistributedConfigurationProcessor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/configuration/distributed/DistributedConfigurationProcessor.java
@@ -92,7 +92,6 @@ public class DistributedConfigurationProcessor extends 
GridProcessorAdapter impl
                 //Register and actualize properties waited for this service.
                 isp.getDistributedConfigurationListeners()
                     .forEach(listener -> 
listener.onReadyToRegister(DistributedConfigurationProcessor.this));
-
             }
 
             @Override public void onReadyForWrite(DistributedMetaStorage 
metastorage) {
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/DistributedSqlConfiguration.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/DistributedSqlConfiguration.java
index 79367e1522c..6e94bd7337d 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/DistributedSqlConfiguration.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/DistributedSqlConfiguration.java
@@ -17,6 +17,9 @@
 
 package org.apache.ignite.internal.processors.query;
 
+import java.io.Serializable;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.GridKernalContext;
@@ -44,6 +47,12 @@ public abstract class DistributedSqlConfiguration {
     /** Default value of the query timeout. */
     public static final int DFLT_QRY_TIMEOUT = 0;
 
+    /** */
+    protected final GridKernalContext ctx;
+
+    /** */
+    protected final IgniteLogger log;
+
     /** Query timeout. */
     private volatile DistributedChangeableProperty<Integer> dfltQryTimeout;
 
@@ -51,43 +60,64 @@ public abstract class DistributedSqlConfiguration {
      * @param ctx Kernal context
      * @param log Logger.
      */
-    protected DistributedSqlConfiguration(
-        GridKernalContext ctx,
-        IgniteLogger log
-    ) {
+    protected DistributedSqlConfiguration(GridKernalContext ctx, IgniteLogger 
log) {
+        this.ctx = ctx;
+        this.log = log;
+
         
ctx.internalSubscriptionProcessor().registerDistributedConfigurationListener(
             new DistributedConfigurationLifecycleListener() {
                 @Override public void 
onReadyToRegister(DistributedPropertyDispatcher dispatcher) {
-                    DistributedChangeableProperty<Integer> prop = 
dispatcher.property(QUERY_TIMEOUT_PROPERTY_NAME);
-
-                    if (prop != null) {
-                        dfltQryTimeout = prop;
-
-                        return;
-                    }
-
-                    dfltQryTimeout = new SimpleDistributedProperty<>(
-                        QUERY_TIMEOUT_PROPERTY_NAME,
-                        SimpleDistributedProperty::parseNonNegativeInteger,
-                        "Timeout in milliseconds for default query timeout. 0 
means there is no timeout."
-                    );
-
-                    
dfltQryTimeout.addListener(makeUpdateListener(PROPERTY_UPDATE_MESSAGE, log));
-
-                    dispatcher.registerProperties(dfltQryTimeout);
+                    
DistributedSqlConfiguration.this.onReadyToRegister(dispatcher);
                 }
 
-                @SuppressWarnings("deprecation")
                 @Override public void onReadyToWrite() {
-                    setDefaultValue(
-                        dfltQryTimeout,
-                        
(int)ctx.config().getSqlConfiguration().getDefaultQueryTimeout(),
-                        log);
+                    DistributedSqlConfiguration.this.onReadyToWrite();
                 }
             }
         );
     }
 
+    /** */
+    protected void onReadyToRegister(DistributedPropertyDispatcher dispatcher) 
{
+        registerProperty(
+            dispatcher,
+            QUERY_TIMEOUT_PROPERTY_NAME,
+            prop -> dfltQryTimeout = prop,
+            () -> new SimpleDistributedProperty<>(
+                QUERY_TIMEOUT_PROPERTY_NAME,
+                SimpleDistributedProperty::parseNonNegativeInteger,
+                "Timeout in milliseconds for default query timeout. 0 means 
there is no timeout."
+            ),
+            log
+        );
+    }
+
+    /** */
+    protected void onReadyToWrite() {
+        setDefaultValue(dfltQryTimeout, 
(int)ctx.config().getSqlConfiguration().getDefaultQueryTimeout(), log);
+    }
+
+    /** */
+    protected static <T extends Serializable> void registerProperty(
+        DistributedPropertyDispatcher dispatcher,
+        String propName,
+        Consumer<DistributedChangeableProperty<T>> propSetter,
+        Supplier<DistributedChangeableProperty<T>> newPropCreator,
+        IgniteLogger log
+    ) {
+        DistributedChangeableProperty<T> prop = dispatcher.property(propName);
+
+        if (prop == null) {
+            prop = newPropCreator.get();
+
+            prop.addListener(makeUpdateListener(PROPERTY_UPDATE_MESSAGE, log));
+
+            dispatcher.registerProperty(prop);
+        }
+
+        propSetter.accept(prop);
+    }
+
     /**
      * @return Default query timeout.
      */
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DistributedIndexingConfiguration.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DistributedIndexingConfiguration.java
index 06fbdc8d0ab..680fb032dd5 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DistributedIndexingConfiguration.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DistributedIndexingConfiguration.java
@@ -26,7 +26,6 @@ import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.GridKernalContext;
 import 
org.apache.ignite.internal.processors.configuration.distributed.DistributePropertyListener;
 import 
org.apache.ignite.internal.processors.configuration.distributed.DistributedBooleanProperty;
-import 
org.apache.ignite.internal.processors.configuration.distributed.DistributedConfigurationLifecycleListener;
 import 
org.apache.ignite.internal.processors.configuration.distributed.DistributedPropertyDispatcher;
 import 
org.apache.ignite.internal.processors.configuration.distributed.SimpleDistributedProperty;
 import org.apache.ignite.internal.processors.query.DistributedSqlConfiguration;
@@ -72,34 +71,26 @@ public class DistributedIndexingConfiguration extends 
DistributedSqlConfiguratio
      * @param ctx Kernal context
      * @param log Logger.
      */
-    public DistributedIndexingConfiguration(
-        GridKernalContext ctx,
-        IgniteLogger log
-    ) {
+    public DistributedIndexingConfiguration(GridKernalContext ctx, 
IgniteLogger log) {
         super(ctx, log);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void onReadyToRegister(DistributedPropertyDispatcher 
dispatcher) {
+        super.onReadyToRegister(dispatcher);
+
+        
disabledSqlFuncs.addListener(makeUpdateListener(PROPERTY_UPDATE_MESSAGE, log));
+
+        dispatcher.registerProperty(disabledSqlFuncs);
+        
dispatcher.registerProperty(disableCreateLuceneIndexForStringValueType);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void onReadyToWrite() {
+        super.onReadyToWrite();
 
-        
ctx.internalSubscriptionProcessor().registerDistributedConfigurationListener(
-            new DistributedConfigurationLifecycleListener() {
-                @Override public void 
onReadyToRegister(DistributedPropertyDispatcher dispatcher) {
-                    
disabledSqlFuncs.addListener(makeUpdateListener(PROPERTY_UPDATE_MESSAGE, log));
-
-                    dispatcher.registerProperty(disabledSqlFuncs);
-                    
dispatcher.registerProperty(disableCreateLuceneIndexForStringValueType);
-                }
-
-                @Override public void onReadyToWrite() {
-                    setDefaultValue(
-                        disabledSqlFuncs,
-                        DFLT_DISABLED_FUNCS,
-                        log);
-
-                    setDefaultValue(
-                        disableCreateLuceneIndexForStringValueType,
-                        false,
-                        log);
-                }
-            }
-        );
+        setDefaultValue(disabledSqlFuncs, DFLT_DISABLED_FUNCS, log);
+        setDefaultValue(disableCreateLuceneIndexForStringValueType, false, 
log);
     }
 
     /**


Reply via email to