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 7563e44b23 [CALCITE-6955] PruneEmptyRules does not handle the all
attribute of SetOp correctly
7563e44b23 is described below
commit 7563e44b2320ef2d90ca4284ea668ea13836abc7
Author: Zhen Chen <[email protected]>
AuthorDate: Thu Apr 17 10:07:42 2025 +0800
[CALCITE-6955] PruneEmptyRules does not handle the all attribute of SetOp
correctly
---
.../apache/calcite/rel/rules/PruneEmptyRules.java | 6 +++
.../org/apache/calcite/test/RelOptRulesTest.java | 35 +++++++++++++++
.../org/apache/calcite/test/RelOptRulesTest.xml | 52 ++++++++++++++++++++++
core/src/test/resources/sql/set-op.iq | 37 +++++++++++++++
4 files changed, 130 insertions(+)
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 58a5caf02a..1afbef100e 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
@@ -406,6 +406,9 @@ public interface UnionEmptyPruneRuleConfig extends
PruneEmptyRule.Config {
+ RelOptUtil.toString(union);
if (nonEmptyInputs == 0) {
relBuilder.push(union).empty();
+ } else if (nonEmptyInputs == 1 && !union.all) {
+ relBuilder.distinct();
+ relBuilder.convert(union.getRowType(), true);
} else {
relBuilder.union(union.all, nonEmptyInputs);
relBuilder.convert(union.getRowType(), true);
@@ -447,6 +450,9 @@ public interface MinusEmptyPruneRuleConfig extends
PruneEmptyRule.Config {
+ RelOptUtil.toString(minus);
if (nonEmptyInputs == 0) {
relBuilder.push(minus).empty();
+ } else if (nonEmptyInputs == 1 && !minus.all) {
+ relBuilder.distinct();
+ relBuilder.convert(minus.getRowType(), true);
} else {
relBuilder.minus(minus.all, nonEmptyInputs);
relBuilder.convert(minus.getRowType(), 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 08abdd46e8..7345adb00d 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -4694,6 +4694,26 @@ RelOptFixture checkDynamicFunctions(boolean
treatDynamicCallsAsConstant) {
.check();
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-6955">[CALCITE-6955]
+ * PruneEmptyRules does not handle the all attribute of SetOp correctly</a>.
*/
+ @Test void testEmptyFilterProjectUnion2() {
+ final String sql = "select * from (\n"
+ + "select * from (values (10, 1), (30, 3), (30, 3)) as t (x, y)\n"
+ + "union\n"
+ + "select * from (values (20, 2))\n"
+ + ")\n"
+ + "where x + y > 30";
+ sql(sql)
+ .withRule(CoreRules.FILTER_SET_OP_TRANSPOSE,
+ CoreRules.FILTER_PROJECT_TRANSPOSE,
+ CoreRules.PROJECT_MERGE,
+ CoreRules.PROJECT_FILTER_VALUES_MERGE,
+ PruneEmptyRules.PROJECT_INSTANCE,
+ PruneEmptyRules.UNION_INSTANCE)
+ .check();
+ }
+
/** Test case for
* <a
href="https://issues.apache.org/jira/browse/CALCITE-1488">[CALCITE-1488]
* ValuesReduceRule should ignore empty Values</a>. */
@@ -4766,6 +4786,21 @@ RelOptFixture checkDynamicFunctions(boolean
treatDynamicCallsAsConstant) {
.check();
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-6955">[CALCITE-6955]
+ * PruneEmptyRules does not handle the all attribute of SetOp correctly</a>.
*/
+ @Test void testEmptyMinus3() {
+ // First input is empty; therefore whole expression is empty
+ final String sql = "select * from (values (30, 3), (30, 3)) as t (x, y)\n"
+ + "except\n"
+ + "select * from (values (20, 2)) as t (x, y) where x > 30";
+ sql(sql)
+ .withRule(CoreRules.PROJECT_FILTER_VALUES_MERGE,
+ PruneEmptyRules.PROJECT_INSTANCE,
+ PruneEmptyRules.MINUS_INSTANCE)
+ .check();
+ }
+
@Test void testEmptyTable() {
// table is transformed to empty values and extra project will be removed.
final String sql = "select * from EMPTY_PRODUCTS\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 1a13759208..7f283af937 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -3741,6 +3741,34 @@ LogicalProject(X=[$0], Y=[$1])
<![CDATA[
LogicalProject(X=[$0], Y=[$1])
LogicalValues(tuples=[[{ 30, 3 }]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testEmptyFilterProjectUnion2">
+ <Resource name="sql">
+ <![CDATA[select * from (
+select * from (values (10, 1), (30, 3), (30, 3)) as t (x, y)
+union
+select * from (values (20, 2))
+)
+where x + y > 30]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(X=[$0], Y=[$1])
+ LogicalFilter(condition=[>(+($0, $1), 30)])
+ LogicalUnion(all=[false])
+ LogicalProject(X=[$0], Y=[$1])
+ LogicalValues(tuples=[[{ 10, 1 }, { 30, 3 }, { 30, 3 }]])
+ LogicalProject(EXPR$0=[$0], EXPR$1=[$1])
+ LogicalValues(tuples=[[{ 20, 2 }]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(X=[$0], Y=[$1])
+ LogicalAggregate(group=[{0, 1}])
+ LogicalValues(tuples=[[{ 30, 3 }, { 30, 3 }]])
]]>
</Resource>
</TestCase>
@@ -3850,6 +3878,30 @@ LogicalMinus(all=[false])
LogicalValues(tuples=[[{ 30, 3 }]])
LogicalProject(EXPR$0=[$0], EXPR$1=[$1])
LogicalValues(tuples=[[{ 40, 4 }]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testEmptyMinus3">
+ <Resource name="sql">
+ <![CDATA[select * from (values (30, 3), (30, 3)) as t (x, y)
+except
+select * from (values (20, 2)) as t (x, y) where x > 30]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalMinus(all=[false])
+ LogicalProject(X=[$0], Y=[$1])
+ LogicalValues(tuples=[[{ 30, 3 }, { 30, 3 }]])
+ LogicalProject(X=[$0], Y=[$1])
+ LogicalFilter(condition=[>($0, 30)])
+ LogicalValues(tuples=[[{ 20, 2 }]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalAggregate(group=[{0, 1}])
+ LogicalProject(X=[$0], Y=[$1])
+ LogicalValues(tuples=[[{ 30, 3 }, { 30, 3 }]])
]]>
</Resource>
</TestCase>
diff --git a/core/src/test/resources/sql/set-op.iq
b/core/src/test/resources/sql/set-op.iq
index 3e6c56b1d2..4b3917ad30 100644
--- a/core/src/test/resources/sql/set-op.iq
+++ b/core/src/test/resources/sql/set-op.iq
@@ -258,4 +258,41 @@ UNION
(2 rows)
!ok
+
+# [CALCITE-6955] PruneEmptyRules does not handle the all attribute of SetOp
correctly
+select * from (values (10, 1), (30, 3), (30, 3)) as t (x, y)
+union
+select * from (values (20, 2)) as t (x, y) where x > 30;
++----+---+
+| X | Y |
++----+---+
+| 10 | 1 |
+| 30 | 3 |
++----+---+
+(2 rows)
+
+!ok
+
+EnumerableAggregate(group=[{0, 1}])
+ EnumerableValues(tuples=[[{ 10, 1 }, { 30, 3 }, { 30, 3 }]])
+!plan
+
+# [CALCITE-6955] PruneEmptyRules does not handle the all attribute of SetOp
correctly
+select * from (values (30, 3), (30, 3)) as t (x, y)
+except
+select * from (values (20, 2)) as t (x, y) where x > 30;
++----+---+
+| X | Y |
++----+---+
+| 30 | 3 |
++----+---+
+(1 row)
+
+!ok
+
+EnumerableCalc(expr#0=[{inputs}], expr#1=[3], proj#0..1=[{exprs}])
+ EnumerableAggregate(group=[{0}])
+ EnumerableValues(tuples=[[{ 30, 3 }, { 30, 3 }]])
+!plan
+
# End set-op.iq