[
https://issues.apache.org/jira/browse/CALCITE-7899?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Weihua Zhang resolved CALCITE-7899.
-----------------------------------
Fix Version/s: 1.43.0
Resolution: Fixed
> SubQueryRemoveRule binds an outer query's correlation variable to the wrong
> input when a Project has several sub-queries
> ------------------------------------------------------------------------------------------------------------------------
>
> Key: CALCITE-7899
> URL: https://issues.apache.org/jira/browse/CALCITE-7899
> Project: Calcite
> Issue Type: Bug
> Reporter: Weihua Zhang
> Assignee: Weihua Zhang
> Priority: Major
> Labels: pull-request-available
> Fix For: 1.43.0
>
>
> If a SELECT list contains a sub-query that is correlated with the current
> query, followed by a sub-query that is correlated with an outer query,
> SubQueryRemoveRule binds the outer query's correlation variable to the wrong
> input. Execution then fails with an AssertionError.
> The following query, on the scott schema, fails:
> {code:sql}
> select d.dname,
> (select max((select count(*) from emp as s where s.mgr = e.empno)
> + (select count(*) from emp as s where s.deptno = d.deptno))
> from emp as e) as c
> from dept as d
> order by d.dname;
> {code}
> {noformat}
> java.lang.AssertionError: Correlation variable $cor1 should be defined
> at
> org.apache.calcite.adapter.enumerable.EnumerableRelImplementor.clearCorrelVariable(EnumerableRelImplementor.java:473)
> at
> org.apache.calcite.adapter.enumerable.EnumerableCorrelate.implement(EnumerableCorrelate.java:142)
> ...
> {noformat}
> The same query with the two inner sub-queries swapped succeeds and returns
> the expected result:
> {noformat}
> +------------+----+
> | DNAME | C |
> +------------+----+
> | ACCOUNTING | 8 |
> | OPERATIONS | 5 |
> | RESEARCH | 10 |
> | SALES | 11 |
> +------------+----+
> {noformat}
> In the plan of the failing query, $cor1 (a row of DEPT) is defined twice. The
> inner Correlate binds it to a row of (EMPNO, EXPR$0), so $cor1.DEPTNO
> resolves to field 0, EMPNO:
> {noformat}
> EnumerableCorrelate(correlation=[$cor1], joinType=[left],
> requiredColumns=[{0}])
> EnumerableCalc(expr#0..2=[{inputs}], proj#0..1=[{exprs}])
> EnumerableTableScan(table=[[scott, DEPT]])
> EnumerableAggregate(group=[{}], EXPR$0=[MAX($0)])
> EnumerableCalc(...)
> EnumerableCorrelate(correlation=[$cor1], joinType=[left],
> requiredColumns=[{0}]) <-- wrong
> EnumerableCalc(..., EMPNO=[$t0], EXPR$0=[$t4])
> EnumerableCorrelate(correlation=[$cor0], joinType=[left],
> requiredColumns=[{0}])
> ...
> EnumerableAggregate(group=[{}], EXPR$0=[COUNT()])
> EnumerableCalc(..., expr#8=[$cor1], expr#9=[$t8.DEPTNO],
> expr#10=[=($t7, $t9)], ...)
> EnumerableTableScan(table=[[scott, EMP]])
> {noformat}
> *Cause*
> SubQueryRemoveRule.matchProject rewrites one sub-query per call and relies on
> the rule firing again for the others. It rebuilds the Project with
> RelBuilder.project(nodes, fieldNames), which gives the new Project an empty
> variablesSet. This is the same problem that CALCITE-7882 fixed for Join.
> For Project, the problem is hidden by a fallback that was added in
> CALCITE-7057: if the Project's variablesSet is empty, the variables used by
> the sub-query are not filtered at all. In the query above:
> # The first call rewrites the sub-query that uses $cor0. The Project's
> variablesSet is \{$cor0\}, so the rewrite is correct. The new Project has an
> empty variablesSet.
> # The second call rewrites the sub-query that uses $cor1. Because the
> variablesSet is now empty, the fallback keeps $cor1, although it is defined
> by an outer Correlate, not by this Project. The rule creates a second
> Correlate on $cor1 over the Project's input.
> When the sub-queries are swapped, the sub-query that uses $cor1 is rewritten
> first. At that point the variablesSet is still \{$cor0\}, so $cor1 is
> filtered out correctly.
> *Proposed fix*
> Rewrite all sub-queries of a Project in a single call, as matchFilter already
> does. Always filter the variables against the original Project's
> variablesSet, and remove the fallback for an empty variablesSet. The Project
> is then built only once, so no sub-queries are left whose correlation
> variables could be lost.
> Related: CALCITE-7882 (same problem in matchJoin), CALCITE-7057 (introduced
> the fallback).
> {code}
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)