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 1e92bbe799 [CALCITE-6942] Support decorrelated for sub-queries with
LIMIT 1 and OFFSET
1e92bbe799 is described below
commit 1e92bbe799252dd47b1bd7133211ad4761e53a06
Author: iwanttobepowerful <[email protected]>
AuthorDate: Tue Dec 16 19:15:11 2025 +0800
[CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
---
.../apache/calcite/sql2rel/RelDecorrelator.java | 127 +++---
.../calcite/sql2rel/RelDecorrelatorTest.java | 199 ++++++++++
.../org/apache/calcite/test/RelOptRulesTest.xml | 22 +-
.../apache/calcite/test/SqlToRelConverterTest.xml | 13 +-
core/src/test/resources/sql/sub-query.iq | 440 ++++++++++++++++++---
site/_docs/history.md | 3 +
6 files changed, 687 insertions(+), 117 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 c5986208d2..ad6d12ca98 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
@@ -74,6 +74,7 @@
import org.apache.calcite.rex.RexSubQuery;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.rex.RexVisitorImpl;
+import org.apache.calcite.rex.RexWindowBounds;
import org.apache.calcite.runtime.PairList;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlExplainFormat;
@@ -575,16 +576,33 @@ protected RexNode removeCorrelationExpr(
}
if (isCorVarDefined && (rel.fetch != null || rel.offset != null)) {
- if (rel.fetch != null
- && rel.offset == null
- && RexLiteral.intValue(rel.fetch) == 1) {
- return decorrelateFetchOneSort(rel, frame);
- }
- // Can not decorrelate if the sort has per-correlate-key attributes like
- // offset or fetch limit, because these attributes scope would change to
- // global after decorrelation. They should take effect within the scope
- // of the correlation key actually.
- return null;
+ if (rel.offset == null && rel.fetch instanceof RexLiteral) {
+ final RexLiteral fetchLiteral = (RexLiteral) requireNonNull(rel.fetch,
"fetch");
+ final BigDecimal fetch = fetchLiteral.getValueAs(BigDecimal.class);
+ assert fetch != null;
+ if (fetch.equals(BigDecimal.ZERO)) {
+ return null;
+ }
+ }
+
+ //
+ // Rewrite logic:
+ //
+ // For correlated Sort with LIMIT/OFFSET:
+ // Special case: if OFFSET is null and FETCH = 1,
+ // we may rewrite as an Aggregate using MIN/MAX.
+ Frame aggFrame = decorrelateSortAsAggregate(rel, frame);
+ if (aggFrame != null) {
+ return aggFrame;
+ }
+
+ // General case: rewrite as
+ // Project(original_fields..., corVars..., rn)
+ // where rn = ROW_NUMBER() OVER (PARTITION BY corVars ORDER BY
sortExprs
+ // ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
+ // Filter(rn > offset, rn <= offset + fetch)
+ // This preserves per-corVar LIMIT/OFFSET semantics.
+ return decorrelateSortWithRowNumber(rel, frame);
}
final RelNode newInput = frame.r;
@@ -1036,30 +1054,7 @@ private static void shiftMapping(Map<Integer, Integer>
mapping, int startIndex,
return null;
}
- protected @Nullable Frame decorrelateFetchOneSort(Sort sort, final Frame
frame) {
- Frame aggFrame = decorrelateSortAsAggregate(sort, frame);
- if (aggFrame != null) {
- return aggFrame;
- }
- //
- // Rewrite logic:
- //
- // If sorted without offset and fetch = 1 (enforced by the caller),
rewrite the sort to be
- // Aggregate(group=(corVar.. , field..))
- // project(first_value(field) over (partition by corVar order by (sort
collation)))
- // input
- //
- // 1. For the original sorted input, apply the FIRST_VALUE window function
to produce
- // the result of sorting with LIMIT 1, and the same as the decorrelate
of aggregate,
- // add correlated variables in partition list to maintain semantic
consistency.
- // 2. To ensure that there is at most one row of output for
- // any combination of correlated variables, distinct for correlated
variables.
- // 3. Since we have partitioned by all correlated variables
- // in the sorted output field window, so for any combination of
correlated variables,
- // all other field values are unique. So the following two are
equivalent:
- // - group by corVar1, covVar2, field1, field2
- // - any_value(fields1), any_value(fields2) group by corVar1, covVar2
- // Here we use the first.
+ protected @Nullable Frame decorrelateSortWithRowNumber(Sort sort, final
Frame frame) {
final Map<Integer, Integer> mapOldToNewOutputs = new HashMap<>();
final NavigableMap<CorDef, Integer> corDefOutputs = new TreeMap<>();
@@ -1091,29 +1086,63 @@ private static void shiftMapping(Map<Integer, Integer>
mapping, int startIndex,
for (RelDataTypeField field : sort.getRowType().getFieldList()) {
final int newIdx =
requireNonNull(frame.oldToNewOutputs.get(field.getIndex()));
-
- RelBuilder.AggCall aggCall =
- relBuilder.aggregateCall(SqlStdOperatorTable.FIRST_VALUE,
- RexInputRef.of(newIdx, fieldList));
-
- // Convert each field from the sorted output to a window function that
partitions by
- // correlated variables, orders by the collation, and return the
first_value.
- RexNode winCall = aggCall.over()
- .orderBy(sortExprs)
- .partitionBy(corVarProjects.leftList())
- .toRex();
mapOldToNewOutputs.put(newProjExprs.size(), newProjExprs.size());
- newProjExprs.add(winCall, field.getName());
+ newProjExprs.add(RexInputRef.of(newIdx, fieldList), field.getName());
}
newProjExprs.addAll(corVarProjects);
- RelNode result = relBuilder.push(frame.r)
- .project(newProjExprs.leftList(), newProjExprs.rightList())
- .distinct().build();
+ relBuilder.push(frame.r);
+
+ RexNode rowNumberCall =
relBuilder.aggregateCall(SqlStdOperatorTable.ROW_NUMBER)
+ .over()
+ .partitionBy(corVarProjects.leftList())
+ .orderBy(sortExprs)
+ .let(c -> c.rowsBetween(RexWindowBounds.UNBOUNDED_PRECEDING,
RexWindowBounds.CURRENT_ROW))
+ .toRex();
+ newProjExprs.add(rowNumberCall, "rn"); // Add the row number column
+ relBuilder.project(newProjExprs.leftList(), newProjExprs.rightList());
+
+ List<RexNode> conditions = new ArrayList<>();
+ if (sort.offset != null) {
+ RexNode greaterThenLowerBound =
+ relBuilder.call(
+ SqlStdOperatorTable.GREATER_THAN,
+ relBuilder.field(newProjExprs.size() - 1),
+ sort.offset);
+ conditions.add(greaterThenLowerBound);
+ }
+ if (sort.fetch != null) {
+ RexNode upperBound = sort.offset == null
+ ? sort.fetch
+ : relBuilder.call(SqlStdOperatorTable.PLUS, sort.offset, sort.fetch);
+ RexNode lessThenOrEqualUpperBound =
+ relBuilder.call(
+ SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
+ relBuilder.field(newProjExprs.size() - 1),
+ upperBound);
+ conditions.add(lessThenOrEqualUpperBound);
+ }
+
+ RelNode result;
+ if (!conditions.isEmpty()) {
+ result = relBuilder.filter(conditions).build();
+ } else {
+ result = relBuilder.build();
+ }
return register(sort, result, mapOldToNewOutputs, corDefOutputs);
}
protected @Nullable Frame decorrelateSortAsAggregate(Sort sort, final Frame
frame) {
+ if (sort.offset != null || sort.fetch == null) {
+ return null;
+ }
+
+ final BigDecimal fetch = ((RexLiteral)
sort.fetch).getValueAs(BigDecimal.class);
+ assert fetch != null;
+ if (!fetch.equals(BigDecimal.ONE)) {
+ return null;
+ }
+
final Map<Integer, Integer> mapOldToNewOutputs = new HashMap<>();
final NavigableMap<CorDef, Integer> corDefOutputs = new TreeMap<>();
if (sort.getCollation().getFieldCollations().size() == 1
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 80a3dda646..83bf5780cc 100644
--- a/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java
+++ b/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java
@@ -999,4 +999,203 @@ public static Frameworks.ConfigBuilder config() {
+ " LogicalTableScan(table=[[scott, DEPT]])\n";
assertThat(decorrelatedNoRules, hasTree(planDecorrelatedNoRules));
}
+
+ @Test void testDecorrelateCorrelatedOrderByLimitToRowNumber() {
+ 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 dname FROM dept WHERE 2000 > (\n"
+ + "SELECT emp.sal FROM emp where dept.deptno = emp.deptno\n"
+ + "ORDER BY year(hiredate), emp.sal limit 1)";
+ 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(DNAME=[$1])\n"
+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2])\n"
+ + " LogicalFilter(condition=[>(2000.00, CAST($3):DECIMAL(12,
2))])\n"
+ + " LogicalCorrelate(correlation=[$cor0], joinType=[left],
requiredColumns=[{0}])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n"
+ + " LogicalProject(SAL=[$0])\n"
+ + " LogicalSort(sort0=[$1], sort1=[$0], dir0=[ASC],
dir1=[ASC], fetch=[1])\n"
+ + " LogicalProject(SAL=[$5], EXPR$1=[EXTRACT(FLAG(YEAR),
$4)])\n"
+ + " LogicalFilter(condition=[=($cor0.DEPTNO, $7)])\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(DNAME=[$1])\n"
+ + " LogicalJoin(condition=[=($0, $4)], joinType=[inner])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n"
+ + " LogicalFilter(condition=[>(2000.00, CAST($0):DECIMAL(12,
2))])\n"
+ + " LogicalProject(SAL=[$0], DEPTNO=[$2])\n"
+ + " LogicalFilter(condition=[<=($3, 1)])\n"
+ + " LogicalProject(SAL=[$5], EXPR$1=[EXTRACT(FLAG(YEAR),
$4)], DEPTNO=[$7], rn=[ROW_NUMBER() OVER (PARTITION BY $7 ORDER BY
EXTRACT(FLAG(YEAR), $4) NULLS LAST, $5 NULLS LAST)])\n"
+ + " LogicalFilter(condition=[IS NOT NULL($7)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ assertThat(after, hasTree(planAfter));
+ }
+
+ @Test void testDecorrelateCorrelatedOrderByLimitToRowNumber2() {
+ 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"
+ + "FROM dept d\n"
+ + "WHERE d.deptno IN (\n"
+ + " SELECT e.deptno\n"
+ + " FROM emp e\n"
+ + " WHERE d.deptno = e.deptno\n"
+ + " LIMIT 10\n"
+ + " OFFSET 2\n"
+ + ")\n"
+ + "LIMIT 2\n"
+ + "OFFSET 1";
+ 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 = ""
+ + "LogicalSort(offset=[1], fetch=[2])\n"
+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2])\n"
+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2])\n"
+ + " LogicalFilter(condition=[=($0, $3)])\n"
+ + " LogicalCorrelate(correlation=[$cor0], joinType=[inner],
requiredColumns=[{0}])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n"
+ + " LogicalAggregate(group=[{0}])\n"
+ + " LogicalSort(offset=[2], fetch=[10])\n"
+ + " LogicalProject(DEPTNO=[$7])\n"
+ + " LogicalFilter(condition=[=($cor0.DEPTNO, $7)])\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 = ""
+ + "LogicalSort(offset=[1], fetch=[2])\n"
+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2])\n"
+ + " LogicalJoin(condition=[=($0, $4)], joinType=[inner])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n"
+ + " LogicalFilter(condition=[=($1, $0)])\n"
+ + " LogicalAggregate(group=[{0, 1}])\n"
+ + " LogicalProject(DEPTNO=[$0], DEPTNO1=[$1])\n"
+ + " LogicalFilter(condition=[AND(>($2, 2), <=($2, +(2,
10)))])\n"
+ + " LogicalProject(DEPTNO=[$7], DEPTNO1=[$7],
rn=[ROW_NUMBER() OVER (PARTITION BY $7)])\n"
+ + " LogicalFilter(condition=[IS NOT NULL($7)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ assertThat(after, hasTree(planAfter));
+ }
+
+ @Test void testDecorrelateCorrelatedOrderByLimitToRowNumber3() {
+ 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 FROM dept WHERE 1000.00 >\n"
+ + "(SELECT sal FROM emp WHERE dept.deptno = emp.deptno\n"
+ + "order by emp.sal limit 1 offset 10)";
+ 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])\n"
+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2])\n"
+ + " LogicalFilter(condition=[>(1000.00, $3)])\n"
+ + " LogicalCorrelate(correlation=[$cor0], joinType=[left],
requiredColumns=[{0}])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n"
+ + " LogicalSort(sort0=[$0], dir0=[ASC], offset=[10],
fetch=[1])\n"
+ + " LogicalProject(SAL=[$5])\n"
+ + " LogicalFilter(condition=[=($cor0.DEPTNO, $7)])\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])\n"
+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2], SAL=[$3],
DEPTNO0=[$4], rn=[CAST($5):BIGINT])\n"
+ + " LogicalJoin(condition=[=($0, $4)], joinType=[inner])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n"
+ + " LogicalFilter(condition=[>(1000.00, $0)])\n"
+ + " LogicalFilter(condition=[AND(>($2, 10), <=($2, +(10,
1)))])\n"
+ + " LogicalProject(SAL=[$5], DEPTNO=[$7], rn=[ROW_NUMBER()
OVER (PARTITION BY $7 ORDER BY $5 NULLS LAST)])\n"
+ + " LogicalFilter(condition=[IS NOT NULL($7)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ assertThat(after, hasTree(planAfter));
+ }
}
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 2eafe44bd8..2b2deaa226 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -2680,12 +2680,12 @@ LogicalProject(NAME=[$1])
<Resource name="planAfter">
<![CDATA[
LogicalProject(NAME=[$1])
- LogicalProject(DEPTNO=[$0], NAME=[$1], SAL=[CAST($2):INTEGER],
DEPTNO0=[CAST($3):INTEGER])
+ LogicalProject(DEPTNO=[$0], NAME=[$1], SAL=[CAST($2):INTEGER],
DEPTNO0=[CAST($3):INTEGER], rn=[CAST($4):BIGINT])
LogicalJoin(condition=[=($0, $3)], joinType=[inner])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
LogicalFilter(condition=[>(10, $0)])
- LogicalAggregate(group=[{0, 1}])
- LogicalProject(SAL=[FIRST_VALUE($5) OVER (PARTITION BY $7 ORDER BY
$5 DESC NULLS FIRST RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED
FOLLOWING)], DEPTNO=[$7])
+ LogicalFilter(condition=[<=($2, 1)])
+ LogicalProject(SAL=[$5], DEPTNO=[$7], rn=[ROW_NUMBER() OVER
(PARTITION BY $7 ORDER BY $5 DESC NULLS FIRST)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -2729,8 +2729,8 @@ LogicalProject(NAME=[$1])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
LogicalFilter(condition=[>(10, $0)])
LogicalProject(SAL=[$0], DEPTNO=[$2])
- LogicalAggregate(group=[{0, 1, 2}])
- LogicalProject(SAL=[FIRST_VALUE($5) OVER (PARTITION BY $7 ORDER BY
EXTRACT(FLAG(YEAR), $4) NULLS LAST, $5 DESC NULLS FIRST RANGE BETWEEN UNBOUNDED
PRECEDING AND UNBOUNDED FOLLOWING)], EXPR$1=[FIRST_VALUE(EXTRACT(FLAG(YEAR),
$4)) OVER (PARTITION BY $7 ORDER BY EXTRACT(FLAG(YEAR), $4) NULLS LAST, $5 DESC
NULLS FIRST RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)],
DEPTNO=[$7])
+ LogicalFilter(condition=[<=($3, 1)])
+ LogicalProject(SAL=[$5], EXPR$1=[EXTRACT(FLAG(YEAR), $4)],
DEPTNO=[$7], rn=[ROW_NUMBER() OVER (PARTITION BY $7 ORDER BY
EXTRACT(FLAG(YEAR), $4) NULLS LAST, $5 DESC NULLS FIRST)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -2843,8 +2843,8 @@ LogicalProject(NAME=[$1], EXPR$1=[$2])
LogicalJoin(condition=[=($0, $3)], joinType=[left])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
LogicalProject(SAL=[$0], DEPTNO=[$2])
- LogicalAggregate(group=[{0, 1, 2}])
- LogicalProject(SAL=[FIRST_VALUE($5) OVER (PARTITION BY $7 ORDER BY
EXTRACT(FLAG(YEAR), $4) NULLS LAST, $5 NULLS LAST RANGE BETWEEN UNBOUNDED
PRECEDING AND UNBOUNDED FOLLOWING)], EXPR$1=[FIRST_VALUE(EXTRACT(FLAG(YEAR),
$4)) OVER (PARTITION BY $7 ORDER BY EXTRACT(FLAG(YEAR), $4) NULLS LAST, $5
NULLS LAST RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)],
DEPTNO=[$7])
+ LogicalFilter(condition=[<=($3, 1)])
+ LogicalProject(SAL=[$5], EXPR$1=[EXTRACT(FLAG(YEAR), $4)],
DEPTNO=[$7], rn=[ROW_NUMBER() OVER (PARTITION BY $7 ORDER BY
EXTRACT(FLAG(YEAR), $4) NULLS LAST, $5 NULLS LAST)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -2884,8 +2884,8 @@ LogicalProject(NAME=[$1], EXPR$1=[$4])
LogicalProject(DEPTNO=[$0], NAME=[$1], DEPTNO0=[$0],
NAME0=[CAST($1):VARCHAR(20) NOT NULL])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
LogicalProject(SAL=[$0], DEPTNO=[$2], ENAME=[$3])
- LogicalAggregate(group=[{0, 1, 2, 3}])
- LogicalProject(SAL=[FIRST_VALUE($5) OVER (PARTITION BY $7, $1 ORDER BY
EXTRACT(FLAG(YEAR), $4) NULLS LAST, $5 NULLS LAST RANGE BETWEEN UNBOUNDED
PRECEDING AND UNBOUNDED FOLLOWING)], EXPR$1=[FIRST_VALUE(EXTRACT(FLAG(YEAR),
$4)) OVER (PARTITION BY $7, $1 ORDER BY EXTRACT(FLAG(YEAR), $4) NULLS LAST, $5
NULLS LAST RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)],
DEPTNO=[$7], ENAME=[$1])
+ LogicalFilter(condition=[<=($4, 1)])
+ LogicalProject(SAL=[$5], EXPR$1=[EXTRACT(FLAG(YEAR), $4)],
DEPTNO=[$7], ENAME=[$1], rn=[ROW_NUMBER() OVER (PARTITION BY $7, $1 ORDER BY
EXTRACT(FLAG(YEAR), $4) NULLS LAST, $5 NULLS LAST)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -8855,8 +8855,8 @@ LogicalProject(A=[$0], TS=[$1], X=[$2], X0=[$4])
LogicalJoin(condition=[AND(=($1, $5), =($2, $6))], joinType=[left])
LogicalProject(EXPR$0=[$0], EXPR$1=[$1], EXPR$00=[CAST($0):VARCHAR(20)
NOT NULL], EXPR$10=[$1])
LogicalValues(tuples=[[{ 'a', 1 }]])
- LogicalAggregate(group=[{0, 1, 2}])
- LogicalProject(X=[FIRST_VALUE($2) OVER (PARTITION BY $3, $4)],
EXPR$1=[$3], EXPR$00=[$4])
+ LogicalFilter(condition=[<=($3, 1)])
+ LogicalProject(X=[$2], EXPR$1=[$3], EXPR$00=[$4], rn=[ROW_NUMBER()
OVER (PARTITION BY $3, $4)])
LogicalJoin(condition=[AND(=($0, $4), <=($1, $3))],
joinType=[inner])
LogicalProject(A=[$1], TS=[$0], X=[$3])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
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 7cdde32314..cbb228b0ed 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -931,14 +931,13 @@ FROM
<Resource name="plan">
<![CDATA[
LogicalProject(DEPTNO=[$0], ENAME=[$1])
- LogicalCorrelate(correlation=[$cor0], joinType=[inner],
requiredColumns=[{0}])
+ LogicalJoin(condition=[=($0, $3)], joinType=[inner])
LogicalAggregate(group=[{0}])
LogicalProject(DEPTNO=[$7])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
- LogicalSort(sort0=[$1], dir0=[DESC], fetch=[3])
- LogicalProject(ENAME=[$1], SAL=[$5])
- LogicalFilter(condition=[=($7, $cor0.DEPTNO)])
- LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+ LogicalFilter(condition=[<=($3, 3)])
+ LogicalProject(ENAME=[$1], SAL=[$5], DEPTNO=[$7], rn=[ROW_NUMBER() OVER
(PARTITION BY $7 ORDER BY $5 DESC NULLS FIRST)])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
</TestCase>
@@ -2196,8 +2195,8 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2],
MGR=[$3], HIREDATE=[$4], SAL=[$
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
LogicalAggregate(group=[{0}], agg#0=[MIN($1)])
LogicalProject(DEPTNO=[$1], $f0=[true])
- LogicalAggregate(group=[{0, 1}])
- LogicalProject(EXPR$0=[FIRST_VALUE(1) OVER (PARTITION BY $0)],
DEPTNO=[$0])
+ LogicalFilter(condition=[<=($2, 1)])
+ LogicalProject(EXPR$0=[1], DEPTNO=[$0], rn=[ROW_NUMBER() OVER
(PARTITION BY $0)])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
]]>
</Resource>
diff --git a/core/src/test/resources/sql/sub-query.iq
b/core/src/test/resources/sql/sub-query.iq
index 47f8f6497d..f64feb0a79 100644
--- a/core/src/test/resources/sql/sub-query.iq
+++ b/core/src/test/resources/sql/sub-query.iq
@@ -654,11 +654,10 @@ SELECT dname FROM "scott".dept WHERE 2000 > (SELECT
emp.sal FROM "scott".emp whe
EnumerableCalc(expr#0..3=[{inputs}], DNAME=[$t3])
EnumerableHashJoin(condition=[=($1, $2)], joinType=[inner])
- EnumerableCalc(expr#0..2=[{inputs}], expr#3=[2000.00:DECIMAL(12, 2)],
expr#4=[CAST($t1):DECIMAL(12, 2)], expr#5=[>($t3, $t4)], SAL=[$t1],
DEPTNO=[$t0], $condition=[$t5])
- EnumerableAggregate(group=[{1, 3, 4}])
- EnumerableWindow(window#0=[window(partition {1} order by [2, 0] range
between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING aggs [FIRST_VALUE($0),
FIRST_VALUE($2)])])
- EnumerableCalc(expr#0..7=[{inputs}], expr#8=[FLAG(YEAR)],
expr#9=[EXTRACT($t8, $t4)], expr#10=[IS NOT NULL($t7)], SAL=[$t5],
DEPTNO=[$t7], $2=[$t9], $condition=[$t10])
- EnumerableTableScan(table=[[scott, EMP]])
+ EnumerableCalc(expr#0..3=[{inputs}], expr#4=[1], expr#5=[<=($t3, $t4)],
expr#6=[2000.00:DECIMAL(12, 2)], expr#7=[CAST($t0):DECIMAL(12, 2)],
expr#8=[>($t6, $t7)], expr#9=[AND($t5, $t8)], proj#0..1=[{exprs}],
$condition=[$t9])
+ EnumerableWindow(window#0=[window(partition {1} order by [2, 0] rows
between UNBOUNDED PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])])
+ EnumerableCalc(expr#0..7=[{inputs}], expr#8=[FLAG(YEAR)],
expr#9=[EXTRACT($t8, $t4)], expr#10=[IS NOT NULL($t7)], SAL=[$t5],
DEPTNO=[$t7], $2=[$t9], $condition=[$t10])
+ EnumerableTableScan(table=[[scott, EMP]])
EnumerableCalc(expr#0..2=[{inputs}], proj#0..1=[{exprs}])
EnumerableTableScan(table=[[scott, DEPT]])
!plan
@@ -678,11 +677,10 @@ SELECT dname FROM "scott".dept WHERE 2000 > (SELECT
emp.sal FROM "scott".emp whe
EnumerableCalc(expr#0..3=[{inputs}], DNAME=[$t3])
EnumerableHashJoin(condition=[=($1, $2)], joinType=[inner])
- EnumerableCalc(expr#0..2=[{inputs}], expr#3=[2000.00:DECIMAL(12, 2)],
expr#4=[CAST($t1):DECIMAL(12, 2)], expr#5=[>($t3, $t4)], SAL=[$t1],
DEPTNO=[$t0], $condition=[$t5])
- EnumerableAggregate(group=[{1, 3, 4}])
- EnumerableWindow(window#0=[window(partition {1} order by [2, 0] range
between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING aggs [FIRST_VALUE($0),
FIRST_VALUE($2)])])
- EnumerableCalc(expr#0..7=[{inputs}], expr#8=[FLAG(YEAR)],
expr#9=[EXTRACT($t8, $t4)], expr#10=[CAST($t3):INTEGER], expr#11=[8000],
expr#12=[>($t10, $t11)], expr#13=[IS NOT NULL($t7)], expr#14=[AND($t12, $t13)],
SAL=[$t5], DEPTNO=[$t7], $2=[$t9], $condition=[$t14])
- EnumerableTableScan(table=[[scott, EMP]])
+ EnumerableCalc(expr#0..3=[{inputs}], expr#4=[1], expr#5=[<=($t3, $t4)],
expr#6=[2000.00:DECIMAL(12, 2)], expr#7=[CAST($t0):DECIMAL(12, 2)],
expr#8=[>($t6, $t7)], expr#9=[AND($t5, $t8)], proj#0..1=[{exprs}],
$condition=[$t9])
+ EnumerableWindow(window#0=[window(partition {1} order by [2, 0] rows
between UNBOUNDED PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])])
+ EnumerableCalc(expr#0..7=[{inputs}], expr#8=[FLAG(YEAR)],
expr#9=[EXTRACT($t8, $t4)], expr#10=[CAST($t3):INTEGER], expr#11=[8000],
expr#12=[>($t10, $t11)], expr#13=[IS NOT NULL($t7)], expr#14=[AND($t12, $t13)],
SAL=[$t5], DEPTNO=[$t7], $2=[$t9], $condition=[$t14])
+ EnumerableTableScan(table=[[scott, EMP]])
EnumerableCalc(expr#0..2=[{inputs}], proj#0..1=[{exprs}])
EnumerableTableScan(table=[[scott, DEPT]])
!plan
@@ -722,16 +720,14 @@ EnumerableCalc(expr#0..3=[{inputs}], DNAME=[$t1],
EXPR$1=[$t3])
# subquery contains null
SELECT dname, (SELECT emp.comm FROM "scott".emp where dept.deptno = emp.deptno
ORDER BY emp.comm desc limit 1) FROM "scott".dept;
-EnumerableCalc(expr#0..3=[{inputs}], DNAME=[$t1], EXPR$1=[$t2])
- EnumerableMergeJoin(condition=[=($0, $3)], joinType=[left])
+EnumerableCalc(expr#0..4=[{inputs}], DNAME=[$t1], EXPR$1=[$t2])
+ EnumerableHashJoin(condition=[=($0, $3)], joinType=[left])
EnumerableCalc(expr#0..2=[{inputs}], proj#0..1=[{exprs}])
EnumerableTableScan(table=[[scott, DEPT]])
- EnumerableSort(sort0=[$1], dir0=[ASC])
- EnumerableCalc(expr#0..1=[{inputs}], w0$o0=[$t1], DEPTNO=[$t0])
- EnumerableAggregate(group=[{7, 8}])
- EnumerableWindow(window#0=[window(partition {7} order by [6 DESC]
range between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING aggs
[FIRST_VALUE($6)])])
- EnumerableCalc(expr#0..7=[{inputs}], expr#8=[IS NOT NULL($t7)],
proj#0..7=[{exprs}], $condition=[$t8])
- EnumerableTableScan(table=[[scott, EMP]])
+ EnumerableCalc(expr#0..8=[{inputs}], expr#9=[1], expr#10=[<=($t8, $t9)],
COMM=[$t6], DEPTNO=[$t7], $2=[$t8], $condition=[$t10])
+ EnumerableWindow(window#0=[window(partition {7} order by [6 DESC] rows
between UNBOUNDED PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])])
+ EnumerableCalc(expr#0..7=[{inputs}], expr#8=[IS NOT NULL($t7)],
proj#0..7=[{exprs}], $condition=[$t8])
+ EnumerableTableScan(table=[[scott, EMP]])
!plan
+------------+--------+
| DNAME | EXPR$1 |
@@ -799,15 +795,13 @@ EnumerableCalc(expr#0..3=[{inputs}], DNAME=[$t1],
EXPR$1=[$t3])
SELECT dname, (SELECT emp.sal FROM "scott".emp where dept.deptno = emp.deptno
ORDER BY year(hiredate), emp.sal limit 1) FROM "scott".dept;
EnumerableCalc(expr#0..3=[{inputs}], DNAME=[$t1], EXPR$1=[$t2])
- EnumerableMergeJoin(condition=[=($0, $3)], joinType=[left])
+ EnumerableHashJoin(condition=[=($0, $3)], joinType=[left])
EnumerableCalc(expr#0..2=[{inputs}], proj#0..1=[{exprs}])
EnumerableTableScan(table=[[scott, DEPT]])
- EnumerableSort(sort0=[$1], dir0=[ASC])
- EnumerableCalc(expr#0..2=[{inputs}], SAL=[$t1], DEPTNO=[$t0])
- EnumerableAggregate(group=[{1, 3, 4}])
- EnumerableWindow(window#0=[window(partition {1} order by [2, 0]
range between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING aggs
[FIRST_VALUE($0), FIRST_VALUE($2)])])
- EnumerableCalc(expr#0..7=[{inputs}], expr#8=[FLAG(YEAR)],
expr#9=[EXTRACT($t8, $t4)], expr#10=[IS NOT NULL($t7)], SAL=[$t5],
DEPTNO=[$t7], $2=[$t9], $condition=[$t10])
- EnumerableTableScan(table=[[scott, EMP]])
+ EnumerableCalc(expr#0..3=[{inputs}], expr#4=[1], expr#5=[<=($t3, $t4)],
proj#0..1=[{exprs}], $condition=[$t5])
+ EnumerableWindow(window#0=[window(partition {1} order by [2, 0] rows
between UNBOUNDED PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])])
+ EnumerableCalc(expr#0..7=[{inputs}], expr#8=[FLAG(YEAR)],
expr#9=[EXTRACT($t8, $t4)], expr#10=[IS NOT NULL($t7)], SAL=[$t5],
DEPTNO=[$t7], $2=[$t9], $condition=[$t10])
+ EnumerableTableScan(table=[[scott, EMP]])
!plan
+------------+---------+
| DNAME | EXPR$1 |
@@ -826,15 +820,13 @@ EnumerableCalc(expr#0..3=[{inputs}], DNAME=[$t1],
EXPR$1=[$t2])
SELECT dname, (SELECT emp.sal FROM "scott".emp where dept.deptno = emp.deptno
and mgr > 8000 ORDER BY year(hiredate), emp.sal limit 1) FROM "scott".dept;
EnumerableCalc(expr#0..3=[{inputs}], DNAME=[$t1], EXPR$1=[$t2])
- EnumerableMergeJoin(condition=[=($0, $3)], joinType=[left])
+ EnumerableHashJoin(condition=[=($0, $3)], joinType=[left])
EnumerableCalc(expr#0..2=[{inputs}], proj#0..1=[{exprs}])
EnumerableTableScan(table=[[scott, DEPT]])
- EnumerableSort(sort0=[$1], dir0=[ASC])
- EnumerableCalc(expr#0..2=[{inputs}], SAL=[$t1], DEPTNO=[$t0])
- EnumerableAggregate(group=[{1, 3, 4}])
- EnumerableWindow(window#0=[window(partition {1} order by [2, 0]
range between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING aggs
[FIRST_VALUE($0), FIRST_VALUE($2)])])
- EnumerableCalc(expr#0..7=[{inputs}], expr#8=[FLAG(YEAR)],
expr#9=[EXTRACT($t8, $t4)], expr#10=[CAST($t3):INTEGER], expr#11=[8000],
expr#12=[>($t10, $t11)], expr#13=[IS NOT NULL($t7)], expr#14=[AND($t12, $t13)],
SAL=[$t5], DEPTNO=[$t7], $2=[$t9], $condition=[$t14])
- EnumerableTableScan(table=[[scott, EMP]])
+ EnumerableCalc(expr#0..3=[{inputs}], expr#4=[1], expr#5=[<=($t3, $t4)],
proj#0..1=[{exprs}], $condition=[$t5])
+ EnumerableWindow(window#0=[window(partition {1} order by [2, 0] rows
between UNBOUNDED PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])])
+ EnumerableCalc(expr#0..7=[{inputs}], expr#8=[FLAG(YEAR)],
expr#9=[EXTRACT($t8, $t4)], expr#10=[CAST($t3):INTEGER], expr#11=[8000],
expr#12=[>($t10, $t11)], expr#13=[IS NOT NULL($t7)], expr#14=[AND($t12, $t13)],
SAL=[$t5], DEPTNO=[$t7], $2=[$t9], $condition=[$t14])
+ EnumerableTableScan(table=[[scott, EMP]])
!plan
+------------+--------+
| DNAME | EXPR$1 |
@@ -2238,14 +2230,14 @@ select sal from "scott".emp e
(0 rows)
!ok
-EnumerableCalc(expr#0..4=[{inputs}], expr#5=[RAND()],
expr#6=[CAST($t5):INTEGER NOT NULL], expr#7=[2], expr#8=[MOD($t6, $t7)],
expr#9=[3], expr#10=[=($t8, $t9)], expr#11=[OR($t10, $t3)], SAL=[$t1],
$condition=[$t11])
+EnumerableCalc(expr#0..5=[{inputs}], expr#6=[RAND()],
expr#7=[CAST($t6):INTEGER NOT NULL], expr#8=[2], expr#9=[MOD($t7, $t8)],
expr#10=[3], expr#11=[=($t9, $t10)], expr#12=[OR($t11, $t3)], SAL=[$t1],
$condition=[$t12])
EnumerableMergeJoin(condition=[=($2, $4)], joinType=[left])
EnumerableSort(sort0=[$2], dir0=[ASC])
EnumerableCalc(expr#0..7=[{inputs}], EMPNO=[$t0], SAL=[$t5],
DEPTNO=[$t7])
EnumerableTableScan(table=[[scott, EMP]])
EnumerableSort(sort0=[$1], dir0=[ASC])
- EnumerableCalc(expr#0..1=[{inputs}], cs=[$t1], DEPTNO=[$t0])
- EnumerableWindow(window#0=[window(partition {0} aggs
[FIRST_VALUE($1)])], constants=[[false]])
+ EnumerableCalc(expr#0..1=[{inputs}], expr#2=[false], expr#3=[1],
expr#4=[<=($t1, $t3)], cs=[$t2], DEPTNO=[$t0], rn=[$t1], $condition=[$t4])
+ EnumerableWindow(window#0=[window(partition {0} rows between UNBOUNDED
PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])], constants=[[false]])
EnumerableCalc(expr#0..2=[{inputs}], DEPTNO=[$t0])
EnumerableTableScan(table=[[scott, DEPT]])
!plan
@@ -2328,14 +2320,14 @@ select sal from "scott".emp e
(0 rows)
!ok
-EnumerableCalc(expr#0..4=[{inputs}], expr#5=[NOT($t3)], expr#6=[IS NOT
NULL($t3)], expr#7=[OR($t5, $t6)], expr#8=[IS NOT TRUE($t7)], SAL=[$t1],
$condition=[$t8])
+EnumerableCalc(expr#0..5=[{inputs}], expr#6=[NOT($t3)], expr#7=[IS NOT
NULL($t3)], expr#8=[OR($t6, $t7)], expr#9=[IS NOT TRUE($t8)], SAL=[$t1],
$condition=[$t9])
EnumerableMergeJoin(condition=[=($2, $4)], joinType=[left])
EnumerableSort(sort0=[$2], dir0=[ASC])
EnumerableCalc(expr#0..7=[{inputs}], EMPNO=[$t0], SAL=[$t5],
DEPTNO=[$t7])
EnumerableTableScan(table=[[scott, EMP]])
EnumerableSort(sort0=[$1], dir0=[ASC])
- EnumerableCalc(expr#0..1=[{inputs}], cs=[$t1], DEPTNO=[$t0])
- EnumerableWindow(window#0=[window(partition {0} aggs
[FIRST_VALUE($1)])], constants=[[false]])
+ EnumerableCalc(expr#0..1=[{inputs}], expr#2=[false], expr#3=[1],
expr#4=[<=($t1, $t3)], cs=[$t2], DEPTNO=[$t0], rn=[$t1], $condition=[$t4])
+ EnumerableWindow(window#0=[window(partition {0} rows between UNBOUNDED
PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])], constants=[[false]])
EnumerableCalc(expr#0..2=[{inputs}], DEPTNO=[$t0])
EnumerableTableScan(table=[[scott, DEPT]])
!plan
@@ -2400,16 +2392,17 @@ select sal from "scott".emp e
(11 rows)
!ok
-EnumerableCalc(expr#0..4=[{inputs}], expr#5=[NOT($t3)], expr#6=[IS NOT
NULL($t3)], expr#7=[OR($t5, $t6)], expr#8=[IS NOT TRUE($t7)], SAL=[$t1],
$condition=[$t8])
+EnumerableCalc(expr#0..5=[{inputs}], expr#6=[NOT($t3)], expr#7=[IS NOT
NULL($t3)], expr#8=[OR($t6, $t7)], expr#9=[IS NOT TRUE($t8)], SAL=[$t1],
$condition=[$t9])
EnumerableMergeJoin(condition=[=($2, $4)], joinType=[left])
EnumerableSort(sort0=[$2], dir0=[ASC])
EnumerableCalc(expr#0..7=[{inputs}], EMPNO=[$t0], SAL=[$t5],
DEPTNO=[$t7])
EnumerableTableScan(table=[[scott, EMP]])
- EnumerableSort(sort0=[$1], dir0=[ASC])
- EnumerableCalc(expr#0..1=[{inputs}], cs=[$t1], DEPTNO1=[$t0])
- EnumerableWindow(window#0=[window(partition {0} aggs
[FIRST_VALUE($1)])], constants=[[true]])
- EnumerableCalc(expr#0..2=[{inputs}], expr#3=[10],
expr#4=[CAST($t0):INTEGER NOT NULL], expr#5=[=($t3, $t4)], DEPTNO=[$t0],
$condition=[$t5])
- EnumerableTableScan(table=[[scott, DEPT]])
+ EnumerableCalc(expr#0..2=[{inputs}], expr#3=[1], expr#4=[<=($t2, $t3)],
proj#0..2=[{exprs}], $condition=[$t4])
+ EnumerableSort(sort0=[$1], dir0=[ASC])
+ EnumerableCalc(expr#0..1=[{inputs}], expr#2=[true], cs=[$t2],
DEPTNO1=[$t0], rn=[$t1])
+ EnumerableWindow(window#0=[window(partition {0} rows between
UNBOUNDED PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])], constants=[[true]])
+ EnumerableCalc(expr#0..2=[{inputs}], expr#3=[10],
expr#4=[CAST($t0):INTEGER NOT NULL], expr#5=[=($t3, $t4)], DEPTNO=[$t0],
$condition=[$t5])
+ EnumerableTableScan(table=[[scott, DEPT]])
!plan
# Test filter literal NOT IN nullable correlated
@@ -2433,16 +2426,17 @@ select sal from "scott".emp e
(11 rows)
!ok
-EnumerableCalc(expr#0..4=[{inputs}], expr#5=[NOT($t3)], expr#6=[IS NOT
NULL($t3)], expr#7=[OR($t5, $t6)], expr#8=[IS NOT TRUE($t7)], SAL=[$t1],
$condition=[$t8])
+EnumerableCalc(expr#0..5=[{inputs}], expr#6=[NOT($t3)], expr#7=[IS NOT
NULL($t3)], expr#8=[OR($t6, $t7)], expr#9=[IS NOT TRUE($t8)], SAL=[$t1],
$condition=[$t9])
EnumerableMergeJoin(condition=[=($2, $4)], joinType=[left])
EnumerableSort(sort0=[$2], dir0=[ASC])
EnumerableCalc(expr#0..7=[{inputs}], EMPNO=[$t0], SAL=[$t5],
DEPTNO=[$t7])
EnumerableTableScan(table=[[scott, EMP]])
- EnumerableSort(sort0=[$1], dir0=[ASC])
- EnumerableCalc(expr#0..2=[{inputs}], cs=[$t2], DEPTNO=[$t0])
- EnumerableWindow(window#0=[window(partition {0} order by [1 DESC]
range between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING aggs
[FIRST_VALUE($1)])])
- EnumerableCalc(expr#0..2=[{inputs}], expr#3=[CAST($t0):INTEGER NOT
NULL], expr#4=[0], expr#5=[>($t3, $t4)], expr#6=[null:TINYINT],
expr#7=[CASE($t5, $t0, $t6)], expr#8=[IS NOT NULL($t7)],
expr#9=[CAST($t7):INTEGER], expr#10=[Sarg[10; NULL AS TRUE]],
expr#11=[SEARCH($t9, $t10)], DEPTNO=[$t0], $1=[$t8], $condition=[$t11])
- EnumerableTableScan(table=[[scott, DEPT]])
+ EnumerableCalc(expr#0..2=[{inputs}], expr#3=[1], expr#4=[<=($t2, $t3)],
proj#0..2=[{exprs}], $condition=[$t4])
+ EnumerableSort(sort0=[$1], dir0=[ASC])
+ EnumerableCalc(expr#0..2=[{inputs}], cs=[$t1], DEPTNO=[$t0], rn=[$t2])
+ EnumerableWindow(window#0=[window(partition {0} order by [1 DESC]
rows between UNBOUNDED PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])])
+ EnumerableCalc(expr#0..2=[{inputs}], expr#3=[CAST($t0):INTEGER NOT
NULL], expr#4=[0], expr#5=[>($t3, $t4)], expr#6=[null:TINYINT],
expr#7=[CASE($t5, $t0, $t6)], expr#8=[IS NOT NULL($t7)],
expr#9=[CAST($t7):INTEGER], expr#10=[Sarg[10; NULL AS TRUE]],
expr#11=[SEARCH($t9, $t10)], DEPTNO=[$t0], $1=[$t8], $condition=[$t11])
+ EnumerableTableScan(table=[[scott, DEPT]])
!plan
# Test filter null IN required is unknown correlated
@@ -6804,4 +6798,350 @@ FROM t0;
!ok
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+select * from emp
+where exists (
+select 1 from dept where emp.deptno=dept.deptno limit 1);
++-------+--------+-----------+------+------------+---------+---------+--------+
+| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
++-------+--------+-----------+------+------------+---------+---------+--------+
+| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | | 20 |
+| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
+| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
+| 7566 | JONES | MANAGER | 7839 | 1981-02-04 | 2975.00 | | 20 |
+| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
+| 7698 | BLAKE | MANAGER | 7839 | 1981-01-05 | 2850.00 | | 30 |
+| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | | 10 |
+| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | | 20 |
+| 7839 | KING | PRESIDENT | | 1981-11-17 | 5000.00 | | 10 |
+| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
+| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | | 20 |
+| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | | 30 |
+| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | | 20 |
+| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | | 10 |
++-------+--------+-----------+------+------------+---------+---------+--------+
+(14 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT deptno, ename
+FROM
+ (SELECT DISTINCT deptno FROM emp) t1,
+ LATERAL (
+ SELECT ename, sal
+ FROM emp
+ WHERE deptno = t1.deptno
+ ORDER BY sal
+ DESC LIMIT 3) s;
++--------+--------+
+| DEPTNO | ENAME |
++--------+--------+
+| 10 | CLARK |
+| 10 | KING |
+| 10 | MILLER |
+| 20 | FORD |
+| 20 | JONES |
+| 20 | SCOTT |
+| 30 | ALLEN |
+| 30 | BLAKE |
+| 30 | TURNER |
++--------+--------+
+(9 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT *
+FROM dept d
+WHERE d.deptno IN (
+ SELECT e.deptno
+ FROM emp e
+ WHERE d.deptno = e.deptno
+)
+LIMIT 2
+OFFSET 1;
++--------+----------+---------+
+| DEPTNO | DNAME | LOC |
++--------+----------+---------+
+| 20 | RESEARCH | DALLAS |
+| 30 | SALES | CHICAGO |
++--------+----------+---------+
+(2 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT *
+FROM dept d
+WHERE d.deptno IN (
+ SELECT e.deptno
+ FROM emp e
+ WHERE d.deptno = e.deptno
+ LIMIT 10
+ OFFSET 2
+)
+LIMIT 2
+OFFSET 1;
++--------+----------+---------+
+| DEPTNO | DNAME | LOC |
++--------+----------+---------+
+| 20 | RESEARCH | DALLAS |
+| 30 | SALES | CHICAGO |
++--------+----------+---------+
+(2 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT *
+FROM dept d
+WHERE d.deptno IN (
+ SELECT e.deptno
+ FROM emp e
+ WHERE d.deptno = e.deptno
+)
+OFFSET 1;
++--------+----------+---------+
+| DEPTNO | DNAME | LOC |
++--------+----------+---------+
+| 20 | RESEARCH | DALLAS |
+| 30 | SALES | CHICAGO |
++--------+----------+---------+
+(2 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT *
+FROM dept d
+WHERE d.deptno IN (
+ SELECT e.deptno
+ FROM emp e
+ WHERE d.deptno = e.deptno
+ OFFSET 2
+)
+OFFSET 1;
++--------+----------+---------+
+| DEPTNO | DNAME | LOC |
++--------+----------+---------+
+| 20 | RESEARCH | DALLAS |
+| 30 | SALES | CHICAGO |
++--------+----------+---------+
+(2 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT *
+FROM dept d
+WHERE EXISTS (
+ SELECT *
+ FROM emp e
+ WHERE d.deptno = e.deptno
+ OFFSET 2
+);
++--------+------------+----------+
+| DEPTNO | DNAME | LOC |
++--------+------------+----------+
+| 10 | ACCOUNTING | NEW YORK |
+| 20 | RESEARCH | DALLAS |
+| 30 | SALES | CHICAGO |
++--------+------------+----------+
+(3 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT *
+FROM dept d
+JOIN LATERAL (
+ SELECT *
+ FROM emp e
+ WHERE e.deptno = d.deptno
+ OFFSET 2
+) s ON TRUE;
++--------+------------+----------+-------+--------+----------+------+------------+---------+---------+---------+
+| DEPTNO | DNAME | LOC | EMPNO | ENAME | JOB | MGR | HIREDATE
| SAL | COMM | DEPTNO0 |
++--------+------------+----------+-------+--------+----------+------+------------+---------+---------+---------+
+| 10 | ACCOUNTING | NEW YORK | 7934 | MILLER | CLERK | 7782 |
1982-01-23 | 1300.00 | | 10 |
+| 20 | RESEARCH | DALLAS | 7788 | SCOTT | ANALYST | 7566 |
1987-04-19 | 3000.00 | | 20 |
+| 20 | RESEARCH | DALLAS | 7876 | ADAMS | CLERK | 7788 |
1987-05-23 | 1100.00 | | 20 |
+| 20 | RESEARCH | DALLAS | 7902 | FORD | ANALYST | 7566 |
1981-12-03 | 3000.00 | | 20 |
+| 30 | SALES | CHICAGO | 7654 | MARTIN | SALESMAN | 7698 |
1981-09-28 | 1250.00 | 1400.00 | 30 |
+| 30 | SALES | CHICAGO | 7698 | BLAKE | MANAGER | 7839 |
1981-01-05 | 2850.00 | | 30 |
+| 30 | SALES | CHICAGO | 7844 | TURNER | SALESMAN | 7698 |
1981-09-08 | 1500.00 | 0.00 | 30 |
+| 30 | SALES | CHICAGO | 7900 | JAMES | CLERK | 7698 |
1981-12-03 | 950.00 | | 30 |
++--------+------------+----------+-------+--------+----------+------+------------+---------+---------+---------+
+(8 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT *
+FROM dept d
+WHERE d.deptno IN (
+ SELECT e.deptno
+ FROM emp e
+ OFFSET 2
+);
++--------+------------+----------+
+| DEPTNO | DNAME | LOC |
++--------+------------+----------+
+| 10 | ACCOUNTING | NEW YORK |
+| 20 | RESEARCH | DALLAS |
+| 30 | SALES | CHICAGO |
++--------+------------+----------+
+(3 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT *
+FROM dept d
+WHERE (
+ SELECT SUM(e.sal)
+ FROM emp e
+ WHERE e.deptno = d.deptno
+ OFFSET 2
+) > 2;
++--------+-------+-----+
+| DEPTNO | DNAME | LOC |
++--------+-------+-----+
++--------+-------+-----+
+(0 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT e.ename
+FROM emp e
+WHERE EXISTS (
+ SELECT MAX(d.deptno) AS a
+ FROM dept d
+ WHERE d.deptno = e.deptno
+ GROUP BY d.loc
+ ORDER BY d.loc
+ OFFSET 1
+);
++-------+
+| ENAME |
++-------+
++-------+
+(0 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT e.ename
+FROM emp e
+JOIN LATERAL (
+ SELECT MAX(d.deptno) AS a
+ FROM dept d
+ WHERE d.deptno = e.deptno
+ GROUP BY d.loc
+ ORDER BY d.loc
+ OFFSET 1
+) s ON TRUE;
++-------+
+| ENAME |
++-------+
++-------+
+(0 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT e.ename
+FROM emp e
+JOIN LATERAL (
+ SELECT MAX(d.deptno) AS a
+ FROM dept d
+ WHERE d.deptno = e.deptno
+ GROUP BY d.loc
+ ORDER BY d.loc
+ LIMIT 2
+ OFFSET 1
+) s ON TRUE;
++-------+
+| ENAME |
++-------+
++-------+
+(0 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT e.ename
+FROM emp e
+WHERE EXISTS (
+ SELECT MAX(d.deptno) AS a
+ FROM dept d
+ WHERE d.deptno = e.deptno
+ GROUP BY d.loc
+ ORDER BY d.loc
+ LIMIT 2
+ OFFSET 1
+);
++-------+
+| ENAME |
++-------+
++-------+
+(0 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT *
+FROM dept d
+JOIN LATERAL (
+ SELECT *
+ FROM emp e
+ WHERE e.deptno = d.deptno
+ LIMIT 1
+ OFFSET 2
+) s ON TRUE;
++--------+------------+----------+-------+--------+----------+------+------------+---------+---------+---------+
+| DEPTNO | DNAME | LOC | EMPNO | ENAME | JOB | MGR | HIREDATE
| SAL | COMM | DEPTNO0 |
++--------+------------+----------+-------+--------+----------+------+------------+---------+---------+---------+
+| 10 | ACCOUNTING | NEW YORK | 7934 | MILLER | CLERK | 7782 |
1982-01-23 | 1300.00 | | 10 |
+| 20 | RESEARCH | DALLAS | 7788 | SCOTT | ANALYST | 7566 |
1987-04-19 | 3000.00 | | 20 |
+| 30 | SALES | CHICAGO | 7654 | MARTIN | SALESMAN | 7698 |
1981-09-28 | 1250.00 | 1400.00 | 30 |
++--------+------------+----------+-------+--------+----------+------+------------+---------+---------+---------+
+(3 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT *
+FROM dept d
+WHERE EXISTS (
+ SELECT *
+ FROM emp e
+ WHERE e.deptno = d.deptno
+ LIMIT 1
+ OFFSET 2
+);
++--------+------------+----------+
+| DEPTNO | DNAME | LOC |
++--------+------------+----------+
+| 10 | ACCOUNTING | NEW YORK |
+| 20 | RESEARCH | DALLAS |
+| 30 | SALES | CHICAGO |
++--------+------------+----------+
+(3 rows)
+
+!ok
+
+# [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET
+SELECT deptno FROM dept WHERE 1000.00 >
+(SELECT sal FROM emp WHERE dept.deptno = emp.deptno order by emp.sal limit 1
offset 10);
++--------+
+| DEPTNO |
++--------+
++--------+
+(0 rows)
+
+!ok
+
# End sub-query.iq
diff --git a/site/_docs/history.md b/site/_docs/history.md
index 399d0bfcec..ea6fd13726 100644
--- a/site/_docs/history.md
+++ b/site/_docs/history.md
@@ -49,6 +49,9 @@ ## <a
href="https://github.com/apache/calcite/releases/tag/calcite-1.42.0">1.42.
#### Breaking Changes
{: #breaking-1-42-0}
+* [<a
href="https://issues.apache.org/jira/browse/CALCITE-6942">CALCITE-6942</a>]
+Rename the method `decorrelateFetchOneSort` to `decorrelateSortWithRowNumber`.
+
#### New features
{: #new-features-1-42-0}