This is an automated email from the ASF dual-hosted git repository.
xuzifu666 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 4a46a9bf87 [CALCITE-7672] PruneEmptyRules should support pruning empty
Calc
4a46a9bf87 is described below
commit 4a46a9bf870eb7ceb3c87ce1b7a04222bbf74be8
Author: Yu Xu <[email protected]>
AuthorDate: Mon Jul 27 11:38:50 2026 +0800
[CALCITE-7672] PruneEmptyRules should support pruning empty Calc
---
.../java/org/apache/calcite/plan/RelOptRules.java | 1 +
.../apache/calcite/rel/rules/PruneEmptyRules.java | 17 +++++++++++++++++
.../org/apache/calcite/test/RelOptRulesTest.java | 16 ++++++++++++++++
.../org/apache/calcite/test/RelOptRulesTest.xml | 20 ++++++++++++++++++++
4 files changed, 54 insertions(+)
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptRules.java
b/core/src/main/java/org/apache/calcite/plan/RelOptRules.java
index 2abce5f327..434e278dcb 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptRules.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptRules.java
@@ -101,6 +101,7 @@ private RelOptRules() {
PruneEmptyRules.MINUS_INSTANCE,
PruneEmptyRules.PROJECT_INSTANCE,
PruneEmptyRules.FILTER_INSTANCE,
+ PruneEmptyRules.CALC_INSTANCE,
PruneEmptyRules.SORT_INSTANCE,
PruneEmptyRules.AGGREGATE_INSTANCE,
PruneEmptyRules.WINDOW_INSTANCE,
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
b/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
index 5cb60bc906..95331c69a4 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
@@ -26,6 +26,7 @@
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.SingleRel;
import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Calc;
import org.apache.calcite.rel.core.Correlate;
import org.apache.calcite.rel.core.Filter;
import org.apache.calcite.rel.core.Intersect;
@@ -191,6 +192,19 @@ private static boolean isEmpty(RelNode node) {
public static final RelOptRule FILTER_INSTANCE =
RemoveEmptySingleRule.RemoveEmptySingleRuleConfig.FILTER.toRule();
+ /**
+ * Rule that converts a {@link org.apache.calcite.rel.core.Calc}
+ * to empty if its child is empty.
+ *
+ * <p>Examples:
+ *
+ * <ul>
+ * <li>Calc(Empty) becomes Empty
+ * </ul>
+ */
+ public static final RelOptRule CALC_INSTANCE =
+ RemoveEmptySingleRule.RemoveEmptySingleRuleConfig.CALC.toRule();
+
/**
* Rule that converts a {@link org.apache.calcite.rel.core.Sort}
* to empty if its child is empty.
@@ -373,6 +387,9 @@ public interface RemoveEmptySingleRuleConfig extends
PruneEmptyRule.Config {
RemoveEmptySingleRuleConfig FILTER =
ImmutableRemoveEmptySingleRuleConfig.of()
.withDescription("PruneEmptyFilter")
.withOperandFor(Filter.class, singleRel -> true);
+ RemoveEmptySingleRuleConfig CALC =
ImmutableRemoveEmptySingleRuleConfig.of()
+ .withDescription("PruneEmptyCalc")
+ .withOperandFor(Calc.class, singleRel -> true);
RemoveEmptySingleRuleConfig SORT =
ImmutableRemoveEmptySingleRuleConfig.of()
.withDescription("PruneEmptySort")
.withOperandFor(Sort.class, singleRel -> true);
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 52c1875e36..5c687519da 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -5349,6 +5349,22 @@ RelOptFixture checkDynamicFunctions(boolean
treatDynamicCallsAsConstant) {
.check();
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7672">[CALCITE-7672]
+ * PruneEmptyRules should support pruning empty Calc</a>.
+ */
+ @Test void testEmptyCalc() {
+ final String sql = "select z + x from (\n"
+ + " select x + y as z, x from (\n"
+ + " select * from (values (10, 1), (30, 3)) as t (x, y)\n"
+ + " where x + y > 50))";
+ sql(sql)
+ .withRule(CoreRules.FILTER_VALUES_MERGE,
+ CoreRules.PROJECT_TO_CALC,
+ PruneEmptyRules.CALC_INSTANCE)
+ .check();
+ }
+
@Test void testEmptyIntersect() {
final String sql = "select * from (values (30, 3))"
+ "intersect\n"
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 e2b725a7d6..f1bb4248e5 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -4212,6 +4212,26 @@ LogicalAggregate(group=[{}], EXPR$0=[COUNT()],
EXPR$1=[SUM($0)])
<Resource name="planAfter">
<![CDATA[
LogicalValues(tuples=[[{ 0, null }]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testEmptyCalc">
+ <Resource name="sql">
+ <![CDATA[select z + x from (
+ select x + y as z, x from (
+ select * from (values (10, 1), (30, 3)) as t (x, y)
+ where x + y > 50))]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(EXPR$0=[+(+($0, $1), $0)])
+ LogicalFilter(condition=[>(+($0, $1), 50)])
+ LogicalValues(tuples=[[{ 10, 1 }, { 30, 3 }]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalValues(tuples=[[]])
]]>
</Resource>
</TestCase>