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

mbudiu 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 b24f476127 [CALCITE-7320] AggregateProjectMergeRule throws 
AssertionError when Project maps multiple grouping keys to the same field
b24f476127 is described below

commit b24f47612768280929cdb3fc6639a93ea6614bac
Author: iwanttobepowerful <[email protected]>
AuthorDate: Fri Jan 23 10:37:07 2026 +0800

    [CALCITE-7320] AggregateProjectMergeRule throws AssertionError when Project 
maps multiple grouping keys to the same field
---
 .../rel/rules/AggregateProjectMergeRule.java       |  8 +++
 .../apache/calcite/sql2rel/RelDecorrelator.java    |  2 +-
 .../calcite/sql2rel/RelDecorrelatorTest.java       | 73 ++++++++++++++++++++++
 core/src/test/resources/sql/sub-query.iq           | 22 +++++++
 4 files changed, 104 insertions(+), 1 deletion(-)

diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java
index 7ad0d57b84..2068b0e40c 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java
@@ -110,6 +110,14 @@ public AggregateProjectMergeRule(
       newGroupingSets =
           ImmutableBitSet.ORDERING.immutableSortedCopy(
               ImmutableBitSet.permute(aggregate.getGroupSets(), map));
+      for (int i = 0; i < newGroupingSets.size() - 1; i++) {
+        if (newGroupingSets.get(i).equals(newGroupingSets.get(i + 1))) {
+          // If the project merges two columns that are both in the grouping 
sets,
+          // we might get duplicate grouping sets. Aggregate does not allow
+          // duplicate grouping sets, so we abort the rule.
+          return null;
+        }
+      }
     }
 
     final ImmutableList.Builder<AggregateCall> aggCalls =
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java 
b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
index 226159104a..f0b7ad4098 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
@@ -992,7 +992,7 @@ private RelNode rewriteScalarAggregate(Aggregate oldRel,
     for (int i1 = 0; i1 < oldRel.getAggCallList().size(); i1++) {
       AggregateCall aggCall = oldRel.getAggCallList().get(i1);
       if (aggCall.getAggregation() instanceof SqlCountAggFunction) {
-        int index = requireNonNull(outputMap.get(i1 + 
oldRel.getGroupSet().size()));
+        int index = requireNonNull(outputMap.get(i1 + oldRel.getGroupCount()));
         final RexInputRef ref = RexInputRef.of(index + valueGenFieldCount, 
joinRowType);
         ImmutableList<RexNode> exprs =
             ImmutableList.of(relBuilder.isNotNull(ref), ref, 
relBuilder.literal(0));
diff --git 
a/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java 
b/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java
index 6d1d2a974b..6ac711e406 100644
--- a/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java
+++ b/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java
@@ -1438,4 +1438,77 @@ public static Frameworks.ConfigBuilder config() {
         + "                  LogicalTableScan(table=[[scott, DEPT]])\n";
     assertThat(after, hasTree(planAfter));
   }
+
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7320";>[CALCITE-7320]
+   * AggregateProjectMergeRule throws AssertionError when Project maps 
multiple grouping keys
+   * to the same field</a>. */
+  @Test void test7320() {
+    final FrameworkConfig frameworkConfig = config().build();
+    final RelBuilder builder = RelBuilder.create(frameworkConfig);
+    final RelOptCluster cluster = builder.getCluster();
+    final Planner planner = Frameworks.getPlanner(frameworkConfig);
+    final String sql = ""
+        + "SELECT deptno,\n"
+        + "       (SELECT SUM(cnt)\n"
+        + "        FROM (\n"
+        + "          SELECT COUNT(*) AS cnt\n"
+        + "          FROM emp\n"
+        + "          WHERE emp.deptno = dept.deptno\n"
+        + "          GROUP BY GROUPING SETS ((deptno), ())\n"
+        + "))\n"
+        + "FROM dept";
+    final RelNode originalRel;
+    try {
+      final SqlNode parse = planner.parse(sql);
+      final SqlNode validate = planner.validate(parse);
+      originalRel = planner.rel(validate).rel;
+    } catch (Exception e) {
+      throw TestUtil.rethrow(e);
+    }
+
+    final HepProgram hepProgram = HepProgram.builder()
+        .addRuleCollection(
+            ImmutableList.of(
+                // SubQuery program rules
+                CoreRules.FILTER_SUB_QUERY_TO_CORRELATE,
+                CoreRules.PROJECT_SUB_QUERY_TO_CORRELATE,
+                CoreRules.JOIN_SUB_QUERY_TO_CORRELATE))
+        .build();
+    final Program program =
+        Programs.of(hepProgram, true,
+            requireNonNull(cluster.getMetadataProvider()));
+    final RelNode before =
+        program.run(cluster.getPlanner(), originalRel, cluster.traitSet(),
+            Collections.emptyList(), Collections.emptyList());
+    final String planBefore = ""
+        + "LogicalProject(DEPTNO=[$0], EXPR$1=[$3])\n"
+        + "  LogicalCorrelate(correlation=[$cor0], joinType=[left], 
requiredColumns=[{0}])\n"
+        + "    LogicalTableScan(table=[[scott, DEPT]])\n"
+        + "    LogicalAggregate(group=[{}], EXPR$0=[SUM($0)])\n"
+        + "      LogicalProject(CNT=[$1])\n"
+        + "        LogicalAggregate(group=[{0}], groups=[[{0}, {}]], 
CNT=[COUNT()])\n"
+        + "          LogicalProject(DEPTNO=[$7])\n"
+        + "            LogicalFilter(condition=[=($7, $cor0.DEPTNO)])\n"
+        + "              LogicalTableScan(table=[[scott, EMP]])\n";
+    assertThat(before, hasTree(planBefore));
+
+    // Decorrelate without any rules, just "purely" decorrelation algorithm on 
RelDecorrelator
+    final RelNode after =
+        RelDecorrelator.decorrelateQuery(before, builder, 
RuleSets.ofList(Collections.emptyList()),
+            RuleSets.ofList(Collections.emptyList()));
+    final String planAfter = ""
+        + "LogicalProject(DEPTNO=[$0], EXPR$1=[$4])\n"
+        + "  LogicalJoin(condition=[=($0, $3)], joinType=[left])\n"
+        + "    LogicalTableScan(table=[[scott, DEPT]])\n"
+        + "    LogicalAggregate(group=[{0}], EXPR$0=[SUM($1)])\n"
+        + "      LogicalProject(DEPTNO1=[$0], CNT=[CASE(IS NOT NULL($3), $3, 
0)])\n"
+        + "        LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $2)], 
joinType=[left])\n"
+        + "          LogicalProject(DEPTNO=[$0])\n"
+        + "            LogicalTableScan(table=[[scott, DEPT]])\n"
+        + "          LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {1}]], 
CNT=[COUNT()])\n"
+        + "            LogicalProject(DEPTNO=[$7], DEPTNO1=[$7])\n"
+        + "              LogicalFilter(condition=[IS NOT NULL($7)])\n"
+        + "                LogicalTableScan(table=[[scott, EMP]])\n";
+    assertThat(after, hasTree(planAfter));
+  }
 }
diff --git a/core/src/test/resources/sql/sub-query.iq 
b/core/src/test/resources/sql/sub-query.iq
index 9095ff3059..0d649f8558 100644
--- a/core/src/test/resources/sql/sub-query.iq
+++ b/core/src/test/resources/sql/sub-query.iq
@@ -8467,5 +8467,27 @@ ON t2.a = foo.a
 +---+---+---+
 (1 row)
 
+!ok
+
+# [CALCITE-7320] AggregateProjectMergeRule throws AssertionError when Project 
maps multiple grouping keys to the same field
+SELECT deptno,
+       (SELECT SUM(cnt)
+        FROM (
+          SELECT COUNT(*) AS cnt
+          FROM emp
+          WHERE emp.deptno = dept.deptno
+          GROUP BY GROUPING SETS ((deptno), ())
+        ))
+FROM dept;
++--------+--------+
+| DEPTNO | EXPR$1 |
++--------+--------+
+|     10 |      6 |
+|     20 |     10 |
+|     30 |     12 |
+|     40 |      0 |
++--------+--------+
+(4 rows)
+
 !ok
 # End sub-query.iq

Reply via email to