[ 
https://issues.apache.org/jira/browse/CALCITE-7936?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Zhen Chen updated CALCITE-7936:
-------------------------------
    Description: 
The top-down decorrelator can introduce a redundant join when decorrelating 
nested INNER LATERAL queries. If the left input of a nested Correlate does not 
reference any enclosing correlation variables, it does not need to carry the 
outer correlation domain when that domain is already carried by the right input.

For example, with top-down decorrelation enabled:

{{WITH dept (deptno, dname) AS (
  VALUES (10, 'ACCOUNTING'), (20, 'RESEARCH')
),
emp (empno, deptno, ename) AS (
  VALUES (7369, 20, 'SMITH'),
         (7782, 10, 'CLARK'),
         (7499, 30, 'ALLEN')
)
SELECT *
FROM dept,
  (VALUES 'A', 'M') AS v (v)
  CROSS JOIN LATERAL (
    SELECT *
    FROM emp
    WHERE emp.deptno = dept.deptno
      AND POSITION(v.v IN emp.ename) > 0
  ) AS emp;}}

 

The input {{v}} is independent of {{{}dept{}}}, while the LATERAL subquery 
references both {{dept.deptno}} and {{{}v.v{}}}.

Before this optimization, the decorrelated plan contains five joins. The left 
input of the nested join is unnecessarily expanded by a cross join between 
{{v}} and the outer department domain:

 

{{LogicalProject(EXPR$0=[$0], EXPR$1=[$1], V=[$2], EXPR$000=[$4], EXPR$10=[$5], 
EXPR$2=[$6])
  LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $3)], joinType=[inner])
    LogicalValues(tuples=[[\{ 10, 'ACCOUNTING' }, \{ 20, 'RESEARCH  ' }]])
    LogicalJoin(condition=[AND(IS NOT DISTINCT FROM($1, $5), IS NOT DISTINCT 
FROM($0, $6))], joinType=[inner])
      LogicalJoin(condition=[true], joinType=[inner])
        LogicalValues(tuples=[[\{ 'A' }, \{ 'M' }]])
        LogicalProject(EXPR$0=[$0])
          LogicalValues(tuples=[[\{ 10, 'ACCOUNTING' }, \{ 20, 'RESEARCH  ' }]])
      LogicalJoin(condition=[AND(=($1, $3), >(POSITION($4, $2), 0))], 
joinType=[inner])
        LogicalValues(tuples=[[\{ 7369, 20, 'SMITH' }, \{ 7782, 10, 'CLARK' }, 
\{ 7499, 30, 'ALLEN' }]])
        LogicalProject(EXPR$0=[$1], V=[$0])
          LogicalJoin(condition=[true], joinType=[inner])
            LogicalValues(tuples=[[\{ 'A' }, \{ 'M' }]])
            LogicalProject(EXPR$0=[$0])
              LogicalValues(tuples=[[\{ 10, 'ACCOUNTING' }, \{ 20, 'RESEARCH  ' 
}]])}}

 

After this optimization, the plan contains four joins. The left input remains 
the original {{{}VALUES{}}}, and the right input carries the outer correlation 
domain:

 

{{LogicalProject(EXPR$0=[$0], EXPR$1=[$1], V=[$2], EXPR$00=[$3], EXPR$10=[$4], 
EXPR$2=[$5])
  LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $6)], joinType=[inner])
    LogicalValues(tuples=[[\{ 10, 'ACCOUNTING' }, \{ 20, 'RESEARCH  ' }]])
    LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $5)], joinType=[inner])
      LogicalValues(tuples=[[\{ 'A' }, \{ 'M' }]])
      LogicalJoin(condition=[AND(=($1, $3), >(POSITION($4, $2), 0))], 
joinType=[inner])
        LogicalValues(tuples=[[\{ 7369, 20, 'SMITH' }, \{ 7782, 10, 'CLARK' }, 
\{ 7499, 30, 'ALLEN' }]])
        LogicalProject(EXPR$0=[$1], V=[$0])
          LogicalJoin(condition=[true], joinType=[inner])
            LogicalValues(tuples=[[\{ 'A' }, \{ 'M' }]])
            LogicalProject(EXPR$0=[$0])
              LogicalValues(tuples=[[\{ 10, 'ACCOUNTING' }, \{ 20, 'RESEARCH  ' 
}]])}}

 

Both plans were generated from the same SQL using the same planning steps, with 
only the implementation of {{TopDownGeneralDecorrelator}} changed.

The optimization removes the redundant domain join from the left branch and 
adjusts the join conditions and output mappings accordingly. The combined 
domain needed to decorrelate the right input remains present. This produces a 
simpler plan for subsequent optimization.

The scope is limited to nested INNER Correlates whose left input does not 
reference enclosing correlation variables. The rewrite must preserve duplicate 
rows, NULL correlation keys, and output column mappings.

The regression test {{testTopDownGeneralDecorrelateForNestedLateralJoin}} 
checks the new plan through its expected {{planAfter}} snapshot. Execution 
tests in {{lateral.iq}} cover duplicate rows and NULL correlation keys.

This improvement was identified during the investigation of CALCITE-7928 and 
initially implemented in [PR 
#5408|https://github.com/apache/calcite/pull/5408]. It is independent of the 
Volcano planning-time explosion discussed in [PR 
#5410|https://github.com/apache/calcite/pull/5410], which involves 
join-reordering rules and {{PROJECT_MERGE}} generating many duplicate Projects. 
Simplifying the decorrelated plan does not resolve that underlying planner 
issue.

  was:
The top-down decorrelator can generate unnecessarily complex plans for nested 
LATERAL queries. When a nested INNER Correlate has a left input that does not 
reference any enclosing correlation variables, the current implementation still 
joins that input with the outer correlation domain. This introduces a redundant 
cross join and additional correlation columns into the generated plan.

For example, consider the following query with top-down decorrelation enabled:

 
{code:java}
WITH dept (deptno, dname) AS (
  VALUES (10, 'ACCOUNTING'), (20, 'RESEARCH')
),
emp (empno, deptno, ename) AS (
  VALUES (7369, 20, 'SMITH'),
         (7782, 10, 'CLARK'),
         (7499, 30, 'ALLEN')
)
SELECT *
FROM dept,
  (VALUES 'A', 'M') AS v (v)
  CROSS JOIN LATERAL (
    SELECT *
    FROM emp
    WHERE emp.deptno = dept.deptno
      AND POSITION(v.v IN emp.ename) > 0
  ) AS emp;
{code}
 

 

Here, the left input of the nested LATERAL join (`v`) is independent of `dept`, 
while its right input references both `dept` and `v`. The enclosing correlation 
variables can be carried by the right input, allowing the resulting join to use 
an independently decorrelated left input without attaching the outer domain to 
it.

The proposed improvement is to avoid this redundant domain propagation for 
nested INNER Correlates. The combined domain required to decorrelate the right 
input must remain available, and the rewrite must preserve duplicate rows, NULL 
correlation keys, and output column mappings. Other join types and left inputs 
that reference enclosing correlation variables require their existing treatment.

Generating a smaller equivalent plan reduces unnecessary joins and columns 
passed to subsequent optimization, and can reduce the work required to explore 
alternative plans. Regression coverage should verify both the simplified plan 
structure and result correctness.

This opportunity was identified while investigating 
CALCITE-7928([https://issues.apache.org/jira/browse/CALCITE-7928]). As 
explained in [PR #5410|#5410]([https://github.com/apache/calcite/pull/5410]), 
the original excessive planning time involves join-reordering rules and 
`PROJECT_MERGE` generating many duplicate Projects. That PR disables 
`PROJECT_MERGE` for the affected test as a workaround; the underlying planner 
issue remains separate.

This ticket tracks the decorrelator improvement independently of that planner 
issue. [PR #5408|#5408]([https://github.com/apache/calcite/pull/5408]) contains 
an initial implementation.


> Top-down decorrelator should avoid redundant domain joins for nested LATERAL 
> queries
> ------------------------------------------------------------------------------------
>
>                 Key: CALCITE-7936
>                 URL: https://issues.apache.org/jira/browse/CALCITE-7936
>             Project: Calcite
>          Issue Type: Improvement
>          Components: core
>            Reporter: Zhen Chen
>            Priority: Major
>
> The top-down decorrelator can introduce a redundant join when decorrelating 
> nested INNER LATERAL queries. If the left input of a nested Correlate does 
> not reference any enclosing correlation variables, it does not need to carry 
> the outer correlation domain when that domain is already carried by the right 
> input.
> For example, with top-down decorrelation enabled:
> {{WITH dept (deptno, dname) AS (
>   VALUES (10, 'ACCOUNTING'), (20, 'RESEARCH')
> ),
> emp (empno, deptno, ename) AS (
>   VALUES (7369, 20, 'SMITH'),
>          (7782, 10, 'CLARK'),
>          (7499, 30, 'ALLEN')
> )
> SELECT *
> FROM dept,
>   (VALUES 'A', 'M') AS v (v)
>   CROSS JOIN LATERAL (
>     SELECT *
>     FROM emp
>     WHERE emp.deptno = dept.deptno
>       AND POSITION(v.v IN emp.ename) > 0
>   ) AS emp;}}
>  
> The input {{v}} is independent of {{{}dept{}}}, while the LATERAL subquery 
> references both {{dept.deptno}} and {{{}v.v{}}}.
> Before this optimization, the decorrelated plan contains five joins. The left 
> input of the nested join is unnecessarily expanded by a cross join between 
> {{v}} and the outer department domain:
>  
> {{LogicalProject(EXPR$0=[$0], EXPR$1=[$1], V=[$2], EXPR$000=[$4], 
> EXPR$10=[$5], EXPR$2=[$6])
>   LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $3)], joinType=[inner])
>     LogicalValues(tuples=[[\{ 10, 'ACCOUNTING' }, \{ 20, 'RESEARCH  ' }]])
>     LogicalJoin(condition=[AND(IS NOT DISTINCT FROM($1, $5), IS NOT DISTINCT 
> FROM($0, $6))], joinType=[inner])
>       LogicalJoin(condition=[true], joinType=[inner])
>         LogicalValues(tuples=[[\{ 'A' }, \{ 'M' }]])
>         LogicalProject(EXPR$0=[$0])
>           LogicalValues(tuples=[[\{ 10, 'ACCOUNTING' }, \{ 20, 'RESEARCH  ' 
> }]])
>       LogicalJoin(condition=[AND(=($1, $3), >(POSITION($4, $2), 0))], 
> joinType=[inner])
>         LogicalValues(tuples=[[\{ 7369, 20, 'SMITH' }, \{ 7782, 10, 'CLARK' 
> }, \{ 7499, 30, 'ALLEN' }]])
>         LogicalProject(EXPR$0=[$1], V=[$0])
>           LogicalJoin(condition=[true], joinType=[inner])
>             LogicalValues(tuples=[[\{ 'A' }, \{ 'M' }]])
>             LogicalProject(EXPR$0=[$0])
>               LogicalValues(tuples=[[\{ 10, 'ACCOUNTING' }, \{ 20, 'RESEARCH  
> ' }]])}}
>  
> After this optimization, the plan contains four joins. The left input remains 
> the original {{{}VALUES{}}}, and the right input carries the outer 
> correlation domain:
>  
> {{LogicalProject(EXPR$0=[$0], EXPR$1=[$1], V=[$2], EXPR$00=[$3], 
> EXPR$10=[$4], EXPR$2=[$5])
>   LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $6)], joinType=[inner])
>     LogicalValues(tuples=[[\{ 10, 'ACCOUNTING' }, \{ 20, 'RESEARCH  ' }]])
>     LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $5)], joinType=[inner])
>       LogicalValues(tuples=[[\{ 'A' }, \{ 'M' }]])
>       LogicalJoin(condition=[AND(=($1, $3), >(POSITION($4, $2), 0))], 
> joinType=[inner])
>         LogicalValues(tuples=[[\{ 7369, 20, 'SMITH' }, \{ 7782, 10, 'CLARK' 
> }, \{ 7499, 30, 'ALLEN' }]])
>         LogicalProject(EXPR$0=[$1], V=[$0])
>           LogicalJoin(condition=[true], joinType=[inner])
>             LogicalValues(tuples=[[\{ 'A' }, \{ 'M' }]])
>             LogicalProject(EXPR$0=[$0])
>               LogicalValues(tuples=[[\{ 10, 'ACCOUNTING' }, \{ 20, 'RESEARCH  
> ' }]])}}
>  
> Both plans were generated from the same SQL using the same planning steps, 
> with only the implementation of {{TopDownGeneralDecorrelator}} changed.
> The optimization removes the redundant domain join from the left branch and 
> adjusts the join conditions and output mappings accordingly. The combined 
> domain needed to decorrelate the right input remains present. This produces a 
> simpler plan for subsequent optimization.
> The scope is limited to nested INNER Correlates whose left input does not 
> reference enclosing correlation variables. The rewrite must preserve 
> duplicate rows, NULL correlation keys, and output column mappings.
> The regression test {{testTopDownGeneralDecorrelateForNestedLateralJoin}} 
> checks the new plan through its expected {{planAfter}} snapshot. Execution 
> tests in {{lateral.iq}} cover duplicate rows and NULL correlation keys.
> This improvement was identified during the investigation of CALCITE-7928 and 
> initially implemented in [PR 
> #5408|https://github.com/apache/calcite/pull/5408]. It is independent of the 
> Volcano planning-time explosion discussed in [PR 
> #5410|https://github.com/apache/calcite/pull/5410], which involves 
> join-reordering rules and {{PROJECT_MERGE}} generating many duplicate 
> Projects. Simplifying the decorrelated plan does not resolve that underlying 
> planner issue.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to