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>

Reply via email to