This is an automated email from the ASF dual-hosted git repository.
iwanttobepowerful 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 a838aba04b [CALCITE-7584] RelDecorrelator produces incorrect results
for correlated LATERAL sub-queries with window functions
a838aba04b is described below
commit a838aba04b55c45c624a50f547c2f239d2b665f3
Author: Weihua Zhang <[email protected]>
AuthorDate: Wed Jun 10 11:44:16 2026 +0800
[CALCITE-7584] RelDecorrelator produces incorrect results for correlated
LATERAL sub-queries with window functions
---
.../apache/calcite/sql2rel/RelDecorrelator.java | 80 ++-
.../java/org/apache/calcite/test/JdbcTest.java | 15 +-
.../apache/calcite/test/SqlToRelConverterTest.xml | 2 +-
core/src/test/resources/sql/sub-query.iq | 582 +++++++++++++++++++++
4 files changed, 659 insertions(+), 20 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 14d72f8dfe..f9e7049af2 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
@@ -70,6 +70,7 @@
import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexOver;
import org.apache.calcite.rex.RexShuttle;
import org.apache.calcite.rex.RexSubQuery;
import org.apache.calcite.rex.RexUtil;
@@ -1619,15 +1620,26 @@ private Frame
decorrelateInputWithValueGenerator(RelNode rel, Frame inputFrame)
new TreeMap<>(inputFrame.corDefOutputs);
final Collection<CorRef> corVarList = cm.mapRefRelToCorRef.get(rel);
+ // Track only correlation variables that are not already produced by the
+ // input frame. Existing outputs still need to be carried forward, but they
+ // should not force an extra value generator.
+ final List<CorRef> missingCorVarList = new ArrayList<>();
+ for (CorRef correlation : corVarList) {
+ if (!corDefOutputs.containsKey(correlation.def())) {
+ missingCorVarList.add(correlation);
+ }
+ }
- // Try to populate correlation variables using local fields.
+ // Try to populate missing correlation variables using local fields.
// This means that we do not need a value generator.
if (rel instanceof Filter) {
NavigableMap<CorDef, Integer> map = new TreeMap<>();
List<RexNode> projects = new ArrayList<>();
- for (CorRef correlation : corVarList) {
+ for (CorRef correlation : missingCorVarList) {
final CorDef def = correlation.def();
- if (corDefOutputs.containsKey(def) || map.containsKey(def)) {
+ if (map.containsKey(def)) {
+ // The same correlation definition may be referenced more than once;
+ // one output slot is enough.
continue;
}
try {
@@ -1651,9 +1663,9 @@ private Frame decorrelateInputWithValueGenerator(RelNode
rel, Frame inputFrame)
}
}
}
- // If all correlation variables are now satisfied, skip creating a value
- // generator.
- if (map.size() == corVarList.size()) {
+ // If all missing correlation variables are now satisfied, skip creating
a
+ // value generator.
+ if (map.size() == missingCorVarList.size()) {
map.putAll(inputFrame.corDefOutputs);
final RelNode r;
if (!projects.isEmpty()) {
@@ -1667,7 +1679,10 @@ private Frame decorrelateInputWithValueGenerator(RelNode
rel, Frame inputFrame)
}
}
- return createFrameWithValueGenerator(rel.getInput(0), inputFrame,
corVarList, corDefOutputs);
+ // Fall back to a value generator for correlation variables that could not
+ // be derived from local fields.
+ return createFrameWithValueGenerator(rel.getInput(0), inputFrame,
+ missingCorVarList, corDefOutputs);
}
/**
@@ -2487,6 +2502,57 @@ private DecorrelateRexShuttle(RelNode currentRel,
return fieldAccess;
}
+ /**
+ * Window operators are decorrelated similarly to aggregates. A correlated
+ * window expression is evaluated once per outer-row binding before
+ * decorrelation; after decorrelation, outer references are represented as
+ * ordinary input fields. Therefore, add those fields to the window
partition
+ * keys so that the window function is still evaluated independently for
+ * each outer-row binding.
+ *
+ * <p>Implementation based on: Improving Unnesting of Complex Queries
+ *
+ * <p>3.3 Unnesting Rules
+ *
(https://dl.gi.de/server/api/core/bitstreams/c1918e8c-6a87-4da2-930a-bfed289f2388/content)
+ */
+ @Override public RexNode visitOver(RexOver over) {
+ final RexOver newOver = (RexOver) super.visitOver(over);
+ final List<RexNode> partitionKeys = new
ArrayList<>(newOver.getWindow().partitionKeys);
+ boolean update = newOver != over;
+ int newInputOutputOffset = 0;
+ for (RelNode input : currentRel.getInputs()) {
+ final Frame frame = map.get(input);
+ if (frame == null) {
+ // Inputs without a decorrelation frame keep their original field
layout.
+ newInputOutputOffset += input.getRowType().getFieldCount();
+ continue;
+ }
+ for (Integer newInputPos : frame.corDefOutputs.values()) {
+ // Correlation variables become regular input fields after
decorrelation.
+ // Add them to the window partition keys to preserve the original
+ // per-correlate evaluation scope.
+ final RexInputRef ref =
+ new RexInputRef(newInputOutputOffset + newInputPos,
+ frame.r.getRowType().getFieldList()
+ .get(newInputPos).getType());
+ if (!partitionKeys.contains(ref)) {
+ partitionKeys.add(ref);
+ update = true;
+ }
+ }
+ newInputOutputOffset += frame.r.getRowType().getFieldCount();
+ }
+ if (!update) {
+ return over;
+ }
+ return currentRel.getCluster().getRexBuilder().makeOver(
+ newOver.getParserPosition(), newOver.getType(),
newOver.getAggOperator(),
+ newOver.getOperands(), partitionKeys, newOver.getWindow().orderKeys,
+ newOver.getWindow().getLowerBound(),
newOver.getWindow().getUpperBound(),
+ newOver.getWindow().getExclude(), newOver.getWindow().isRows(),
true, false,
+ newOver.isDistinct(), newOver.ignoreNulls());
+ }
+
@Override public RexNode visitInputRef(RexInputRef inputRef) {
final RexInputRef ref = getNewForOldInputRef(currentRel, map, inputRef);
if (ref.getIndex() == inputRef.getIndex()
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 78098912ed..f90c5525ca 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -2583,18 +2583,9 @@ void checkMultisetQueryWithSingleColumn() {
CalciteAssert.that()
.with(CalciteAssert.Config.REGULAR)
.query(sql)
- .returnsUnordered("name=Bill; deptno=10; M=190",
- "name=Bill; deptno=30; M=190",
- "name=Bill; deptno=40; M=190",
- "name=Eric; deptno=10; M=240",
- "name=Eric; deptno=30; M=240",
- "name=Eric; deptno=40; M=240",
- "name=Sebastian; deptno=10; M=190",
- "name=Sebastian; deptno=30; M=190",
- "name=Sebastian; deptno=40; M=190",
- "name=Theodore; deptno=10; M=190",
- "name=Theodore; deptno=30; M=190",
- "name=Theodore; deptno=40; M=190");
+ .returnsUnordered("name=Bill; deptno=10; M=110",
+ "name=Sebastian; deptno=10; M=160",
+ "name=Theodore; deptno=10; M=120");
}
/** Per SQL std, UNNEST is implicitly LATERAL. */
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 1705493edb..0bf249f3af 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -7050,7 +7050,7 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2],
MGR=[$3], HIREDATE=[$4], SAL=[$
LogicalAggregate(group=[{0}], agg#0=[MIN($1)])
LogicalProject(DEPTNO0=[$2], $f0=[true])
LogicalFilter(condition=[$1])
- LogicalProject(NAME=[$1], QualifyExpression=[=(RANK() OVER
(PARTITION BY $1 ORDER BY $0 DESC), $2)], DEPTNO0=[$2])
+ LogicalProject(NAME=[$1], QualifyExpression=[=(RANK() OVER
(PARTITION BY $1, $2 ORDER BY $0 DESC), $2)], DEPTNO0=[$2])
LogicalJoin(condition=[true], joinType=[inner])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
LogicalAggregate(group=[{0}])
diff --git a/core/src/test/resources/sql/sub-query.iq
b/core/src/test/resources/sql/sub-query.iq
index 7c7e9fb053..3a1fa64784 100644
--- a/core/src/test/resources/sql/sub-query.iq
+++ b/core/src/test/resources/sql/sub-query.iq
@@ -8168,6 +8168,588 @@ SELECT deptno FROM dept WHERE 1000.00 >
!ok
+# [CALCITE-7584] RelDecorrelator produces incorrect results for correlated
LATERAL sub-queries with window functions
+# Correlated LATERAL sub-query with a window expression.
+# The equality predicate between the inner and outer query must remain applied
+# after decorrelation.
+# this was validated using postgres
+SELECT e.ename, d.deptno, d.m
+FROM emp e
+JOIN LATERAL (
+ SELECT d.deptno,
+ MAX(d.deptno + e.empno) OVER (PARTITION BY e.deptno) AS m
+ FROM dept d
+ WHERE e.deptno = d.deptno
+) d ON TRUE
+ORDER BY e.empno;
++--------+--------+------+
+| ENAME | DEPTNO | M |
++--------+--------+------+
+| SMITH | 20 | 7389 |
+| ALLEN | 30 | 7529 |
+| WARD | 30 | 7551 |
+| JONES | 20 | 7586 |
+| MARTIN | 30 | 7684 |
+| BLAKE | 30 | 7728 |
+| CLARK | 10 | 7792 |
+| SCOTT | 20 | 7808 |
+| KING | 10 | 7849 |
+| TURNER | 30 | 7874 |
+| ADAMS | 20 | 7896 |
+| JAMES | 30 | 7930 |
+| FORD | 20 | 7922 |
+| MILLER | 10 | 7944 |
++--------+--------+------+
+(14 rows)
+
+!ok
+
+# The window must also be partitioned by correlation variables
+# that are only referenced by the window expression.
+# this was validated using postgres
+SELECT e.ename, d.deptno, d.rn
+FROM emp e
+JOIN LATERAL (
+ SELECT d.deptno,
+ ROW_NUMBER() OVER (PARTITION BY e.deptno ORDER BY e.empno, d.deptno) AS rn
+ FROM dept d
+ WHERE e.deptno = d.deptno
+) d ON TRUE
+ORDER BY e.empno;
+!if (use_old_decorr) {
++--------+--------+----+
+| ENAME | DEPTNO | RN |
++--------+--------+----+
+| SMITH | 20 | 1 |
+| ALLEN | 30 | 1 |
+| WARD | 30 | 1 |
+| JONES | 20 | 1 |
+| MARTIN | 30 | 1 |
+| BLAKE | 30 | 1 |
+| CLARK | 10 | 1 |
+| SCOTT | 20 | 1 |
+| KING | 10 | 1 |
+| TURNER | 30 | 1 |
+| ADAMS | 20 | 1 |
+| JAMES | 30 | 1 |
+| FORD | 20 | 1 |
+| MILLER | 10 | 1 |
++--------+--------+----+
+(14 rows)
+
+!ok
+!}
+
+# Multiple equality-derived correlation keys must remain available
+# when a window expression also needs an additional correlation key.
+# this was validated using postgres
+SELECT e.ename, s.empno, s.m
+FROM emp e
+JOIN LATERAL (
+ SELECT e2.empno,
+ MAX(e2.empno + e.sal) OVER (PARTITION BY e.deptno, e.job) AS m
+ FROM emp e2
+ WHERE e2.deptno = e.deptno
+ AND e2.job = e.job
+) s ON TRUE
+WHERE e.empno IN (7369, 7499, 7788)
+ORDER BY e.empno, s.empno;
++-------+-------+----------+
+| ENAME | EMPNO | M |
++-------+-------+----------+
+| SMITH | 7369 | 8676.00 |
+| SMITH | 7876 | 8676.00 |
+| ALLEN | 7499 | 9444.00 |
+| ALLEN | 7521 | 9444.00 |
+| ALLEN | 7654 | 9444.00 |
+| ALLEN | 7844 | 9444.00 |
+| SCOTT | 7788 | 10902.00 |
+| SCOTT | 7902 | 10902.00 |
++-------+-------+----------+
+(8 rows)
+
+!ok
+
+# this was validated using postgres
+WITH bonus(ENAME, JOB, SAL, COMM) AS (
+ VALUES ('ALLEN', 'SALESMAN', 1600.00, 300.00),
+ ('WARD', 'SALESMAN', 1250.00, 500.00)
+)
+SELECT *
+FROM BONUS
+WHERE EXISTS(SELECT RANK() OVER (PARTITION BY hiredate ORDER BY sal) AS s
+ FROM EMP, DEPT where EMP.deptno = DEPT.deptno
+ AND DEPT.dname < BONUS.ENAME);
++-------+----------+---------+--------+
+| ENAME | JOB | SAL | COMM |
++-------+----------+---------+--------+
+| ALLEN | SALESMAN | 1600.00 | 300.00 |
+| WARD | SALESMAN | 1250.00 | 500.00 |
++-------+----------+---------+--------+
+(2 rows)
+
+!ok
+
+# this was validated using postgres
+SELECT *
+FROM emp e
+WHERE EXISTS (
+ SELECT 1
+ FROM (
+ SELECT
+ d.dname,
+ d.deptno,
+ RANK() OVER (PARTITION BY d.dname ORDER BY d.deptno DESC) AS rnk
+ FROM dept d
+ ) x
+ WHERE x.rnk = e.deptno
+);
++-------+-------+-----+-----+----------+-----+------+--------+
+| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
++-------+-------+-----+-----+----------+-----+------+--------+
++-------+-------+-----+-----+----------+-----+------+--------+
+(0 rows)
+
+!ok
+
+!if (use_old_decorr) {
+EnumerableCalc(expr#0..8=[{inputs}], proj#0..7=[{exprs}])
+ EnumerableHashJoin(condition=[=($8, $12)], joinType=[semi])
+ EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t7):BIGINT],
proj#0..8=[{exprs}])
+ EnumerableTableScan(table=[[scott, EMP]])
+ EnumerableWindow(window#0=[window(partition {1} order by [0 DESC] aggs
[RANK()])])
+ EnumerableTableScan(table=[[scott, DEPT]])
+!plan
+!}
+
+create view calcite_7584_t1 as
+select * from (values
+ ('val1a', cast(6 as smallint), 8, cast(10 as bigint)),
+ ('val1b', cast(8 as smallint), 16, cast(19 as bigint)),
+ ('val1a', cast(16 as smallint), 12, cast(21 as bigint)),
+ ('val1a', cast(16 as smallint), 12, cast(10 as bigint)),
+ ('val1c', cast(8 as smallint), 16, cast(19 as bigint)),
+ ('val1d', cast(null as smallint), 16, cast(22 as bigint)),
+ ('val1d', cast(null as smallint), 16, cast(19 as bigint)),
+ ('val1e', cast(10 as smallint), cast(null as integer), cast(25 as bigint)),
+ ('val1e', cast(10 as smallint), cast(null as integer), cast(19 as bigint)),
+ ('val1d', cast(10 as smallint), cast(null as integer), cast(12 as bigint)),
+ ('val1a', cast(6 as smallint), 8, cast(10 as bigint)),
+ ('val1e', cast(10 as smallint), cast(null as integer), cast(19 as bigint))
+) as t(t1a, t1b, t1c, t1d);
+(0 rows modified)
+
+!update
+
+create view calcite_7584_t2 as
+select * from (values
+ ('val2a', cast(6 as smallint), 12, cast(14 as bigint)),
+ ('val1b', cast(10 as smallint), 12, cast(19 as bigint)),
+ ('val1b', cast(8 as smallint), 16, cast(119 as bigint)),
+ ('val1c', cast(12 as smallint), 16, cast(219 as bigint)),
+ ('val1b', cast(null as smallint), 16, cast(319 as bigint)),
+ ('val2e', cast(8 as smallint), cast(null as integer), cast(419 as bigint)),
+ ('val1f', cast(19 as smallint), cast(null as integer), cast(519 as bigint)),
+ ('val1b', cast(10 as smallint), 12, cast(19 as bigint)),
+ ('val1b', cast(8 as smallint), 16, cast(19 as bigint)),
+ ('val1c', cast(12 as smallint), 16, cast(19 as bigint)),
+ ('val1e', cast(8 as smallint), cast(null as integer), cast(19 as bigint)),
+ ('val1f', cast(19 as smallint), cast(null as integer), cast(19 as bigint)),
+ ('val1b', cast(null as smallint), 16, cast(19 as bigint))
+) as t(t2a, t2b, t2c, t2d);
+(0 rows modified)
+
+!update
+
+create view calcite_7584_t3 as
+select * from (values
+ ('val3a', cast(6 as smallint), 12, cast(110 as bigint)),
+ ('val3a', cast(6 as smallint), 12, cast(10 as bigint)),
+ ('val1b', cast(10 as smallint), 12, cast(219 as bigint)),
+ ('val1b', cast(10 as smallint), 12, cast(19 as bigint)),
+ ('val1b', cast(8 as smallint), 16, cast(319 as bigint)),
+ ('val1b', cast(8 as smallint), 16, cast(19 as bigint)),
+ ('val3c', cast(17 as smallint), 16, cast(519 as bigint)),
+ ('val3c', cast(17 as smallint), 16, cast(19 as bigint)),
+ ('val1b', cast(null as smallint), 16, cast(419 as bigint)),
+ ('val1b', cast(null as smallint), 16, cast(19 as bigint)),
+ ('val3b', cast(8 as smallint), cast(null as integer), cast(719 as bigint)),
+ ('val3b', cast(8 as smallint), cast(null as integer), cast(19 as bigint))
+) as t(t3a, t3b, t3c, t3d);
+(0 rows modified)
+
+!update
+
+# Window function in a correlated subquery.
+# this was validated using postgres
+SELECT 1
+FROM calcite_7584_t1 t1
+WHERE t1b < (SELECT MAX(tmp.s) FROM (
+ SELECT SUM(t2b) OVER (PARTITION BY t2c ORDER BY t2d) AS s
+ FROM calcite_7584_t2 t2 WHERE t2.t2d = t1.t1d) AS tmp);
++--------+
+| EXPR$0 |
++--------+
+| 1 |
+| 1 |
+| 1 |
+| 1 |
++--------+
+(4 rows)
+
+!ok
+
+!if (use_old_decorr) {
+EnumerableCalc(expr#0..1=[{inputs}], expr#2=[1], EXPR$0=[$t2])
+ EnumerableHashJoin(condition=[AND(=($1, $2), <($0, $3))], joinType=[semi])
+ EnumerableValues(tuples=[[{ 6, 10 }, { 8, 19 }, { 16, 21 }, { 16, 10 }, {
8, 19 }, { null, 22 }, { null, 19 }, { 10, 25 }, { 10, 19 }, { 10, 12 }, { 6,
10 }, { 10, 19 }]])
+ EnumerableSort(sort0=[$0], dir0=[ASC])
+ EnumerableAggregate(group=[{0}], EXPR$0=[MAX($1) FILTER $2])
+ EnumerableCalc(expr#0..4=[{inputs}], expr#5=[0:BIGINT], expr#6=[>($t4,
$t5)], T2D=[$t2], $f2=[$t3], $f3=[$t6])
+ EnumerableWindow(window#0=[window(partition {1, 2} order by [2] aggs
[$SUM0($0), COUNT($0)])])
+ EnumerableValues(tuples=[[{ 6, 12, 14 }, { 10, 12, 19 }, { 8, 16,
119 }, { 12, 16, 219 }, { null, 16, 319 }, { 8, null, 419 }, { 19, null, 519 },
{ 10, 12, 19 }, { 8, 16, 19 }, { 12, 16, 19 }, { 8, null, 19 }, { 19, null, 19
}, { null, 16, 19 }]])
+!plan
+!}
+
+# Same as above but with LIMIT/ORDER BY instead of MAX.
+# this was validated using postgres
+SELECT 1
+FROM calcite_7584_t1 t1
+WHERE t1b < (SELECT SUM(t2b) OVER (PARTITION BY t2c ORDER BY t2d) AS s
+ FROM calcite_7584_t2 t2 WHERE t2.t2d = t1.t1d
+ ORDER BY s DESC
+ LIMIT 1);
++--------+
+| EXPR$0 |
++--------+
+| 1 |
+| 1 |
+| 1 |
+| 1 |
++--------+
+(4 rows)
+
+!ok
+
+!if (use_old_decorr) {
+EnumerableCalc(expr#0..4=[{inputs}], expr#5=[1], EXPR$0=[$t5])
+ EnumerableHashJoin(condition=[AND(=($1, $4), <($3, $0))], joinType=[inner])
+ EnumerableCalc(expr#0..2=[{inputs}], expr#3=[1], expr#4=[<=($t2, $t3)],
proj#0..2=[{exprs}], $condition=[$t4])
+ EnumerableWindow(window#0=[window(partition {1} order by [0 DESC] rows
between UNBOUNDED PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])])
+ EnumerableCalc(expr#0..4=[{inputs}], expr#5=[0:BIGINT], expr#6=[>($t3,
$t5)], expr#7=[null:SMALLINT], expr#8=[CASE($t6, $t4, $t7)], S=[$t8], T2D=[$t2])
+ EnumerableWindow(window#0=[window(partition {1, 2} order by [2] aggs
[COUNT($0), $SUM0($0)])])
+ EnumerableValues(tuples=[[{ 6, 12, 14 }, { 10, 12, 19 }, { 8, 16,
119 }, { 12, 16, 219 }, { null, 16, 319 }, { 8, null, 419 }, { 19, null, 519 },
{ 10, 12, 19 }, { 8, 16, 19 }, { 12, 16, 19 }, { 8, null, 19 }, { 19, null, 19
}, { null, 16, 19 }]])
+ EnumerableValues(tuples=[[{ 6, 10 }, { 8, 19 }, { 16, 21 }, { 16, 10 }, {
8, 19 }, { null, 22 }, { null, 19 }, { 10, 25 }, { 10, 19 }, { 10, 12 }, { 6,
10 }, { 10, 19 }]])
+!plan
+!}
+
+# Window function in a correlated subquery with a non-equi predicate.
+# this was validated using postgres
+SELECT 1
+FROM calcite_7584_t1 t1
+WHERE t1b < (SELECT MAX(tmp.s) FROM (
+ SELECT SUM(t2b) OVER (PARTITION BY t2c ORDER BY t2d) AS s
+ FROM calcite_7584_t2 t2 WHERE t2.t2d <= t1.t1d) AS tmp);
++--------+
+| EXPR$0 |
++--------+
+| 1 |
+| 1 |
+| 1 |
+| 1 |
+| 1 |
+| 1 |
++--------+
+(6 rows)
+
+!ok
+
+!if (use_old_decorr) {
+EnumerableCalc(expr#0..1=[{inputs}], expr#2=[1], EXPR$0=[$t2])
+ EnumerableHashJoin(condition=[AND(=($1, $2), <($0, $3))], joinType=[semi])
+ EnumerableValues(tuples=[[{ 6, 10 }, { 8, 19 }, { 16, 21 }, { 16, 10 }, {
8, 19 }, { null, 22 }, { null, 19 }, { 10, 25 }, { 10, 19 }, { 10, 12 }, { 6,
10 }, { 10, 19 }]])
+ EnumerableSort(sort0=[$0], dir0=[ASC])
+ EnumerableAggregate(group=[{0}], EXPR$0=[MAX($1) FILTER $2])
+ EnumerableCalc(expr#0..5=[{inputs}], expr#6=[0:BIGINT], expr#7=[>($t5,
$t6)], T1D=[$t3], $f2=[$t4], $f3=[$t7])
+ EnumerableWindow(window#0=[window(partition {1, 3} order by [2] aggs
[$SUM0($0), COUNT($0)])])
+ EnumerableNestedLoopJoin(condition=[<=($2, $3)], joinType=[inner])
+ EnumerableValues(tuples=[[{ 6, 12, 14 }, { 10, 12, 19 }, { 8,
16, 119 }, { 12, 16, 219 }, { null, 16, 319 }, { 8, null, 419 }, { 19, null,
519 }, { 10, 12, 19 }, { 8, 16, 19 }, { 12, 16, 19 }, { 8, null, 19 }, { 19,
null, 19 }, { null, 16, 19 }]])
+ EnumerableAggregate(group=[{0}])
+ EnumerableValues(tuples=[[{ 10 }, { 19 }, { 21 }, { 10 }, { 19
}, { 22 }, { 19 }, { 25 }, { 19 }, { 12 }, { 10 }, { 19 }]])
+!plan
+!}
+
+# Same as above but with LIMIT/ORDER BY.
+# this was validated using postgres
+SELECT 1
+FROM calcite_7584_t1 t1
+WHERE t1b < (SELECT SUM(t2b) OVER (PARTITION BY t2c ORDER BY t2d) AS s
+ FROM calcite_7584_t2 t2 WHERE t2.t2d <= t1.t1d
+ ORDER BY s DESC
+ LIMIT 1);
++--------+
+| EXPR$0 |
++--------+
+| 1 |
+| 1 |
+| 1 |
+| 1 |
+| 1 |
+| 1 |
++--------+
+(6 rows)
+
+!ok
+
+!if (use_old_decorr) {
+EnumerableCalc(expr#0..4=[{inputs}], expr#5=[1], EXPR$0=[$t5])
+ EnumerableHashJoin(condition=[AND(=($1, $3), <($0, $2))], joinType=[inner])
+ EnumerableValues(tuples=[[{ 6, 10 }, { 8, 19 }, { 16, 21 }, { 16, 10 }, {
8, 19 }, { null, 22 }, { null, 19 }, { 10, 25 }, { 10, 19 }, { 10, 12 }, { 6,
10 }, { 10, 19 }]])
+ EnumerableCalc(expr#0..2=[{inputs}], expr#3=[1], expr#4=[<=($t2, $t3)],
proj#0..2=[{exprs}], $condition=[$t4])
+ EnumerableWindow(window#0=[window(partition {1} order by [0 DESC] rows
between UNBOUNDED PRECEDING and CURRENT ROW aggs [ROW_NUMBER()])])
+ EnumerableCalc(expr#0..5=[{inputs}], expr#6=[0:BIGINT], expr#7=[>($t4,
$t6)], expr#8=[null:SMALLINT], expr#9=[CASE($t7, $t5, $t8)], S=[$t9], T1D=[$t3])
+ EnumerableWindow(window#0=[window(partition {1, 3} order by [2] aggs
[COUNT($0), $SUM0($0)])])
+ EnumerableNestedLoopJoin(condition=[<=($2, $3)], joinType=[inner])
+ EnumerableValues(tuples=[[{ 6, 12, 14 }, { 10, 12, 19 }, { 8,
16, 119 }, { 12, 16, 219 }, { null, 16, 319 }, { 8, null, 419 }, { 19, null,
519 }, { 10, 12, 19 }, { 8, 16, 19 }, { 12, 16, 19 }, { 8, null, 19 }, { 19,
null, 19 }, { null, 16, 19 }]])
+ EnumerableAggregate(group=[{0}])
+ EnumerableValues(tuples=[[{ 10 }, { 19 }, { 21 }, { 10 }, { 19
}, { 22 }, { 19 }, { 25 }, { 19 }, { 12 }, { 10 }, { 19 }]])
+!plan
+!}
+
+# Window function in a correlated subquery over joins.
+# this was validated using postgres
+SELECT t1b
+FROM calcite_7584_t1 t1
+WHERE t1b > (SELECT MAX(tmp.s) FROM (
+ SELECT RANK() OVER (PARTITION BY t3c, t2b ORDER BY t3c) AS s
+ FROM calcite_7584_t2 t2, calcite_7584_t3 t3
+ WHERE t2.t2c = t3.t3c AND t2.t2a = t1.t1a) AS tmp);
++-----+
+| T1B |
++-----+
+| 8 |
+| 8 |
++-----+
+(2 rows)
+
+!ok
+
+!if (use_old_decorr) {
+EnumerableCalc(expr#0..1=[{inputs}], T1B=[$t1])
+ EnumerableHashJoin(condition=[AND(=($0, $2), >(CAST($1):BIGINT, $3))],
joinType=[semi])
+ EnumerableValues(tuples=[[{ 'val1a', 6 }, { 'val1b', 8 }, { 'val1a', 16 },
{ 'val1a', 16 }, { 'val1c', 8 }, { 'val1d', null }, { 'val1d', null }, {
'val1e', 10 }, { 'val1e', 10 }, { 'val1d', 10 }, { 'val1a', 6 }, { 'val1e', 10
}]])
+ EnumerableSort(sort0=[$0], dir0=[ASC])
+ EnumerableAggregate(group=[{0}], EXPR$0=[MAX($4)])
+ EnumerableWindow(window#0=[window(partition {0, 1, 3} order by [3]
aggs [RANK()])])
+ EnumerableMergeJoin(condition=[=($2, $3)], joinType=[inner])
+ EnumerableSort(sort0=[$2], dir0=[ASC])
+ EnumerableValues(tuples=[[{ 'val2a', 6, 12 }, { 'val1b', 10, 12
}, { 'val1b', 8, 16 }, { 'val1c', 12, 16 }, { 'val1b', null, 16 }, { 'val2e',
8, null }, { 'val1f', 19, null }, { 'val1b', 10, 12 }, { 'val1b', 8, 16 }, {
'val1c', 12, 16 }, { 'val1e', 8, null }, { 'val1f', 19, null }, { 'val1b',
null, 16 }]])
+ EnumerableValues(tuples=[[{ 12 }, { 12 }, { 12 }, { 12 }, { 16 },
{ 16 }, { 16 }, { 16 }, { 16 }, { 16 }, { null }, { null }]])
+!plan
+!}
+
+# Window function in a correlated subquery over aggregation.
+# this was validated using postgres
+SELECT t1b
+FROM calcite_7584_t1 t1
+WHERE t1b > (SELECT MAX(tmp.s) FROM (
+ SELECT RANK() OVER (PARTITION BY t3c, t3d ORDER BY t3c) AS s
+ FROM (SELECT t3b, t3c, MAX(t3d) AS t3d
+ FROM calcite_7584_t3 t3 GROUP BY t3b, t3c) AS g) AS tmp)
+ORDER BY t1b;
++-----+
+| T1B |
++-----+
+| 6 |
+| 6 |
+| 8 |
+| 8 |
+| 10 |
+| 10 |
+| 10 |
+| 10 |
+| 16 |
+| 16 |
++-----+
+(10 rows)
+
+!ok
+
+!if (use_old_decorr) {
+EnumerableSort(sort0=[$0], dir0=[ASC])
+ EnumerableCalc(expr#0..1=[{inputs}], T1B=[$t0])
+ EnumerableNestedLoopJoin(condition=[>(CAST($0):BIGINT, $1)],
joinType=[inner])
+ EnumerableValues(tuples=[[{ 6 }, { 8 }, { 16 }, { 16 }, { 8 }, { null },
{ null }, { 10 }, { 10 }, { 10 }, { 6 }, { 10 }]])
+ EnumerableAggregate(group=[{}], EXPR$0=[MAX($3)])
+ EnumerableWindow(window#0=[window(partition {1, 2} order by [1] aggs
[RANK()])])
+ EnumerableAggregate(group=[{0, 1}], T3D=[MAX($2)])
+ EnumerableValues(tuples=[[{ 6, 12, 110 }, { 6, 12, 10 }, { 10, 12,
219 }, { 10, 12, 19 }, { 8, 16, 319 }, { 8, 16, 19 }, { 17, 16, 519 }, { 17,
16, 19 }, { null, 16, 419 }, { null, 16, 19 }, { 8, null, 719 }, { 8, null, 19
}]])
+!plan
+!}
+
+# this was validated using postgres
+SELECT 1
+FROM calcite_7584_t1 t1
+WHERE t1b = (SELECT MAX(tmp.s) FROM (
+ SELECT SUM(t2c) OVER (PARTITION BY t2c ORDER BY t1.t1d + t2d) AS s
+ FROM calcite_7584_t2 t2) AS tmp);
+!if (use_old_decorr) {
++--------+
+| EXPR$0 |
++--------+
++--------+
+(0 rows)
+
+!ok
+
+EnumerableCalc(expr#0..1=[{inputs}], expr#2=[1], EXPR$0=[$t2])
+ EnumerableHashJoin(condition=[AND(=($1, $2), =(CAST($0):INTEGER, $3))],
joinType=[semi])
+ EnumerableValues(tuples=[[{ 6, 10 }, { 8, 19 }, { 16, 21 }, { 16, 10 }, {
8, 19 }, { null, 22 }, { null, 19 }, { 10, 25 }, { 10, 19 }, { 10, 12 }, { 6,
10 }, { 10, 19 }]])
+ EnumerableSort(sort0=[$0], dir0=[ASC])
+ EnumerableAggregate(group=[{0}], EXPR$0=[MAX($1) FILTER $2])
+ EnumerableCalc(expr#0..4=[{inputs}], expr#5=[0:BIGINT], expr#6=[>($t4,
$t5)], T1D=[$t1], $f2=[$t3], $f3=[$t6])
+ EnumerableWindow(window#0=[window(partition {0, 1} order by [2] aggs
[$SUM0($0), COUNT($0)])])
+ EnumerableCalc(expr#0..2=[{inputs}], expr#3=[+($t2, $t1)],
T2C=[$t0], T1D=[$t2], $2=[$t3])
+ EnumerableNestedLoopJoin(condition=[true], joinType=[inner])
+ EnumerableValues(tuples=[[{ 12, 14 }, { 12, 19 }, { 16, 119 },
{ 16, 219 }, { 16, 319 }, { null, 419 }, { null, 519 }, { 12, 19 }, { 16, 19 },
{ 16, 19 }, { null, 19 }, { null, 19 }, { 16, 19 }]])
+ EnumerableAggregate(group=[{0}])
+ EnumerableValues(tuples=[[{ 10 }, { 19 }, { 21 }, { 10 }, {
19 }, { 22 }, { 19 }, { 25 }, { 19 }, { 12 }, { 10 }, { 19 }]])
+!plan
+!}
+
+# [CALCITE-7584] Correlated subquery with RANK() window function and IN
predicate
+# Test case for window function in correlated subquery with IN predicate
+# this was validated using postgres
+SELECT t1a
+FROM calcite_7584_t1 t1
+WHERE t1b IN (SELECT RANK() OVER (PARTITION BY t3c ORDER BY t2b) AS s
+ FROM calcite_7584_t2 t2, calcite_7584_t3 t3
+ WHERE t2.t2c = t3.t3c AND t2.t2a < t1.t1a);
++-----+
+| T1A |
++-----+
++-----+
+(0 rows)
+
+!ok
+
+!if (use_old_decorr) {
+EnumerableCalc(expr#0..2=[{inputs}], T1A=[$t0])
+ EnumerableHashJoin(condition=[AND(=($0, $4), =($2, $3))], joinType=[semi])
+ EnumerableCalc(expr#0..1=[{inputs}], expr#2=[CAST($t1):BIGINT],
proj#0..2=[{exprs}])
+ EnumerableValues(tuples=[[{ 'val1a', 6 }, { 'val1b', 8 }, { 'val1a', 16
}, { 'val1a', 16 }, { 'val1c', 8 }, { 'val1d', null }, { 'val1d', null }, {
'val1e', 10 }, { 'val1e', 10 }, { 'val1d', 10 }, { 'val1a', 6 }, { 'val1e', 10
}]])
+ EnumerableCalc(expr#0..1=[{inputs}], S=[$t1], T1A=[$t0])
+ EnumerableAggregate(group=[{4, 5}])
+ EnumerableWindow(window#0=[window(partition {0, 4} order by [2] aggs
[RANK()])])
+ EnumerableHashJoin(condition=[=($0, $3)], joinType=[inner])
+ EnumerableValues(tuples=[[{ 12 }, { 12 }, { 12 }, { 12 }, { 16 },
{ 16 }, { 16 }, { 16 }, { 16 }, { 16 }, { null }, { null }]])
+ EnumerableNestedLoopJoin(condition=[<($0, $3)], joinType=[inner])
+ EnumerableValues(tuples=[[{ 'val2a', 6, 12 }, { 'val1b', 10, 12
}, { 'val1b', 8, 16 }, { 'val1c', 12, 16 }, { 'val1b', null, 16 }, { 'val2e',
8, null }, { 'val1f', 19, null }, { 'val1b', 10, 12 }, { 'val1b', 8, 16 }, {
'val1c', 12, 16 }, { 'val1e', 8, null }, { 'val1f', 19, null }, { 'val1b',
null, 16 }]])
+ EnumerableAggregate(group=[{0}])
+ EnumerableValues(tuples=[[{ 'val1a' }, { 'val1b' }, { 'val1a'
}, { 'val1a' }, { 'val1c' }, { 'val1d' }, { 'val1d' }, { 'val1e' }, { 'val1e'
}, { 'val1d' }, { 'val1a' }, { 'val1e' }]])
+!plan
+!}
+
+# [CALCITE-7584] LATERAL subquery with window function
+# this was validated using postgres
+SELECT *
+FROM calcite_7584_t1 t1 JOIN LATERAL
+ (SELECT SUM(t2.t2b) OVER (ORDER BY t2.t2b) AS window_sum
+ FROM calcite_7584_t2 t2
+ WHERE t2.t2b >= t1.t1b) AS t2_window ON TRUE
+order by 1,2,3,4,5;
++-------+-----+-----+-----+------------+
+| T1A | T1B | T1C | T1D | WINDOW_SUM |
++-------+-----+-----+-----+------------+
+| val1a | 6 | 8 | 10 | 6 |
+| val1a | 6 | 8 | 10 | 6 |
+| val1a | 6 | 8 | 10 | 38 |
+| val1a | 6 | 8 | 10 | 38 |
+| val1a | 6 | 8 | 10 | 38 |
+| val1a | 6 | 8 | 10 | 38 |
+| val1a | 6 | 8 | 10 | 38 |
+| val1a | 6 | 8 | 10 | 38 |
+| val1a | 6 | 8 | 10 | 38 |
+| val1a | 6 | 8 | 10 | 38 |
+| val1a | 6 | 8 | 10 | 58 |
+| val1a | 6 | 8 | 10 | 58 |
+| val1a | 6 | 8 | 10 | 58 |
+| val1a | 6 | 8 | 10 | 58 |
+| val1a | 6 | 8 | 10 | 82 |
+| val1a | 6 | 8 | 10 | 82 |
+| val1a | 6 | 8 | 10 | 82 |
+| val1a | 6 | 8 | 10 | 82 |
+| val1a | 6 | 8 | 10 | 120 |
+| val1a | 6 | 8 | 10 | 120 |
+| val1a | 6 | 8 | 10 | 120 |
+| val1a | 6 | 8 | 10 | 120 |
+| val1a | 16 | 12 | 10 | 38 |
+| val1a | 16 | 12 | 10 | 38 |
+| val1a | 16 | 12 | 21 | 38 |
+| val1a | 16 | 12 | 21 | 38 |
+| val1b | 8 | 16 | 19 | 32 |
+| val1b | 8 | 16 | 19 | 32 |
+| val1b | 8 | 16 | 19 | 32 |
+| val1b | 8 | 16 | 19 | 32 |
+| val1b | 8 | 16 | 19 | 52 |
+| val1b | 8 | 16 | 19 | 52 |
+| val1b | 8 | 16 | 19 | 76 |
+| val1b | 8 | 16 | 19 | 76 |
+| val1b | 8 | 16 | 19 | 114 |
+| val1b | 8 | 16 | 19 | 114 |
+| val1c | 8 | 16 | 19 | 32 |
+| val1c | 8 | 16 | 19 | 32 |
+| val1c | 8 | 16 | 19 | 32 |
+| val1c | 8 | 16 | 19 | 32 |
+| val1c | 8 | 16 | 19 | 52 |
+| val1c | 8 | 16 | 19 | 52 |
+| val1c | 8 | 16 | 19 | 76 |
+| val1c | 8 | 16 | 19 | 76 |
+| val1c | 8 | 16 | 19 | 114 |
+| val1c | 8 | 16 | 19 | 114 |
+| val1d | 10 | | 12 | 20 |
+| val1d | 10 | | 12 | 20 |
+| val1d | 10 | | 12 | 44 |
+| val1d | 10 | | 12 | 44 |
+| val1d | 10 | | 12 | 82 |
+| val1d | 10 | | 12 | 82 |
+| val1e | 10 | | 19 | 20 |
+| val1e | 10 | | 19 | 20 |
+| val1e | 10 | | 19 | 20 |
+| val1e | 10 | | 19 | 20 |
+| val1e | 10 | | 19 | 44 |
+| val1e | 10 | | 19 | 44 |
+| val1e | 10 | | 19 | 44 |
+| val1e | 10 | | 19 | 44 |
+| val1e | 10 | | 19 | 82 |
+| val1e | 10 | | 19 | 82 |
+| val1e | 10 | | 19 | 82 |
+| val1e | 10 | | 19 | 82 |
+| val1e | 10 | | 25 | 20 |
+| val1e | 10 | | 25 | 20 |
+| val1e | 10 | | 25 | 44 |
+| val1e | 10 | | 25 | 44 |
+| val1e | 10 | | 25 | 82 |
+| val1e | 10 | | 25 | 82 |
++-------+-----+-----+-----+------------+
+(70 rows)
+
+!ok
+
+!if (use_old_decorr) {
+EnumerableSort(sort0=[$0], sort1=[$1], sort2=[$2], sort3=[$3], sort4=[$4],
dir0=[ASC], dir1=[ASC], dir2=[ASC], dir3=[ASC], dir4=[ASC])
+ EnumerableCalc(expr#0..5=[{inputs}], proj#0..4=[{exprs}])
+ EnumerableHashJoin(condition=[=($1, $5)], joinType=[inner])
+ EnumerableValues(tuples=[[{ 'val1a', 6, 8, 10 }, { 'val1b', 8, 16, 19 },
{ 'val1a', 16, 12, 21 }, { 'val1a', 16, 12, 10 }, { 'val1c', 8, 16, 19 }, {
'val1d', null, 16, 22 }, { 'val1d', null, 16, 19 }, { 'val1e', 10, null, 25 },
{ 'val1e', 10, null, 19 }, { 'val1d', 10, null, 12 }, { 'val1a', 6, 8, 10 }, {
'val1e', 10, null, 19 }]])
+ EnumerableCalc(expr#0..3=[{inputs}], expr#4=[0:BIGINT], expr#5=[>($t2,
$t4)], expr#6=[null:SMALLINT], expr#7=[CASE($t5, $t3, $t6)], WINDOW_SUM=[$t7],
T1B=[$t1])
+ EnumerableWindow(window#0=[window(partition {1} order by [0] aggs
[COUNT($0), $SUM0($0)])])
+ EnumerableNestedLoopJoin(condition=[>=($0, $1)], joinType=[inner])
+ EnumerableValues(tuples=[[{ 6 }, { 10 }, { 8 }, { 12 }, { null },
{ 8 }, { 19 }, { 10 }, { 8 }, { 12 }, { 8 }, { 19 }, { null }]])
+ EnumerableAggregate(group=[{0}])
+ EnumerableValues(tuples=[[{ 6 }, { 8 }, { 16 }, { 16 }, { 8 }, {
null }, { null }, { 10 }, { 10 }, { 10 }, { 6 }, { 10 }]])
+!plan
+!}
+
# [CALCITE-7274] RexFieldAccess has wrong index when use trim unused fields
!set trimfields true