This is an automated email from the ASF dual-hosted git repository.

mihaibudiu 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 f840e7ab3f [CALCITE-7779] Optimization rule 
FilterAggregateTransposeRule rewrites queries to semantically non-equivalent 
ones
f840e7ab3f is described below

commit f840e7ab3f8121e71035a7a1ed3fc1372f0139d5
Author: npaincomplet <[email protected]>
AuthorDate: Sun Sep 13 22:38:48 2026 +0200

    [CALCITE-7779] Optimization rule FilterAggregateTransposeRule rewrites 
queries to semantically non-equivalent ones
---
 .../rel/rules/FilterAggregateTransposeRule.java    |  8 +++-
 .../org/apache/calcite/test/RelOptRulesTest.java   | 26 +++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml    | 52 ++++++++++++++++++++++
 core/src/test/resources/sql/hep.iq                 | 22 +++++++++
 4 files changed, 107 insertions(+), 1 deletion(-)

diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
index 5edac100ac..e274ace199 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
@@ -32,6 +32,7 @@
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.tools.RelBuilderFactory;
 import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.mapping.Mappings;
 
 import com.google.common.collect.ImmutableList;
 
@@ -149,8 +150,13 @@ private static boolean canPush(Aggregate aggregate, 
ImmutableBitSet rCols) {
       // If grouping sets are used, the filter can be pushed if
       // the columns referenced in the predicate are present in
       // all the grouping sets.
+      final Mappings.TargetMapping aggregateInputToOutput =
+          Mappings.target(aggregate.getGroupSet().asList(),
+              aggregate.getInput().getRowType().getFieldCount());
       for (ImmutableBitSet groupingSet : aggregate.getGroupSets()) {
-        if (!groupingSet.contains(rCols)) {
+        final ImmutableBitSet groupingSetOutputRefs =
+            groupingSet.permute(aggregateInputToOutput);
+        if (!groupingSetOutputRefs.contains(rCols)) {
           return false;
         }
       }
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 7eae22b17e..a7945606bc 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -1364,6 +1364,32 @@ private RelOptFixture 
basePushFilterPastAggWithGroupingSets() {
     basePushFilterPastAggWithGroupingSets().check();
   }
 
+  private RelOptFixture pushFilterPastAggWithNonLeadingGroupingKeys(String 
condition) {
+    final String sql = "select a, b\n"
+        + "from (values (0, 1, 2)) as t(unused, a, b)\n"
+        + "group by grouping sets ((a), (a, b))\n"
+        + "having " + condition;
+    return sql(sql)
+        .withPreRule(CoreRules.AGGREGATE_PROJECT_MERGE)
+        .withRule(CoreRules.FILTER_AGGREGATE_TRANSPOSE);
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7779";>[CALCITE-7779]
+   * Optimization rule FilterAggregateTransposeRule rewrites queries to
+   * semantically non-equivalent ones</a>. */
+  @Test void testPushFilterPastAggWithGroupingSetsNonLeadingKeysIsNull() {
+    pushFilterPastAggWithNonLeadingGroupingKeys("b is null").checkUnchanged();
+  }
+
+  @Test void testPushFilterPastAggWithGroupingSetsNonLeadingKeysEquals() {
+    pushFilterPastAggWithNonLeadingGroupingKeys("b = 2").checkUnchanged();
+  }
+
+  @Test void testPushFilterPastAggWithGroupingSetsNonLeadingKeys() {
+    pushFilterPastAggWithNonLeadingGroupingKeys("a = 1").check();
+  }
+
   /** Test case for
    * <a href="https://issues.apache.org/jira/browse/CALCITE-434";>[CALCITE-434]
    * FilterAggregateTransposeRule loses conditions that cannot be pushed</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 88c80246ad..97e54f5c7c 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -15104,6 +15104,58 @@ LogicalProject(DNAME=[$0], DDEPTNO=[$1], C=[$2])
     LogicalFilter(condition=[=($0, 'Charlie')])
       LogicalProject(DNAME=[$1], DDEPTNO=[$0])
         LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testPushFilterPastAggWithGroupingSetsNonLeadingKeys">
+    <Resource name="sql">
+      <![CDATA[select a, b
+from (values (0, 1, 2)) as t(unused, a, b)
+group by grouping sets ((a), (a, b))
+having a = 1]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalFilter(condition=[=($0, 1)])
+  LogicalAggregate(group=[{1, 2}], groups=[[{1, 2}, {1}]])
+    LogicalValues(tuples=[[{ 0, 1, 2 }]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+LogicalAggregate(group=[{1, 2}], groups=[[{1, 2}, {1}]])
+  LogicalFilter(condition=[=($1, 1)])
+    LogicalValues(tuples=[[{ 0, 1, 2 }]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testPushFilterPastAggWithGroupingSetsNonLeadingKeysEquals">
+    <Resource name="sql">
+      <![CDATA[select a, b
+from (values (0, 1, 2)) as t(unused, a, b)
+group by grouping sets ((a), (a, b))
+having b = 2]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalFilter(condition=[=($1, 2)])
+  LogicalAggregate(group=[{1, 2}], groups=[[{1, 2}, {1}]])
+    LogicalValues(tuples=[[{ 0, 1, 2 }]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testPushFilterPastAggWithGroupingSetsNonLeadingKeysIsNull">
+    <Resource name="sql">
+      <![CDATA[select a, b
+from (values (0, 1, 2)) as t(unused, a, b)
+group by grouping sets ((a), (a, b))
+having b is null]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalFilter(condition=[IS NULL($1)])
+  LogicalAggregate(group=[{1, 2}], groups=[[{1, 2}, {1}]])
+    LogicalValues(tuples=[[{ 0, 1, 2 }]])
 ]]>
     </Resource>
   </TestCase>
diff --git a/core/src/test/resources/sql/hep.iq 
b/core/src/test/resources/sql/hep.iq
index 812663177a..b9aee8e1f4 100644
--- a/core/src/test/resources/sql/hep.iq
+++ b/core/src/test/resources/sql/hep.iq
@@ -353,4 +353,26 @@ EnumerableAggregate(group=[{0}], EXPR$1=[COUNT()])
 
 !set hep-rules original
 
+# [CALCITE-7779] Optimization rule FilterAggregateTransposeRule rewrites 
queries to semantically non-equivalent ones
+!set hep-rules "
++CoreRules.AGGREGATE_PROJECT_MERGE,
++CoreRules.FILTER_AGGREGATE_TRANSPOSE"
+
+select dname, loc
+from dept
+group by grouping sets ((dname), (dname, loc))
+having loc is null;
++------------+-----+
+| DNAME      | LOC |
++------------+-----+
+| ACCOUNTING |     |
+| OPERATIONS |     |
+| RESEARCH   |     |
+| SALES      |     |
++------------+-----+
+(4 rows)
+
+!ok
+!set hep-rules original
+
 # End hep.iq

Reply via email to