This is an automated email from the ASF dual-hosted git repository.
xiong 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 a4b6f6ea41 [CALCITE-6953] Extend UnionEliminatorRule to support
Intersect and Minus
a4b6f6ea41 is described below
commit a4b6f6ea41e91881dc11d56a5cd44047c4fcb430
Author: Zhen Chen <[email protected]>
AuthorDate: Tue Apr 15 18:41:15 2025 +0800
[CALCITE-6953] Extend UnionEliminatorRule to support Intersect and Minus
---
.../org/apache/calcite/rel/rules/CoreRules.java | 12 +++++++
.../calcite/rel/rules/UnionEliminatorRule.java | 40 ++++++++++++++++++----
.../org/apache/calcite/test/RelOptRulesTest.java | 39 +++++++++++++++++++++
.../org/apache/calcite/test/RelOptRulesTest.xml | 39 +++++++++++++++++++++
4 files changed, 123 insertions(+), 7 deletions(-)
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 bd2ebb06b5..76b3fb5e13 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
@@ -355,6 +355,12 @@ private CoreRules() {}
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 */
+ public static final UnionEliminatorRule INTERSECT_REMOVE =
+ UnionEliminatorRule.Config.INTERSECT.toRule();
+
/** Planner rule that reorders inputs of an {@link Intersect} to put smaller
inputs first.
* This helps reduce the size of intermediate results. */
public static final IntersectReorderRule INTERSECT_REORDER =
@@ -394,6 +400,12 @@ private CoreRules() {}
public static final UnionMergeRule MINUS_MERGE =
UnionMergeRule.Config.MINUS.toRule();
+ /** Rule that removes a {@link Minus} if it has only one input.
+ *
+ * @see PruneEmptyRules#UNION_INSTANCE */
+ public static final UnionEliminatorRule MINUS_REMOVE =
+ UnionEliminatorRule.Config.MINUS.toRule();
+
/** Rule that matches a {@link Project} on an {@link Aggregate},
* projecting away aggregate calls that are not used. */
public static final ProjectAggregateMergeRule PROJECT_AGGREGATE_MERGE =
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/UnionEliminatorRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/UnionEliminatorRule.java
index 81b76c326f..e09f3be58a 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/UnionEliminatorRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/UnionEliminatorRule.java
@@ -18,7 +18,12 @@
import org.apache.calcite.plan.RelOptRuleCall;
import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Intersect;
+import org.apache.calcite.rel.core.Minus;
+import org.apache.calcite.rel.core.SetOp;
import org.apache.calcite.rel.core.Union;
+import org.apache.calcite.rel.logical.LogicalIntersect;
+import org.apache.calcite.rel.logical.LogicalMinus;
import org.apache.calcite.rel.logical.LogicalUnion;
import org.apache.calcite.tools.RelBuilderFactory;
@@ -29,6 +34,9 @@
* Union call by eliminating the Union operator altogether in the case the call
* consists of only one input.
*
+ * <p>Originally written for {@link Union} (hence the name),
+ * but now also applies to {@link Intersect} and {@link Minus}.
+ *
* @see CoreRules#UNION_REMOVE
*/
@Value.Enclosing
@@ -41,6 +49,15 @@ protected UnionEliminatorRule(Config config) {
super(config);
}
+ @Deprecated // to be removed before 2.0
+ public UnionEliminatorRule(Class<? extends SetOp> setOpClass,
+ String description, RelBuilderFactory relBuilderFactory) {
+ super(Config.DEFAULT.withRelBuilderFactory(relBuilderFactory)
+ .withDescription(description)
+ .as(Config.class)
+ .withOperandFor(setOpClass));
+ }
+
@Deprecated // to be removed before 2.0
public UnionEliminatorRule(Class<? extends Union> unionClass,
RelBuilderFactory relBuilderFactory) {
@@ -52,13 +69,13 @@ public UnionEliminatorRule(Class<? extends Union>
unionClass,
//~ Methods ----------------------------------------------------------------
@Override public boolean matches(RelOptRuleCall call) {
- Union union = call.rel(0);
- return union.all && union.getInputs().size() == 1;
+ SetOp setOp = call.rel(0);
+ return setOp.all && setOp.getInputs().size() == 1;
}
@Override public void onMatch(RelOptRuleCall call) {
- Union union = call.rel(0);
- call.transformTo(union.getInputs().get(0));
+ SetOp setOp = call.rel(0);
+ call.transformTo(setOp.getInputs().get(0));
}
@Override public boolean autoPruneOld() {
@@ -69,15 +86,24 @@ public UnionEliminatorRule(Class<? extends Union>
unionClass,
@Value.Immutable
public interface Config extends RelRule.Config {
Config DEFAULT = ImmutableUnionEliminatorRule.Config.of()
- .withOperandFor(LogicalUnion.class);
+ .withDescription("UnionEliminatorRule")
+ .withOperandFor(LogicalUnion.class);
+
+ Config INTERSECT = ImmutableUnionEliminatorRule.Config.of()
+ .withDescription("IntersectEliminatorRule")
+ .withOperandFor(LogicalIntersect.class);
+
+ Config MINUS = ImmutableUnionEliminatorRule.Config.of()
+ .withDescription("MinusEliminatorRule")
+ .withOperandFor(LogicalMinus.class);
@Override default UnionEliminatorRule toRule() {
return new UnionEliminatorRule(this);
}
/** Defines an operand tree for the given classes. */
- default Config withOperandFor(Class<? extends Union> unionClass) {
- return withOperandSupplier(b -> b.operand(unionClass).anyInputs())
+ default Config withOperandFor(Class<? extends SetOp> setOpClass) {
+ return withOperandSupplier(b -> b.operand(setOpClass).anyInputs())
.as(Config.class);
}
}
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index 65720a062c..08abdd46e8 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -62,8 +62,11 @@
import org.apache.calcite.rel.logical.LogicalAggregate;
import org.apache.calcite.rel.logical.LogicalCorrelate;
import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.logical.LogicalIntersect;
+import org.apache.calcite.rel.logical.LogicalMinus;
import org.apache.calcite.rel.logical.LogicalProject;
import org.apache.calcite.rel.logical.LogicalTableModify;
+import org.apache.calcite.rel.logical.LogicalUnion;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.rules.AggregateExpandWithinDistinctRule;
import org.apache.calcite.rel.rules.AggregateExtractProjectRule;
@@ -3209,6 +3212,42 @@ private void
checkPushJoinThroughUnionOnRightDoesNotMatchSemiOrAntiJoin(JoinRelT
.check();
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-6953">[CALCITE-6953]
+ * Extend UnionEliminatorRule to support Intersect and Minus</a>. */
+ @Test void testUnionEliminatorRuleUnion() {
+ final Function<RelBuilder, RelNode> relFn =
+ b -> LogicalUnion.create(ImmutableList.of(b.scan("DEPT").build()),
true);
+
+ relFn(relFn)
+ .withRule(CoreRules.UNION_REMOVE)
+ .check();
+ }
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-6953">[CALCITE-6953]
+ * Extend UnionEliminatorRule to support Intersect and Minus</a>. */
+ @Test void testUnionEliminatorRuleIntersect() {
+ final Function<RelBuilder, RelNode> relFn =
+ b -> LogicalIntersect.create(ImmutableList.of(b.scan("DEPT").build()),
true);
+
+ relFn(relFn)
+ .withRule(CoreRules.INTERSECT_REMOVE)
+ .check();
+ }
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-6953">[CALCITE-6953]
+ * Extend UnionEliminatorRule to support Intersect and Minus</a>. */
+ @Test void testUnionEliminatorRuleMinus() {
+ final Function<RelBuilder, RelNode> relFn =
+ b -> LogicalMinus.create(ImmutableList.of(b.scan("DEPT").build()),
true);
+
+ relFn(relFn)
+ .withRule(CoreRules.MINUS_REMOVE)
+ .check();
+ }
+
/** Test case for
* <a
href="https://issues.apache.org/jira/browse/CALCITE-6617">[CALCITE-6617]
* TypeCoercion is not applied correctly to comparisons</a>. */
diff --git
a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index c3f66840f5..1a13759208 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -18363,6 +18363,45 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2],
MGR=[$3], HIREDATE=[$4], SAL=[$
LogicalFilter(condition=[AND(<(+($0, 50), 20), >=($cor0.DEPTNO,
$9))])
LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3],
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[+(30, $7)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testUnionEliminatorRuleIntersect">
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalIntersect(all=[true])
+ LogicalTableScan(table=[[scott, DEPT]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalTableScan(table=[[scott, DEPT]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testUnionEliminatorRuleMinus">
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalMinus(all=[true])
+ LogicalTableScan(table=[[scott, DEPT]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalTableScan(table=[[scott, DEPT]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testUnionEliminatorRuleUnion">
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalUnion(all=[true])
+ LogicalTableScan(table=[[scott, DEPT]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalTableScan(table=[[scott, DEPT]])
]]>
</Resource>
</TestCase>