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

silun 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 270ba649cf [CALCITE-7382] The TopDownGeneralDecorrelator returns an 
error result when a subquery contains a LIMIT 1
270ba649cf is described below

commit 270ba649cfa80228d3f4f6635a08ab06de504399
Author: Silun Dong <[email protected]>
AuthorDate: Sun Jan 18 17:07:53 2026 +0800

    [CALCITE-7382] The TopDownGeneralDecorrelator returns an error result when 
a subquery contains a LIMIT 1
---
 .../sql2rel/TopDownGeneralDecorrelator.java        | 85 +++++++++++-----------
 core/src/test/resources/sql/new-decorr.iq          | 26 +++++++
 2 files changed, 68 insertions(+), 43 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/sql2rel/TopDownGeneralDecorrelator.java 
b/core/src/main/java/org/apache/calcite/sql2rel/TopDownGeneralDecorrelator.java
index 10b9a411ff..72401e3426 100644
--- 
a/core/src/main/java/org/apache/calcite/sql2rel/TopDownGeneralDecorrelator.java
+++ 
b/core/src/main/java/org/apache/calcite/sql2rel/TopDownGeneralDecorrelator.java
@@ -43,6 +43,7 @@
 import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.rex.RexWindow;
+import org.apache.calcite.rex.RexWindowBounds;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.fun.SqlCountAggFunction;
@@ -598,50 +599,48 @@ public RelNode unnestInternal(Sort sort, boolean 
allowEmptyOutputFromRewrite) {
     RelCollation shiftCollation = sort.getCollation().apply(targetMapping);
     builder.push(newInput);
 
-    if (!sort.collation.getFieldCollations().isEmpty()
-        && (sort.offset != null || sort.fetch != null)) {
-      // the Sort with ORDER BY and LIMIT or OFFSET have to be changed during 
rewriting because
-      // now the limit has to be enforced per value of the outer bindings 
instead of globally.
-      // It can be rewritten using ROW_NUMBER() window function and filtering 
on it,
-      // see section 4.4 in paper Improving Unnesting of Complex Queries
-      List<RexNode> partitionKeys = new ArrayList<>();
-      for (CorDef corDef : corDefs) {
-        int partitionKeyIndex = 
requireNonNull(inputInfo.corDefOutputs.get(corDef));
-        partitionKeys.add(builder.field(partitionKeyIndex));
-      }
-      RexNode rowNumber = builder.aggregateCall(SqlStdOperatorTable.ROW_NUMBER)
-          .over()
-          .partitionBy(partitionKeys)
-          .orderBy(builder.fields(shiftCollation))
-          .toRex();
-      List<RexNode> projectsWithRowNumber = new ArrayList<>(builder.fields());
-      projectsWithRowNumber.add(rowNumber);
-      builder.project(projectsWithRowNumber);
-
-      List<RexNode> conditions = new ArrayList<>();
-      if (sort.offset != null) {
-        RexNode greaterThenLowerBound =
-            builder.call(
-                SqlStdOperatorTable.GREATER_THAN,
-                builder.field(projectsWithRowNumber.size() - 1),
-                sort.offset);
-        conditions.add(greaterThenLowerBound);
-      }
-      if (sort.fetch != null) {
-        RexNode upperBound = sort.offset == null
-            ? sort.fetch
-            : builder.call(SqlStdOperatorTable.PLUS, sort.offset, sort.fetch);
-        RexNode lessThenOrEqualUpperBound =
-            builder.call(
-                SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
-                builder.field(projectsWithRowNumber.size() - 1),
-                upperBound);
-        conditions.add(lessThenOrEqualUpperBound);
-      }
-      builder.filter(conditions);
-    } else {
-      builder.sortLimit(sort.offset, sort.fetch, 
builder.fields(shiftCollation));
+    // the Sort have to be changed during rewriting because now the 
order/limit/offset has to be
+    // enforced per value of the outer bindings instead of globally. It can be 
rewritten using
+    // ROW_NUMBER() window function and filtering on it, see section 4.4 in 
paper
+    // Improving Unnesting of Complex Queries
+    List<RexNode> partitionKeys = new ArrayList<>();
+    for (CorDef corDef : corDefs) {
+      int partitionKeyIndex = 
requireNonNull(inputInfo.corDefOutputs.get(corDef));
+      partitionKeys.add(builder.field(partitionKeyIndex));
+    }
+    RexNode rowNumber = builder.aggregateCall(SqlStdOperatorTable.ROW_NUMBER)
+        .over()
+        .partitionBy(partitionKeys)
+        .orderBy(builder.fields(shiftCollation))
+        .rowsFrom(RexWindowBounds.UNBOUNDED_PRECEDING)
+        .rowsTo(RexWindowBounds.CURRENT_ROW)
+        .toRex();
+    List<RexNode> projectsWithRowNumber = new ArrayList<>(builder.fields());
+    projectsWithRowNumber.add(rowNumber);
+    builder.project(projectsWithRowNumber);
+
+    List<RexNode> conditions = new ArrayList<>();
+    if (sort.offset != null) {
+      RexNode greaterThenLowerBound =
+          builder.call(
+              SqlStdOperatorTable.GREATER_THAN,
+              builder.field(projectsWithRowNumber.size() - 1),
+              sort.offset);
+      conditions.add(greaterThenLowerBound);
     }
+    if (sort.fetch != null) {
+      RexNode upperBound = sort.offset == null
+          ? sort.fetch
+          : builder.call(SqlStdOperatorTable.PLUS, sort.offset, sort.fetch);
+      RexNode lessThenOrEqualUpperBound =
+          builder.call(
+              SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
+              builder.field(projectsWithRowNumber.size() - 1),
+              upperBound);
+      conditions.add(lessThenOrEqualUpperBound);
+    }
+    builder.filter(conditions);
+
     RelNode newSort = builder.build();
     UnnestedQuery unnestedQuery
         = new UnnestedQuery(sort, newSort, inputInfo.corDefOutputs, 
inputInfo.oldToNewOutputs);
diff --git a/core/src/test/resources/sql/new-decorr.iq 
b/core/src/test/resources/sql/new-decorr.iq
index d329c160ed..4e804687da 100644
--- a/core/src/test/resources/sql/new-decorr.iq
+++ b/core/src/test/resources/sql/new-decorr.iq
@@ -98,4 +98,30 @@ EnumerableSort(sort0=[$0], dir0=[ASC])
 !plan
 !}
 
+# [CALCITE-7382] The TopDownGeneralDecorrelator returns an error result when a 
subquery contains a LIMIT 1
+# This case comes from sub-query.iq [CALCITE-6652]
+!use scott
+SELECT dname, (SELECT emp.comm FROM "scott".emp WHERE dept.deptno = emp.deptno 
ORDER BY emp.comm LIMIT 1) FROM "scott".dept;
++------------+--------+
+| DNAME      | EXPR$1 |
++------------+--------+
+| ACCOUNTING |        |
+| OPERATIONS |        |
+| RESEARCH   |        |
+| SALES      |   0.00 |
++------------+--------+
+(4 rows)
+
+!ok
+
+SELECT dname, (SELECT empno FROM emp WHERE dept.deptno = emp.deptno LIMIT 1) 
FROM dept WHERE deptno = 10;
++------------+--------+
+| DNAME      | EXPR$1 |
++------------+--------+
+| ACCOUNTING |   7782 |
++------------+--------+
+(1 row)
+
+!ok
+
 # End new-decorr.iq

Reply via email to