This is an automated email from the ASF dual-hosted git repository.
mbudiu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new 4283b8735f [CALCITE-6998] The command !set planner-rules does not
support rules with multiple Config
4283b8735f is described below
commit 4283b8735faaafc4bbefc6244456b242eb9afa16
Author: Zhen Chen <[email protected]>
AuthorDate: Wed May 14 06:54:04 2025 +0800
[CALCITE-6998] The command !set planner-rules does not support rules with
multiple Config
---
.../rules/AggregateProjectPullUpConstantsRule.java | 1 +
.../org/apache/calcite/rel/rules/CoreRules.java | 52 ++++++++++----
.../rules/ExpandDisjunctionForJoinInputsRule.java | 2 +
.../apache/calcite/rel/rules/FilterJoinRule.java | 6 ++
.../rules/FilterTableFunctionTransposeRule.java | 3 +-
.../apache/calcite/rel/rules/JoinCommuteRule.java | 2 +
.../rel/rules/JoinDeriveIsNotNullFilterRule.java | 2 +-
.../org/apache/calcite/rel/rules/RuleConfig.java | 55 +++++++++++++++
.../calcite/rel/rules/SortUnionTransposeRule.java | 2 +
.../org/apache/calcite/test/LoadCoreRulesTest.java | 79 ++++++++++++++++++++++
core/src/test/resources/sql/planner.iq | 37 +++++++---
.../java/org/apache/calcite/test/QuidemTest.java | 56 ++++++++++++++-
12 files changed, 271 insertions(+), 26 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
index 231d65418c..3ca398469c 100644
---
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
+++
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
@@ -183,6 +183,7 @@ public AggregateProjectPullUpConstantsRule(
@Value.Immutable
public interface Config extends RelRule.Config {
Config DEFAULT = ImmutableAggregateProjectPullUpConstantsRule.Config.of();
+ Config ANY = DEFAULT.withOperandFor(LogicalAggregate.class, RelNode.class);
@Override default AggregateProjectPullUpConstantsRule toRule() {
return new AggregateProjectPullUpConstantsRule(this);
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
b/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
index 89753edb1d..6cd62c2240 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
@@ -44,6 +44,7 @@
import org.apache.calcite.rel.logical.LogicalProject;
import org.apache.calcite.rel.logical.LogicalSortExchange;
import org.apache.calcite.rel.logical.LogicalWindow;
+import
org.apache.calcite.rel.rules.FilterJoinRule.FilterIntoJoinRule.FilterIntoJoinRuleConfig;
import org.apache.calcite.rel.rules.FilterTableFunctionTransposeRule.Config;
import org.apache.calcite.rel.rules.materialize.MaterializedViewRules;
import org.apache.calcite.rex.RexInputRef;
@@ -72,9 +73,7 @@ private CoreRules() {}
* that matches any relational expression. */
public static final AggregateProjectPullUpConstantsRule
AGGREGATE_ANY_PULL_UP_CONSTANTS =
- AggregateProjectPullUpConstantsRule.Config.DEFAULT
- .withOperandFor(LogicalAggregate.class, RelNode.class)
- .toRule();
+ AggregateProjectPullUpConstantsRule.Config.ANY.toRule();
/** Rule that matches an {@link Aggregate} on
* a {@link StarTable.StarTableScan}. */
@@ -121,6 +120,7 @@ private CoreRules() {}
AggregateExpandDistinctAggregatesRule.Config.DEFAULT.toRule();
/** As {@link #AGGREGATE_EXPAND_DISTINCT_AGGREGATES} but generates a Join. */
+ @RuleConfig(value = "JOIN")
public static final AggregateExpandDistinctAggregatesRule
AGGREGATE_EXPAND_DISTINCT_AGGREGATES_TO_JOIN =
AggregateExpandDistinctAggregatesRule.Config.JOIN.toRule();
@@ -158,6 +158,7 @@ private CoreRules() {}
/** As {@link #AGGREGATE_JOIN_TRANSPOSE}, but extended to push down aggregate
* functions. */
+ @RuleConfig(value = "EXTENDED")
public static final AggregateJoinTransposeRule
AGGREGATE_JOIN_TRANSPOSE_EXTENDED =
AggregateJoinTransposeRule.Config.EXTENDED.toRule();
@@ -179,11 +180,13 @@ private CoreRules() {}
/** As {@link #AGGREGATE_UNION_AGGREGATE}, but matches an {@code Aggregate}
* only as the left input of the {@code Union}. */
+ @RuleConfig(value = "AGG_FIRST")
public static final AggregateUnionAggregateRule
AGGREGATE_UNION_AGGREGATE_FIRST =
AggregateUnionAggregateRule.Config.AGG_FIRST.toRule();
/** As {@link #AGGREGATE_UNION_AGGREGATE}, but matches an {@code Aggregate}
* only as the right input of the {@code Union}. */
+ @RuleConfig(value = "AGG_SECOND")
public static final AggregateUnionAggregateRule
AGGREGATE_UNION_AGGREGATE_SECOND =
AggregateUnionAggregateRule.Config.AGG_SECOND.toRule();
@@ -235,6 +238,7 @@ private CoreRules() {}
ExchangeRemoveConstantKeysRule.Config.DEFAULT.toRule();
/** Rule that removes constants inside a {@link LogicalSortExchange}. */
+ @RuleConfig(value = "SORT")
public static final ExchangeRemoveConstantKeysRule
SORT_EXCHANGE_REMOVE_CONSTANT_KEYS =
ExchangeRemoveConstantKeysRule.Config.SORT.toRule();
@@ -247,10 +251,7 @@ private CoreRules() {}
* use, but keeps some tests working for which {@code FILTER_INTO_JOIN} is
too
* smart. */
public static final FilterJoinRule.FilterIntoJoinRule FILTER_INTO_JOIN_DUMB =
- FILTER_INTO_JOIN.config
- .withSmart(false)
- .as(FilterJoinRule.FilterIntoJoinRule.FilterIntoJoinRuleConfig.class)
- .toRule();
+ FilterIntoJoinRuleConfig.SMART_FALSE.toRule();
/** Rule that combines two {@link LogicalFilter}s. */
public static final FilterMergeRule FILTER_MERGE =
@@ -294,9 +295,7 @@ private CoreRules() {}
/** Rule that pushes a {@link Filter}
* past a {@link TableFunctionScan}. */
public static final FilterTableFunctionTransposeRule
- FILTER_TABLE_FUNCTION_TRANSPOSE = Config.DEFAULT
- .withOperandFor(Filter.class, TableFunctionScan.class)
- .toRule();
+ FILTER_TABLE_FUNCTION_TRANSPOSE = Config.DEFAULT.toRule();
/** Rule that matches a {@link Filter} on a {@link TableScan}. */
public static final FilterTableScanRule FILTER_SCAN =
@@ -310,6 +309,7 @@ private CoreRules() {}
/** Rule that matches a {@link Filter} on an
* {@link org.apache.calcite.adapter.enumerable.EnumerableInterpreter} on a
* {@link TableScan}. */
+ @RuleConfig(value = "INTERPRETER")
public static final FilterTableScanRule FILTER_INTERPRETER_SCAN =
FilterTableScanRule.Config.INTERPRETER.toRule();
@@ -359,12 +359,14 @@ private CoreRules() {}
/** Rule that flattens an {@link Intersect} on an {@code Intersect}
* into a single {@code Intersect}. */
+ @RuleConfig(value = "INTERSECT")
public static final UnionMergeRule INTERSECT_MERGE =
UnionMergeRule.Config.INTERSECT.toRule();
/** Rule that removes a {@link Intersect} if it has only one input.
*
* @see PruneEmptyRules#UNION_INSTANCE */
+ @RuleConfig(value = "INTERSECT")
public static final UnionEliminatorRule INTERSECT_REMOVE =
UnionEliminatorRule.Config.INTERSECT.toRule();
@@ -389,14 +391,17 @@ private CoreRules() {}
IntersectToSemiJoinRule.Config.DEFAULT.toRule();
/** Rule that translates a {@link Union} to {@link Filter}. */
+ @RuleConfig(value = "UNION")
public static final SetOpToFilterRule UNION_FILTER_TO_FILTER =
SetOpToFilterRule.Config.UNION.toRule();
/** Rule that translates a {@link Intersect} to {@link Filter}. */
+ @RuleConfig(value = "INTERSECT")
public static final SetOpToFilterRule INTERSECT_FILTER_TO_FILTER =
SetOpToFilterRule.Config.INTERSECT.toRule();
/** Rule that translates a {@link Minus} to {@link Filter}. */
+ @RuleConfig(value = "MINUS")
public static final SetOpToFilterRule MINUS_FILTER_TO_FILTER =
SetOpToFilterRule.Config.MINUS.toRule();
@@ -456,6 +461,7 @@ private CoreRules() {}
/** As {@link #PROJECT_FILTER_TRANSPOSE}, but pushes down project and filter
* expressions whole, not field references. */
+ @RuleConfig(value = "PROJECT_FILTER")
public static final ProjectFilterTransposeRule
PROJECT_FILTER_TRANSPOSE_WHOLE_EXPRESSIONS =
ProjectFilterTransposeRule.Config.PROJECT_FILTER.toRule();
@@ -480,6 +486,7 @@ private CoreRules() {}
*
* @see #FILTER_SUB_QUERY_TO_CORRELATE
* @see #JOIN_SUB_QUERY_TO_CORRELATE */
+ @RuleConfig(value = "PROJECT")
public static final SubQueryRemoveRule PROJECT_SUB_QUERY_TO_CORRELATE =
SubQueryRemoveRule.Config.PROJECT.toRule();
@@ -488,6 +495,7 @@ private CoreRules() {}
*
* @see #PROJECT_SUB_QUERY_TO_CORRELATE
* @see #JOIN_SUB_QUERY_TO_CORRELATE */
+ @RuleConfig(value = "FILTER")
public static final SubQueryRemoveRule FILTER_SUB_QUERY_TO_CORRELATE =
SubQueryRemoveRule.Config.FILTER.toRule();
@@ -496,6 +504,7 @@ private CoreRules() {}
*
* @see #PROJECT_SUB_QUERY_TO_CORRELATE
* @see #FILTER_SUB_QUERY_TO_CORRELATE */
+ @RuleConfig(value = "JOIN")
public static final SubQueryRemoveRule JOIN_SUB_QUERY_TO_CORRELATE =
SubQueryRemoveRule.Config.JOIN.toRule();
@@ -572,6 +581,7 @@ private CoreRules() {}
/** As {@link #PROJECT_TABLE_SCAN}, but with an intervening
* {@link org.apache.calcite.adapter.enumerable.EnumerableInterpreter}. */
+ @RuleConfig(value = "INTERPRETER")
public static final ProjectTableScanRule PROJECT_INTERPRETER_TABLE_SCAN =
ProjectTableScanRule.Config.INTERPRETER.toRule();
@@ -608,7 +618,7 @@ private CoreRules() {}
/** As {@link #JOIN_COMMUTE} but swaps outer joins as well as inner joins. */
public static final JoinCommuteRule JOIN_COMMUTE_OUTER =
- JoinCommuteRule.Config.DEFAULT.withSwapOuter(true).toRule();
+ JoinCommuteRule.Config.SWAP_OUTER.toRule();
/** Rule to convert an
* {@link LogicalJoin inner join} to a
@@ -624,28 +634,33 @@ private CoreRules() {}
/** As {@link #JOIN_PROJECT_BOTH_TRANSPOSE} but only the left input is
* a {@link LogicalProject}. */
+ @RuleConfig(value = "LEFT")
public static final JoinProjectTransposeRule JOIN_PROJECT_LEFT_TRANSPOSE =
JoinProjectTransposeRule.Config.LEFT.toRule();
/** As {@link #JOIN_PROJECT_BOTH_TRANSPOSE} but only the right input is
* a {@link LogicalProject}. */
+ @RuleConfig(value = "RIGHT")
public static final JoinProjectTransposeRule JOIN_PROJECT_RIGHT_TRANSPOSE =
JoinProjectTransposeRule.Config.RIGHT.toRule();
/** As {@link #JOIN_PROJECT_BOTH_TRANSPOSE} but match outer as well as
* inner join. */
+ @RuleConfig(value = "OUTER")
public static final JoinProjectTransposeRule
JOIN_PROJECT_BOTH_TRANSPOSE_INCLUDE_OUTER =
JoinProjectTransposeRule.Config.OUTER.toRule();
/** As {@link #JOIN_PROJECT_LEFT_TRANSPOSE} but match outer as well as
* inner join. */
+ @RuleConfig(value = "LEFT_OUTER")
public static final JoinProjectTransposeRule
JOIN_PROJECT_LEFT_TRANSPOSE_INCLUDE_OUTER =
JoinProjectTransposeRule.Config.LEFT_OUTER.toRule();
/** As {@link #JOIN_PROJECT_RIGHT_TRANSPOSE} but match outer as well as
* inner join. */
+ @RuleConfig(value = "RIGHT_OUTER")
public static final JoinProjectTransposeRule
JOIN_PROJECT_RIGHT_TRANSPOSE_INCLUDE_OUTER =
JoinProjectTransposeRule.Config.RIGHT_OUTER.toRule();
@@ -700,11 +715,13 @@ private CoreRules() {}
/** Rule that pushes a {@link Join}
* past a non-distinct {@link Union} as its left input. */
+ @RuleConfig(value = "LEFT")
public static final JoinUnionTransposeRule JOIN_LEFT_UNION_TRANSPOSE =
JoinUnionTransposeRule.Config.LEFT.toRule();
/** Rule that pushes a {@link Join}
* past a non-distinct {@link Union} as its right input. */
+ @RuleConfig(value = "RIGHT")
public static final JoinUnionTransposeRule JOIN_RIGHT_UNION_TRANSPOSE =
JoinUnionTransposeRule.Config.RIGHT.toRule();
@@ -732,16 +749,19 @@ private CoreRules() {}
/** Rule that matches a {@link LogicalJoin} whose inputs are both a
* {@link MultiJoin} with intervening {@link LogicalProject}s,
* and pulls the Projects up above the Join. */
+ @RuleConfig(value = "BOTH_PROJECT")
public static final MultiJoinProjectTransposeRule MULTI_JOIN_BOTH_PROJECT =
MultiJoinProjectTransposeRule.Config.BOTH_PROJECT.toRule();
/** As {@link #MULTI_JOIN_BOTH_PROJECT} but only the left input has
* an intervening Project. */
+ @RuleConfig(value = "LEFT_PROJECT")
public static final MultiJoinProjectTransposeRule MULTI_JOIN_LEFT_PROJECT =
MultiJoinProjectTransposeRule.Config.LEFT_PROJECT.toRule();
/** As {@link #MULTI_JOIN_BOTH_PROJECT} but only the right input has
* an intervening Project. */
+ @RuleConfig(value = "RIGHT_PROJECT")
public static final MultiJoinProjectTransposeRule MULTI_JOIN_RIGHT_PROJECT =
MultiJoinProjectTransposeRule.Config.RIGHT_PROJECT.toRule();
@@ -788,7 +808,7 @@ private CoreRules() {}
* even if the Sort does not have a limit, for the merge of already sorted
* inputs that the Union can do is usually cheap. */
public static final SortUnionTransposeRule
SORT_UNION_TRANSPOSE_MATCH_NULL_FETCH =
- SortUnionTransposeRule.Config.DEFAULT.withMatchNullFetch(true).toRule();
+ SortUnionTransposeRule.Config.MATCH_NULL_FETCH.toRule();
/** Rule that copies a {@link Sort} past a {@link Join} without its limit and
* offset. The original {@link Sort} is preserved but can potentially be
@@ -802,6 +822,7 @@ private CoreRules() {}
/** Rule that merge a {@link Sort} representing the Limit semantics and
* another {@link Sort} representing the Limit or TOPN semantics. */
+ @RuleConfig(value = "LIMIT_MERGE")
public static final SortMergeRule LIMIT_MERGE =
SortMergeRule.Config.LIMIT_MERGE.toRule();
@@ -864,12 +885,14 @@ private CoreRules() {}
/** Rule that merges a {@link Filter} onto an underlying
* {@link org.apache.calcite.rel.logical.LogicalValues},
* resulting in a {@code Values} with potentially fewer rows. */
+ @RuleConfig(value = "FILTER")
public static final ValuesReduceRule FILTER_VALUES_MERGE =
ValuesReduceRule.Config.FILTER.toRule();
/** Rule that merges a {@link Project} onto an underlying
* {@link org.apache.calcite.rel.logical.LogicalValues},
* resulting in a {@code Values} with different columns. */
+ @RuleConfig(value = "PROJECT")
public static final ValuesReduceRule PROJECT_VALUES_MERGE =
ValuesReduceRule.Config.PROJECT.toRule();
@@ -878,6 +901,7 @@ private CoreRules() {}
* {@link org.apache.calcite.rel.logical.LogicalValues},
* resulting in a {@code Values} with different columns
* and potentially fewer rows. */
+ @RuleConfig(value = "PROJECT_FILTER")
public static final ValuesReduceRule PROJECT_FILTER_VALUES_MERGE =
ValuesReduceRule.Config.PROJECT_FILTER.toRule();
@@ -905,24 +929,28 @@ private CoreRules() {}
/** Rule that expands disjunction in the condition of a {@link Filter}.
*
* @see #EXPAND_JOIN_DISJUNCTION_GLOBAL */
+ @RuleConfig(value = "FILTER")
public static final ExpandDisjunctionForTableRule
EXPAND_FILTER_DISJUNCTION_GLOBAL =
ExpandDisjunctionForTableRule.Config.FILTER.toRule();
/** Rule that expands disjunction in the condition of a {@link Join}.
*
* @see #EXPAND_FILTER_DISJUNCTION_GLOBAL */
+ @RuleConfig(value = "JOIN")
public static final ExpandDisjunctionForTableRule
EXPAND_JOIN_DISJUNCTION_GLOBAL =
ExpandDisjunctionForTableRule.Config.JOIN.toRule();
/** Rule that expands disjunction in the condition of a {@link Filter} for
join inputs.
*
* @see #EXPAND_JOIN_DISJUNCTION_LOCAL */
+ @RuleConfig(value = "FILTER")
public static final ExpandDisjunctionForJoinInputsRule
EXPAND_FILTER_DISJUNCTION_LOCAL =
ExpandDisjunctionForJoinInputsRule.Config.FILTER.toRule();
/** Rule that expands disjunction in the condition of a {@link Join} for
join inputs.
*
* @see #EXPAND_FILTER_DISJUNCTION_LOCAL */
+ @RuleConfig(value = "JOIN")
public static final ExpandDisjunctionForJoinInputsRule
EXPAND_JOIN_DISJUNCTION_LOCAL =
ExpandDisjunctionForJoinInputsRule.Config.JOIN.toRule();
}
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/ExpandDisjunctionForJoinInputsRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/ExpandDisjunctionForJoinInputsRule.java
index 3a337716d5..3c149080c1 100644
---
a/core/src/main/java/org/apache/calcite/rel/rules/ExpandDisjunctionForJoinInputsRule.java
+++
b/core/src/main/java/org/apache/calcite/rel/rules/ExpandDisjunctionForJoinInputsRule.java
@@ -210,6 +210,7 @@ private ExpandDisjunctionForJoinInputsHelper(
@Value.Immutable(singleton = false)
public interface Config extends RelRule.Config {
Config FILTER =
ImmutableExpandDisjunctionForJoinInputsRule.Config.builder()
+ .withDescription("ExpandDisjunctionForJoinInputsRule(Filter)")
.withMatchHandler(ExpandDisjunctionForJoinInputsRule::matchFilter)
.build()
.withOperandSupplier(b0 ->
@@ -217,6 +218,7 @@ public interface Config extends RelRule.Config {
b1.operand(Join.class).anyInputs()));
Config JOIN = ImmutableExpandDisjunctionForJoinInputsRule.Config.builder()
+ .withDescription("ExpandDisjunctionForJoinInputsRule(Join)")
.withMatchHandler(ExpandDisjunctionForJoinInputsRule::matchJoin)
.build()
.withOperandSupplier(b -> b.operand(Join.class).anyInputs());
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java
index ef69a41189..8e58173c4b 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java
@@ -510,6 +510,12 @@ public interface FilterIntoJoinRuleConfig extends
FilterJoinRule.Config {
b0.operand(Filter.class).oneInput(b1 ->
b1.operand(Join.class).anyInputs()))
.withSmart(true);
+ FilterIntoJoinRuleConfig SMART_FALSE =
+ ImmutableFilterIntoJoinRuleConfig.of((join, joinType, exp) -> true)
+ .withOperandSupplier(b0 ->
+ b0.operand(Filter.class).oneInput(b1 ->
+ b1.operand(Join.class).anyInputs()))
+ .withSmart(false);
@Override default FilterIntoJoinRule toRule() {
return new FilterIntoJoinRule(this);
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/FilterTableFunctionTransposeRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/FilterTableFunctionTransposeRule.java
index 3b16949c5d..e429726cd6 100644
---
a/core/src/main/java/org/apache/calcite/rel/rules/FilterTableFunctionTransposeRule.java
+++
b/core/src/main/java/org/apache/calcite/rel/rules/FilterTableFunctionTransposeRule.java
@@ -123,7 +123,8 @@ public FilterTableFunctionTransposeRule(RelBuilderFactory
relBuilderFactory) {
/** Rule configuration. */
@Value.Immutable
public interface Config extends RelRule.Config {
- Config DEFAULT = ImmutableFilterTableFunctionTransposeRule.Config.of();
+ Config DEFAULT = ImmutableFilterTableFunctionTransposeRule.Config.of()
+ .withOperandFor(Filter.class, TableFunctionScan.class);
@Override default FilterTableFunctionTransposeRule toRule() {
return new FilterTableFunctionTransposeRule(this);
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/JoinCommuteRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/JoinCommuteRule.java
index c037e2fa9d..c2c06f0675 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/JoinCommuteRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/JoinCommuteRule.java
@@ -239,6 +239,8 @@ public interface Config extends RelRule.Config {
.withOperandFor(LogicalJoin.class)
.withSwapOuter(false);
+ Config SWAP_OUTER = DEFAULT.withSwapOuter(true);
+
@Override default JoinCommuteRule toRule() {
return new JoinCommuteRule(this);
}
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/JoinDeriveIsNotNullFilterRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/JoinDeriveIsNotNullFilterRule.java
index aec3cb34fb..a95354bf8f 100644
---
a/core/src/main/java/org/apache/calcite/rel/rules/JoinDeriveIsNotNullFilterRule.java
+++
b/core/src/main/java/org/apache/calcite/rel/rules/JoinDeriveIsNotNullFilterRule.java
@@ -104,7 +104,7 @@ public JoinDeriveIsNotNullFilterRule(Config config) {
* Rule configuration.
*/
@Value.Immutable public interface Config extends RelRule.Config {
- ImmutableJoinDeriveIsNotNullFilterRule.Config DEFAULT =
+ Config DEFAULT =
ImmutableJoinDeriveIsNotNullFilterRule.Config
.of().withOperandSupplier(
b -> b.operand(LogicalJoin.class).predicate(
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/RuleConfig.java
b/core/src/main/java/org/apache/calcite/rel/rules/RuleConfig.java
new file mode 100644
index 0000000000..ece2b3e359
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/rules/RuleConfig.java
@@ -0,0 +1,55 @@
+/*
+ * 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.calcite.rel.rules;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The {@code @RuleConfig} annotation is exclusively used for testing purposes
+ * to identify configuration names when a rule contains multiple Configs.
+ *
+ * <p>Usage Example:
+ *
+ * <pre>
+ * @RuleConfig(value = "FILTER")
+ * public static final ExpandDisjunctionForJoinInputsRule
+ * EXPAND_FILTER_DISJUNCTION_LOCAL =
+ * ExpandDisjunctionForJoinInputsRule.Config.FILTER.toRule();
+ * @RuleConfig(value = "JOIN")
+ * public static final ExpandDisjunctionForJoinInputsRule
+ * EXPAND_JOIN_DISJUNCTION_LOCAL =
+ * ExpandDisjunctionForJoinInputsRule.Config.JOIN.toRule();
+ * </pre>
+ *
+ * <p>Key Characteristics:
+ * <ul>
+ * <li>Test-scoped annotation (not used in production code)</li>
+ * <li>Required when a rule class has multiple Configs</li>
+ * <li>Annotation value must match the static variable that holds the
Config</li>
+ * </ul>
+ *
+ * @see CoreRules#EXPAND_FILTER_DISJUNCTION_GLOBAL
+ * @see CoreRules#EXPAND_JOIN_DISJUNCTION_LOCAL
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RuleConfig {
+ String value();
+}
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java
index e85d4f6f1b..416825ee92 100644
---
a/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java
+++
b/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java
@@ -117,6 +117,8 @@ public interface Config extends RelRule.Config {
.withOperandFor(Sort.class, Union.class)
.withMatchNullFetch(false);
+ Config MATCH_NULL_FETCH = DEFAULT.withMatchNullFetch(true);
+
@Override default SortUnionTransposeRule toRule() {
return new SortUnionTransposeRule(this);
}
diff --git a/core/src/test/java/org/apache/calcite/test/LoadCoreRulesTest.java
b/core/src/test/java/org/apache/calcite/test/LoadCoreRulesTest.java
new file mode 100644
index 0000000000..2cd8aee8c3
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/test/LoadCoreRulesTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.calcite.test;
+
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.rel.rules.CoreRules;
+
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Field;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Unit test for {@link QuidemTest} loading {@link CoreRules}.
+ */
+class LoadCoreRulesTest {
+ //~ Methods ----------------------------------------------------------------
+
+ @Test void testLoadAllRules() {
+ Field[] fields = CoreRules.class.getDeclaredFields();
+ for (Field field : fields) {
+ String fieldName = field.getName();
+ // Skip JaCoCo-injected fields (e.g. $jacocoData)
+ if (fieldName.contains("jacocoData")) {
+ continue;
+ }
+ QuidemTest.getCoreRule(fieldName);
+ }
+ }
+
+ @Test void testLoadNonExistRule() {
+ assertThrows(RuntimeException.class,
+ () -> QuidemTest.getCoreRule("xxx"));
+ }
+
+ @Test void testLoadSpecifyRule() {
+ RelOptRule rule1 =
+ QuidemTest.getCoreRule("EXPAND_FILTER_DISJUNCTION_LOCAL");
+ RelOptRule expected1 = CoreRules.EXPAND_FILTER_DISJUNCTION_LOCAL;
+ assertEquals(rule1, expected1);
+
+ RelOptRule rule2 =
+ QuidemTest.getCoreRule("EXPAND_JOIN_DISJUNCTION_LOCAL");
+ RelOptRule expected2 = CoreRules.EXPAND_JOIN_DISJUNCTION_LOCAL;
+ assertEquals(rule2, expected2);
+
+ // Same rule type with different Configs should not be equal.
+ assertNotEquals(rule1, rule2);
+ }
+
+ @Test void testLoadIncludeSubclassesRule() {
+ RelOptRule rule1 =
+ QuidemTest.getCoreRule("FILTER_REDUCE_EXPRESSIONS");
+ RelOptRule expected1 = CoreRules.FILTER_REDUCE_EXPRESSIONS;
+ assertEquals(rule1, expected1);
+
+ RelOptRule rule2 =
+ QuidemTest.getCoreRule("PROJECT_REDUCE_EXPRESSIONS");
+ RelOptRule expected2 = CoreRules.PROJECT_REDUCE_EXPRESSIONS;
+ assertEquals(rule2, expected2);
+ }
+}
diff --git a/core/src/test/resources/sql/planner.iq
b/core/src/test/resources/sql/planner.iq
index dea07b77fa..78633557c0 100644
--- a/core/src/test/resources/sql/planner.iq
+++ b/core/src/test/resources/sql/planner.iq
@@ -15,14 +15,30 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+
+# This documents the required format for .iq files containing planner rule
configurations
+#
+# CORRECT SYNTAX:
+# ---------------
+# !set planner-rules "
+# -Rule1,
+# -Rule2,
+# +Rule3"
+#
+# FORMAT RULES:
+# -------------
+# 1. Must begin with: !set planner-rules " (double quote)
+# 2. Each rule must be on its own line. All lines except last must end with
comma.
+# Final rule line must close with " (double quote) on final line
+# 3. Rules must start with - (remove) or + (add)
+
!use post
!set outputformat mysql
-# The following line is used to update the default planner rules.
-# "+" means add a rule, "-" means remove a rule. Only the "CoreRules." can be
omitted.
-!set planner-rules "-INTERSECT_TO_DISTINCT,
- -EnumerableRules.ENUMERABLE_INTERSECT_RULE,
- +CoreRules.INTERSECT_TO_SEMI_JOIN"
+!set planner-rules "
+-INTERSECT_TO_DISTINCT,
+-EnumerableRules.ENUMERABLE_INTERSECT_RULE,
++CoreRules.INTERSECT_TO_SEMI_JOIN"
# Test INTERSECT_TO_SEMI_JOIN
with t (i) as (values (0), (1))
@@ -103,9 +119,10 @@ EnumerableAggregate(group=[{}], EXPR$0=[MIN($0)],
EXPR$1=[MAX($0)])
EnumerableValues(tuples=[[{ 10 }, { 10 }, { 20 }, { 30 }, { 30 }, { 50 }, {
50 }, { 60 }, { null }]])
!plan
-!set planner-rules "+AGGREGATE_MIN_MAX_TO_LIMIT,
- -EnumerableRules.ENUMERABLE_AGGREGATE_RULE,
- +PROJECT_SUB_QUERY_TO_CORRELATE"
+!set planner-rules "
++AGGREGATE_MIN_MAX_TO_LIMIT,
+-EnumerableRules.ENUMERABLE_AGGREGATE_RULE,
++PROJECT_SUB_QUERY_TO_CORRELATE"
select min(deptno), max(deptno) from emp;
+--------+--------+
| EXPR$0 | EXPR$1 |
@@ -213,7 +230,8 @@ EnumerableMergeJoin(condition=[AND(=($0, $6), OR(AND(>($1,
11), <=($7, 32)), AND
EnumerableValues(tuples=[[{ 1, 31, 311 }, { 2, 32, 322 }, { 3, 33, 333 }, {
4, 34, 344 }, { 5, 35, 355 }]])
!plan
-!set planner-rules "+CoreRules.EXPAND_FILTER_DISJUNCTION_LOCAL,
+CoreRules.EXPAND_JOIN_DISJUNCTION_LOCAL"
+!set planner-rules "
++CoreRules.EXPAND_FILTER_DISJUNCTION_LOCAL"
with t1 (id1, col11, col12) as (values (1, 11, 111), (2, 12, 122), (3, 13,
133), (4, 14, 144), (5, 15, 155)),
t2 (id2, col21, col22) as (values (1, 21, 211), (2, 22, 222), (3, 23, 233),
(4, 24, 244), (5, 25, 255)),
@@ -241,7 +259,6 @@ EnumerableHashJoin(condition=[AND(=($0, $6), OR(AND(>($1,
11), <=($7, 32)), AND(
EnumerableCalc(expr#0..2=[{inputs}], expr#3=[32], expr#4=[<=($t1, $t3)],
expr#5=[344], expr#6=[>=($t2, $t5)], expr#7=[OR($t4, $t6)],
proj#0..2=[{exprs}], $condition=[$t7])
EnumerableValues(tuples=[[{ 1, 31, 311 }, { 2, 32, 322 }, { 3, 33, 333 },
{ 4, 34, 344 }, { 5, 35, 355 }]])
!plan
-
!set planner-rules original
# End planner.iq
diff --git a/testkit/src/main/java/org/apache/calcite/test/QuidemTest.java
b/testkit/src/main/java/org/apache/calcite/test/QuidemTest.java
index 5c1bd044b9..1ac85fb17d 100644
--- a/testkit/src/main/java/org/apache/calcite/test/QuidemTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/QuidemTest.java
@@ -22,8 +22,10 @@
import org.apache.calcite.jdbc.CalciteConnection;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelRule.Config;
import org.apache.calcite.prepare.Prepare;
import org.apache.calcite.rel.rules.CoreRules;
+import org.apache.calcite.rel.rules.RuleConfig;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.runtime.Hook;
@@ -51,8 +53,10 @@
import java.io.FilenameFilter;
import java.io.Reader;
import java.io.Writer;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Connection;
@@ -242,8 +246,7 @@ private void parseRules(String value, List<RelOptRule>
rulesAdd, List<RelOptRule
try {
if (ruleSource == null || ruleSource.equals("CoreRules")) {
- Object rule = CoreRules.class.getField(ruleName).get(null);
- setRules(operation, (RelOptRule) rule, rulesAdd, rulesRemove);
+ setRules(operation, getCoreRule(ruleName), rulesAdd, rulesRemove);
} else if (ruleSource.equals("EnumerableRules")) {
Object rule = EnumerableRules.class.getField(ruleName).get(null);
setRules(operation, (RelOptRule) rule, rulesAdd, rulesRemove);
@@ -256,6 +259,55 @@ private void parseRules(String value, List<RelOptRule>
rulesAdd, List<RelOptRule
}
}
+ public static RelOptRule getCoreRule(String ruleName) {
+ RelOptRule rule = null;
+ try {
+ // Get rule class and config annotation
+ Field ruleField = CoreRules.class.getField(ruleName);
+ Class<?> ruleClass = ruleField.getType();
+
+ // Find Config inner class
+ Class<?> configClass = null;
+ for (Class<?> innerClass : ruleClass.getDeclaredClasses()) {
+ if (innerClass.getSimpleName().endsWith("Config")) {
+ configClass = innerClass;
+ break;
+ }
+ }
+ if (configClass == null) {
+ // Should not enter
+ throw new RuntimeException("Config not found in " +
ruleClass.getName());
+ }
+
+ // Determine config field name
+ RuleConfig ruleConfig = ruleField.getAnnotation(RuleConfig.class);
+ String configValue = (ruleConfig == null || ruleConfig.value().isEmpty())
+ ? "DEFAULT"
+ : ruleConfig.value();
+
+ // Find and process the target config field
+ for (Field field : configClass.getDeclaredFields()) {
+ if (field.getType() == configClass
+ && Modifier.isStatic(field.getModifiers())
+ && field.getName().equals(configValue)) {
+ field.setAccessible(true);
+ Config config = (Config) field.get(null);
+ rule = config.toRule();
+ break;
+ }
+ }
+
+ if (rule == null) {
+ throw new RuntimeException("No matching config value '" + configValue
+ + "' found in " + configClass.getName());
+ }
+ } catch (NoSuchFieldException | IllegalAccessException
+ | RuntimeException e) {
+ throw new RuntimeException("Failed to get rule '" + ruleName + "': " +
e.getMessage());
+ }
+ return rule;
+ }
+
private void setRules(char operation, RelOptRule rule,
List<RelOptRule> rulesAdd, List<RelOptRule> rulesRemove) {
if (operation == '+') {