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 3b416debe7 [CALCITE-7297] The result is incorrect when the GROUP BY
key in a subquery is a RexFieldAccess
3b416debe7 is described below
commit 3b416debe7127a72afe36a716ec9d62ec56c7319
Author: iwanttobepowerful <[email protected]>
AuthorDate: Mon Dec 8 22:36:52 2025 +0800
[CALCITE-7297] The result is incorrect when the GROUP BY key in a subquery
is a RexFieldAccess
---
.../apache/calcite/sql2rel/RelDecorrelator.java | 12 +---
.../calcite/sql2rel/RelDecorrelatorTest.java | 69 ++++++++++++++++++++
.../apache/calcite/test/SqlToRelConverterTest.xml | 14 ++--
core/src/test/resources/sql/sub-query.iq | 75 ++++++++++++++++++++++
4 files changed, 151 insertions(+), 19 deletions(-)
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 e45edff7e5..db647da89c 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
@@ -1217,9 +1217,7 @@ private static void shiftMapping(Map<Integer, Integer>
mapping, int startIndex,
// If this Project has correlated reference, create value generator
// and produce the correlated variables in the new output.
- if (cm.mapRefRelToCorRef.containsKey(rel)) {
- frame = decorrelateInputWithValueGenerator(rel, frame);
- }
+ frame = maybeAddValueGenerator(rel, frame);
// Project projects the original expressions
final Map<Integer, Integer> mapOldToNewOutputs = new HashMap<>();
@@ -1609,13 +1607,7 @@ private static boolean isWidening(RelDataType type,
RelDataType type1) {
// If this Filter has correlated reference, create value generator
// and produce the correlated variables in the new output.
- if (false) {
- if (cm.mapRefRelToCorRef.containsKey(rel)) {
- frame = decorrelateInputWithValueGenerator(rel, frame);
- }
- } else {
- frame = maybeAddValueGenerator(rel, frame);
- }
+ frame = maybeAddValueGenerator(rel, frame);
final CorelMap cm2 = new CorelMapBuilder().build(rel);
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 f6504fdef9..b92606be5b 100644
--- a/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java
+++ b/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java
@@ -356,6 +356,75 @@ public static Frameworks.ConfigBuilder config() {
assertThat(after, hasTree(planAfter));
}
+ /** Test case for <a
href="https://issues.apache.org/jira/browse/CALCITE-7297">[CALCITE-7297]
+ * The result is incorrect when the GROUP BY key in a subquery is a
RexFieldAccess</a>. */
+ @Test void testSkipsRedundantValueGenerator() {
+ 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 *,\n"
+ + " (SELECT COUNT(*)\n"
+ + " FROM \n"
+ + " (\n"
+ + " SELECT empno, ename, job\n"
+ + " FROM emp\n"
+ + " WHERE emp.deptno = dept.deptno) AS sub\n"
+ + " GROUP BY deptno) AS num_dept_groups\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 = ""
+ + "LogicalCorrelate(correlation=[$cor1], joinType=[left],
requiredColumns=[{0}])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n"
+ + " LogicalAggregate(group=[{}], agg#0=[SINGLE_VALUE($0)])\n"
+ + " LogicalProject(EXPR$0=[$1])\n"
+ + " LogicalAggregate(group=[{0}], EXPR$0=[COUNT()])\n"
+ + " LogicalProject($f0=[$cor1.DEPTNO])\n"
+ + " LogicalFilter(condition=[=($7, $cor1.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()));
+ // Verify plan
+ final String planAfter = ""
+ + "LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2], $f1=[$4])\n"
+ + " LogicalJoin(condition=[=($0, $3)], joinType=[left])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n"
+ + " LogicalAggregate(group=[{0}], agg#0=[SINGLE_VALUE($1)])\n"
+ + " LogicalProject(DEPTNO=[$1], EXPR$0=[$2])\n"
+ + " LogicalAggregate(group=[{0, 1}], EXPR$0=[COUNT()])\n"
+ + " LogicalProject($f0=[$7], DEPTNO=[$7])\n"
+ + " LogicalFilter(condition=[IS NOT NULL($7)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ assertThat(after, hasTree(planAfter));
+ }
+
/**
* Test case for
* <a
href="https://issues.apache.org/jira/browse/CALCITE-6468">[CALCITE-6468]
RelDecorrelator
diff --git
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index a1c7f101b3..7cdde32314 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -1601,15 +1601,11 @@ LogicalProject(C=[$0], D=[$1], C0=[$4], F=[$5])
LogicalJoin(condition=[AND(=($0, $6), =($3, $7))], joinType=[inner])
LogicalProject(C=[$0], D=[$1], C0=[$0], $f3=[+($0, $1)])
LogicalValues(tuples=[[{ 4, 5 }]])
- LogicalProject(C=[$3], F=[*($0, $3)], C0=[$3], $f3=[$2])
- LogicalJoin(condition=[true], joinType=[inner])
- LogicalJoin(condition=[=($2, *($0, $1))], joinType=[inner])
- LogicalValues(tuples=[[{ 2 }]])
- LogicalAggregate(group=[{0, 1}])
- LogicalProject(C=[$0], $f3=[+($0, $1)])
- LogicalValues(tuples=[[{ 4, 5 }]])
- LogicalAggregate(group=[{0}])
- LogicalProject(C=[$0])
+ LogicalProject(C=[$1], F=[*($0, $1)], C2=[$1], $f3=[$2])
+ LogicalJoin(condition=[=($2, *($0, $1))], joinType=[inner])
+ LogicalValues(tuples=[[{ 2 }]])
+ LogicalAggregate(group=[{0, 1}])
+ LogicalProject(C=[$0], $f3=[+($0, $1)])
LogicalValues(tuples=[[{ 4, 5 }]])
]]>
</Resource>
diff --git a/core/src/test/resources/sql/sub-query.iq
b/core/src/test/resources/sql/sub-query.iq
index 59fda9fca2..a12b087ebb 100644
--- a/core/src/test/resources/sql/sub-query.iq
+++ b/core/src/test/resources/sql/sub-query.iq
@@ -5544,4 +5544,79 @@ WHERE EXISTS
!ok
+# [CALCITE-7297] The result is incorrect when the GROUP BY key in a subquery
is a RexFieldAccess
+SELECT *,
+ (SELECT COUNT(*)
+ FROM
+ (
+ SELECT empno, ename, job
+ FROM emp
+ WHERE emp.deptno = dept.deptno) AS sub
+ GROUP BY deptno) AS num_dept_groups
+FROM dept;
++--------+------------+----------+-----------------+
+| DEPTNO | DNAME | LOC | NUM_DEPT_GROUPS |
++--------+------------+----------+-----------------+
+| 10 | ACCOUNTING | NEW YORK | 3 |
+| 20 | RESEARCH | DALLAS | 5 |
+| 30 | SALES | CHICAGO | 6 |
+| 40 | OPERATIONS | BOSTON | |
++--------+------------+----------+-----------------+
+(4 rows)
+
+!ok
+
+# [CALCITE-7297] The result is incorrect when the GROUP BY key in a subquery
is a RexFieldAccess
+SELECT *,
+ (SELECT COUNT(*)
+ FROM
+ (
+ SELECT empno, ename, job, comm
+ FROM emp
+ WHERE emp.deptno = dept.deptno
+ ORDER BY empno LIMIT 1
+ ) AS sub
+ GROUP BY sub.comm
+ ) AS num_dept_groups
+FROM dept;
++--------+------------+----------+-----------------+
+| DEPTNO | DNAME | LOC | NUM_DEPT_GROUPS |
++--------+------------+----------+-----------------+
+| 10 | ACCOUNTING | NEW YORK | 1 |
+| 20 | RESEARCH | DALLAS | 1 |
+| 30 | SALES | CHICAGO | 1 |
+| 40 | OPERATIONS | BOSTON | |
++--------+------------+----------+-----------------+
+(4 rows)
+
+!ok
+
+# [CALCITE-7297] The result is incorrect when the GROUP BY key in a subquery
is a RexFieldAccess
+select * from (values (4, 5)) as t(c, d)
+cross join lateral
+(select c, a*c as f
+from (values 2) as s(a)
+where c+d=a*c);
++---+---+----+---+
+| C | D | C0 | F |
++---+---+----+---+
++---+---+----+---+
+(0 rows)
+
+!ok
+
+# [CALCITE-7297] The result is incorrect when the GROUP BY key in a subquery
is a RexFieldAccess
+select * from (values (2,2), (2,2)) as t(c,d)
+cross join lateral
+(select c,a*c as f from (values 2) as s(a)
+where c+d=a*c);
++---+---+----+---+
+| C | D | C0 | F |
++---+---+----+---+
+| 2 | 2 | 2 | 4 |
+| 2 | 2 | 2 | 4 |
++---+---+----+---+
+(2 rows)
+
+!ok
# End sub-query.iq