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 ad6d12ca98f..80f1f95335b 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java @@ -1543,17 +1543,17 @@ private RelNode getCorRel(CorRef corVar) { /** Adds a value generator to satisfy the correlating variables used by * a relational expression, if those variables are not already provided by * its input. */ - private Frame maybeAddValueGenerator(RelNode rel, Frame frame) { - final CorelMap cm1 = new CorelMapBuilder().build(frame.r, rel); + private Frame maybeAddValueGenerator(RelNode rel, Frame inputFrame) { + final CorelMap cm1 = new CorelMapBuilder().build(inputFrame.r, rel); if (!cm1.mapRefRelToCorRef.containsKey(rel)) { - return frame; + return inputFrame; } final Collection needs = cm1.mapRefRelToCorRef.get(rel); - final ImmutableSortedSet haves = frame.corDefOutputs.keySet(); + final ImmutableSortedSet haves = inputFrame.corDefOutputs.keySet(); if (hasAll(needs, haves)) { - return frame; + return inputFrame; } - return decorrelateInputWithValueGenerator(rel, frame); + return decorrelateInputWithValueGenerator(rel, inputFrame); } /** Returns whether all of a collection of {@link CorRef}s are satisfied @@ -1579,13 +1579,13 @@ private static boolean has(Collection corDefs, CorRef corr) { return false; } - private Frame decorrelateInputWithValueGenerator(RelNode rel, Frame frame) { + private Frame decorrelateInputWithValueGenerator(RelNode rel, Frame inputFrame) { // currently only handles one input assert rel.getInputs().size() == 1; - RelNode oldInput = frame.r; + RelNode oldInput = inputFrame.r; final NavigableMap corDefOutputs = - new TreeMap<>(frame.corDefOutputs); + new TreeMap<>(inputFrame.corDefOutputs); final Collection corVarList = cm.mapRefRelToCorRef.get(rel); @@ -1606,8 +1606,7 @@ private Frame decorrelateInputWithValueGenerator(RelNode rel, Frame frame) { if (node instanceof RexInputRef) { map.put(def, ((RexInputRef) node).getIndex()); } else { - map.put(def, - frame.r.getRowType().getFieldCount() + projects.size()); + map.put(def, inputFrame.r.getRowType().getFieldCount() + projects.size()); projects.add((RexNode) node); } } @@ -1615,7 +1614,7 @@ private Frame decorrelateInputWithValueGenerator(RelNode rel, Frame frame) { // If all correlation variables are now satisfied, skip creating a value // generator. if (map.size() == corVarList.size()) { - map.putAll(frame.corDefOutputs); + map.putAll(inputFrame.corDefOutputs); final RelNode r; if (!projects.isEmpty()) { relBuilder.push(oldInput) @@ -1624,17 +1623,40 @@ private Frame decorrelateInputWithValueGenerator(RelNode rel, Frame frame) { } else { r = oldInput; } - return register(rel.getInput(0), r, - frame.oldToNewOutputs, map); + return register(rel.getInput(0), r, inputFrame.oldToNewOutputs, map); } } - int leftInputOutputCount = frame.r.getRowType().getFieldCount(); + return createFrameWithValueGenerator(rel.getInput(0), inputFrame, corVarList, corDefOutputs); + } + + /** + * Creates a new {@link Frame} for the given rel by joining its current + * decorrelated rel with a value generator that produces the required + * correlation variables. + * + *

The value generator is built from {@code corVarList} and joined with + * {@code frame.r} using an INNER join. The provided + * {@code corDefOutputs} map is updated to reflect the positions of all + * correlation definitions in the join output, and the resulting frame is + * registered for {@code rel}. + * + * @param rel target RelNode whose frame is updated to use the join of + * {@code frame.r} and the value generator + * @param frame existing Frame of the rel + * @param corVarList correlated variables that still need to be produced + * @param corDefOutputs mapping from {@link CorDef} to output positions; updated in place + * to include positions in the new join + * @return a new Frame describing {@code rel} after attaching the value generator + */ + private Frame createFrameWithValueGenerator(RelNode rel, Frame frame, + Collection corVarList, NavigableMap corDefOutputs) { + int leftFieldCount = frame.r.getRowType().getFieldCount(); // can directly add positions into corDefOutputs since join // does not change the output ordering from the inputs. final RelNode valueGen = - createValueGenerator(corVarList, leftInputOutputCount, corDefOutputs); + createValueGenerator(corVarList, leftFieldCount, corDefOutputs); requireNonNull(valueGen, "valueGen"); RelNode join = @@ -1647,8 +1669,7 @@ private Frame decorrelateInputWithValueGenerator(RelNode rel, Frame frame) { // Join or Filter does not change the old input ordering. All // input fields from newLeftInput (i.e. the original input to the old // Filter) are in the output and in the same position. - return register(rel.getInput(0), join, frame.oldToNewOutputs, - corDefOutputs); + return register(rel, join, frame.oldToNewOutputs, corDefOutputs); } /** Finds a {@link RexInputRef} that is equivalent to a {@link CorRef}, @@ -1931,8 +1952,19 @@ private static boolean isWidening(RelDataType type, RelDataType type1) { return null; } + Frame newLeftFrame = leftFrame; + boolean joinConditionContainsFieldAccess = RexUtil.containsFieldAccess(rel.getCondition()); + if (joinConditionContainsFieldAccess && isCorVarDefined) { + final CorelMap localCorelMap = new CorelMapBuilder().build(rel); + final List corVarList = new ArrayList<>(localCorelMap.mapRefRelToCorRef.values()); + Collections.sort(corVarList); + + final NavigableMap corDefOutputs = new TreeMap<>(); + newLeftFrame = createFrameWithValueGenerator(oldLeft, leftFrame, corVarList, corDefOutputs); + } + RelNode newJoin = relBuilder - .push(leftFrame.r) + .push(newLeftFrame.r) .push(rightFrame.r) .join(rel.getJoinType(), decorrelateExpr(castNonNull(currentRel), map, cm, rel.getCondition()), @@ -1944,7 +1976,7 @@ private static boolean isWidening(RelDataType type, RelDataType type1) { Map mapOldToNewOutputs = new HashMap<>(); int oldLeftFieldCount = oldLeft.getRowType().getFieldCount(); - int newLeftFieldCount = leftFrame.r.getRowType().getFieldCount(); + int newLeftFieldCount = newLeftFrame.r.getRowType().getFieldCount(); int oldRightFieldCount = oldRight.getRowType().getFieldCount(); //noinspection AssertWithSideEffects @@ -1952,8 +1984,7 @@ private static boolean isWidening(RelDataType type, RelDataType type1) { == oldLeftFieldCount + oldRightFieldCount; // Left input positions are not changed. - mapOldToNewOutputs.putAll(leftFrame.oldToNewOutputs); - + mapOldToNewOutputs.putAll(newLeftFrame.oldToNewOutputs); // Right input positions are shifted by newLeftFieldCount. for (int i = 0; i < oldRightFieldCount; i++) { mapOldToNewOutputs.put(i + oldLeftFieldCount, @@ -1961,8 +1992,7 @@ private static boolean isWidening(RelDataType type, RelDataType type1) { } final NavigableMap corDefOutputs = - new TreeMap<>(leftFrame.corDefOutputs); - + new TreeMap<>(newLeftFrame.corDefOutputs); // Right input positions are shifted by newLeftFieldCount. for (Map.Entry entry : rightFrame.corDefOutputs.entrySet()) { 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 83bf5780cc5..45ef07cc45e 100644 --- a/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java +++ b/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java @@ -1198,4 +1198,78 @@ public static Frameworks.ConfigBuilder config() { + " LogicalTableScan(table=[[scott, EMP]])\n"; assertThat(after, hasTree(planAfter)); } + + /** Test case for [CALCITE-7257] + * Subqueries cannot be decorrelated if join condition contains RexFieldAccess. */ + @Test void testJoinConditionContainsRexFieldAccess() { + 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 E1.* \n" + + "FROM\n" + + " EMP E1\n" + + "WHERE\n" + + " E1.EMPNO = (\n" + + " SELECT D1.DEPTNO FROM DEPT D1\n" + + " WHERE E1.ENAME IN (SELECT B1.ENAME FROM BONUS B1))"; + 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(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7])\n" + + " LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7])\n" + + " LogicalFilter(condition=[=($0, CAST($8):SMALLINT)])\n" + + " LogicalCorrelate(correlation=[$cor0], joinType=[left], requiredColumns=[{1}])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n" + + " LogicalAggregate(group=[{}], agg#0=[SINGLE_VALUE($0)])\n" + + " LogicalProject(DEPTNO=[$0])\n" + + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2])\n" + + " LogicalJoin(condition=[=($cor0.ENAME, $3)], joinType=[inner])\n" + + " LogicalTableScan(table=[[scott, DEPT]])\n" + + " LogicalProject(ENAME=[$0])\n" + + " LogicalTableScan(table=[[scott, BONUS]])\n"; + assertThat(before, hasTree(planBefore)); + + // Decorrelate without any rules, just "purely" decorrelation algorithm on RelDecorrelator + final RelNode after = + RelDecorrelator.decorrelateQuery(before, builder, RuleSets.ofList(Collections.emptyList()), + RuleSets.ofList(Collections.emptyList())); + final String planAfter = "" + + "LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7])\n" + + " LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], ENAME0=[$8], $f1=[CAST($9):TINYINT])\n" + + " LogicalJoin(condition=[AND(=($1, $8), =($0, CAST($9):SMALLINT))], joinType=[inner])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n" + + " LogicalAggregate(group=[{0}], agg#0=[SINGLE_VALUE($1)])\n" + + " LogicalProject(ENAME=[$3], DEPTNO=[$0])\n" + + " LogicalJoin(condition=[=($3, $4)], joinType=[inner])\n" + + " LogicalJoin(condition=[true], joinType=[inner])\n" + + " LogicalTableScan(table=[[scott, DEPT]])\n" + + " LogicalProject(ENAME=[$1])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n" + + " LogicalProject(ENAME=[$0])\n" + + " LogicalTableScan(table=[[scott, BONUS]])\n"; + assertThat(after, hasTree(planAfter)); + } } diff --git a/core/src/test/resources/sql/sub-query.iq b/core/src/test/resources/sql/sub-query.iq index f64feb0a793..6aa385690af 100644 --- a/core/src/test/resources/sql/sub-query.iq +++ b/core/src/test/resources/sql/sub-query.iq @@ -6798,6 +6798,367 @@ FROM t0; !ok +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.ENAME +FROM EMP E1 +WHERE + E1.SAL > (SELECT D1.DEPTNO FROM + DEPT D1 JOIN EMP E2 + ON E1.DEPTNO = E2.DEPTNO); +more than one value in agg SINGLE_VALUE +!error + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.ENAME +FROM EMP E1 +WHERE + E1.SAL > (SELECT D1.DEPTNO FROM + DEPT D1 LEFT JOIN EMP E2 + ON E1.DEPTNO = E2.DEPTNO); +more than one value in agg SINGLE_VALUE +!error + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT + E1.* +FROM + EMP E1 +WHERE + E1.EMPNO = ( + SELECT D1.DEPTNO FROM DEPT D1 + WHERE E1.ENAME IN (SELECT B1.ENAME FROM BONUS B1)); ++-------+-------+-----+-----+----------+-----+------+--------+ +| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO | ++-------+-------+-----+-----+----------+-----+------+--------+ ++-------+-------+-----+-----+----------+-----+------+--------+ +(0 rows) + +!ok + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.ENAME +FROM EMP E1 +WHERE EXISTS ( + SELECT 1 + FROM DEPT D + JOIN BONUS B + ON D.LOC = 'NEW YORK' OR B.JOB = E1.JOB +); ++-------+ +| ENAME | ++-------+ ++-------+ +(0 rows) + +!ok + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.ENAME +FROM EMP E1 +WHERE EXISTS ( + SELECT 1 + FROM DEPT D + LEFT JOIN BONUS B + ON D.LOC = 'NEW YORK' OR B.JOB = E1.JOB +); ++--------+ +| ENAME | ++--------+ +| ADAMS | +| ALLEN | +| BLAKE | +| CLARK | +| FORD | +| JAMES | +| JONES | +| KING | +| MARTIN | +| MILLER | +| SCOTT | +| SMITH | +| TURNER | +| WARD | ++--------+ +(14 rows) + +!ok + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.EMPNO, E1.SAL +FROM EMP E1 +WHERE E1.COMM > ( + SELECT COUNT(*) + FROM BONUS B + JOIN DEPT D + ON B.SAL > E1.SAL AND D.DEPTNO = 10 +); ++-------+---------+ +| EMPNO | SAL | ++-------+---------+ +| 7499 | 1600.00 | +| 7521 | 1250.00 | +| 7654 | 1250.00 | ++-------+---------+ +(3 rows) + +!ok + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.EMPNO, E1.SAL +FROM EMP E1 +WHERE E1.COMM > ( + SELECT COUNT(*) + FROM BONUS B + LEFT JOIN DEPT D + ON B.SAL > E1.SAL AND D.DEPTNO = 10 +); ++-------+---------+ +| EMPNO | SAL | ++-------+---------+ +| 7499 | 1600.00 | +| 7521 | 1250.00 | +| 7654 | 1250.00 | ++-------+---------+ +(3 rows) + +!ok + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.ENAME +FROM EMP E1 +WHERE EXISTS ( + SELECT 1 + FROM DEPT D + JOIN BONUS B + ON D.DNAME = B.ENAME AND B.JOB = E1.JOB + WHERE B.ENAME IS NULL +); ++-------+ +| ENAME | ++-------+ ++-------+ +(0 rows) + +!ok + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.ENAME +FROM EMP E1 +WHERE EXISTS ( + SELECT 1 + FROM DEPT D + LEFT JOIN BONUS B + ON D.DNAME = B.ENAME AND B.JOB = E1.JOB + WHERE B.ENAME IS NULL +); ++--------+ +| ENAME | ++--------+ +| ADAMS | +| ALLEN | +| BLAKE | +| CLARK | +| FORD | +| JAMES | +| JONES | +| KING | +| MARTIN | +| MILLER | +| SCOTT | +| SMITH | +| TURNER | +| WARD | ++--------+ +(14 rows) + +!ok + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.ENAME +FROM EMP E1 +WHERE EXISTS ( + SELECT 1 + FROM DEPT D + LEFT JOIN BONUS B + ON D.DNAME = B.ENAME AND B.JOB = E1.JOB +); ++--------+ +| ENAME | ++--------+ +| ADAMS | +| ALLEN | +| BLAKE | +| CLARK | +| FORD | +| JAMES | +| JONES | +| KING | +| MARTIN | +| MILLER | +| SCOTT | +| SMITH | +| TURNER | +| WARD | ++--------+ +(14 rows) + +!ok + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.ENAME +FROM EMP E1 +WHERE E1.SAL IN ( + SELECT B.SAL + FROM BONUS B + JOIN DEPT D + ON D.DEPTNO = E1.DEPTNO + AND B.SAL = (CASE WHEN E1.COMM IS NULL THEN 0 ELSE E1.COMM END + 100) +); ++-------+ +| ENAME | ++-------+ ++-------+ +(0 rows) + +!ok + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.ENAME +FROM EMP E1 +WHERE E1.SAL IN ( + SELECT B.SAL + FROM BONUS B + LEFT JOIN DEPT D + ON D.DEPTNO = E1.DEPTNO + AND B.SAL = (CASE WHEN E1.COMM IS NULL THEN 0 ELSE E1.COMM END + 100) +); ++-------+ +| ENAME | ++-------+ ++-------+ +(0 rows) + +!ok + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.* +FROM EMP E1 +WHERE NOT EXISTS ( + SELECT 1 + FROM EMP E2 + JOIN BONUS B + ON E2.SAL = E1.SAL AND B.JOB = E1.JOB + WHERE E2.EMPNO <> E1.EMPNO +); ++-------+--------+-----------+------+------------+---------+---------+--------+ +| 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-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +SELECT E1.* +FROM EMP E1 +WHERE NOT EXISTS ( + SELECT 1 + FROM EMP E2 + JOIN BONUS B + ON E2.SAL = E1.SAL AND B.JOB = E1.JOB + WHERE E2.EMPNO <> E1.EMPNO +); ++-------+--------+-----------+------+------------+---------+---------+--------+ +| 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-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +WITH t0(t0a, t0b) AS (VALUES (1, 1), (2, 0)), + t1(t1a, t1b, t1c) AS (VALUES (1, 1, 3)), + t2(t2a, t2b, t2c) AS (VALUES (1, 1, 5), (2, 2, 7)) +SELECT * FROM t0 WHERE t0a < +(SELECT sum(t1c) FROM + (SELECT t1c + FROM t1 JOIN t2 ON (t1a = t0a AND t2b = t1b)) +); ++-----+-----+ +| T0A | T0B | ++-----+-----+ +| 1 | 1 | ++-----+-----+ +(1 row) + +!ok + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +WITH t0(t0a, t0b) AS (VALUES (1, 1), (2, 0)), + t1(t1a, t1b, t1c) AS (VALUES (1, 1, 3)), + t2(t2a, t2b, t2c) AS (VALUES (1, 1, 5), (2, 2, 7)) +SELECT * FROM t0 WHERE t0a < +(SELECT sum(t1c) FROM + (SELECT t1c + FROM t1 JOIN t2 ON (t1a < t0a AND t2b >= t1b)) +); ++-----+-----+ +| T0A | T0B | ++-----+-----+ +| 2 | 0 | ++-----+-----+ +(1 row) + +!ok + +# [CALCITE-7257] Subqueries cannot be decorrelated if join condition contains RexFieldAccess +WITH t0(t0a, t0b) AS (VALUES (1, 1), (2, 0)), + t1(t1a, t1b, t1c) AS (VALUES (1, 1, 3)), + t2(t2a, t2b, t2c) AS (VALUES (1, 1, 5), (2, 2, 7)) +SELECT * FROM t0 WHERE t0a < +(SELECT sum(t1c) FROM + (SELECT t1c + FROM t1 LEFT JOIN t2 ON (t1a = t0a AND t2b = t0b)) +); ++-----+-----+ +| T0A | T0B | ++-----+-----+ +| 1 | 1 | +| 2 | 0 | ++-----+-----+ +(2 rows) + +!ok + # [CALCITE-6942] Support decorrelated for sub-queries with LIMIT 1 and OFFSET select * from emp where exists (