@@ -208,6 +208,101 @@ public static Frameworks.ConfigBuilder config() {
208208 assertThat (after , hasTree (planAfter ));
209209 }
210210
211+ /** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-7057">[CALCITE-7057]
212+ * NPE when decorrelating query containing nested correlated subqueries</a>. */
213+ @ Test void test7057 () {
214+ final FrameworkConfig frameworkConfig = config ().build ();
215+ final RelBuilder builder = RelBuilder .create (frameworkConfig );
216+ final RelOptCluster cluster = builder .getCluster ();
217+ final Planner planner = Frameworks .getPlanner (frameworkConfig );
218+ final String sql = ""
219+ + "select\n "
220+ + " (select ename || ' from dept '\n "
221+ + " || (select dname from dept where deptno = emp.deptno and emp.empno = empnos.empno)\n "
222+ + " from emp\n "
223+ + " ) as ename_from_dept\n "
224+ + "from (values (7369), (7499)) as empnos(empno) order by 1" ;
225+ final RelNode originalRel ;
226+ try {
227+ final SqlNode parse = planner .parse (sql );
228+ final SqlNode validate = planner .validate (parse );
229+ originalRel = planner .rel (validate ).rel ;
230+ } catch (Exception e ) {
231+ throw TestUtil .rethrow (e );
232+ }
233+
234+ final HepProgram hepProgram = HepProgram .builder ()
235+ .addRuleCollection (
236+ ImmutableList .of (
237+ // SubQuery program rules
238+ CoreRules .FILTER_SUB_QUERY_TO_CORRELATE ,
239+ CoreRules .PROJECT_SUB_QUERY_TO_CORRELATE ,
240+ CoreRules .JOIN_SUB_QUERY_TO_CORRELATE ))
241+ .build ();
242+ final Program program =
243+ Programs .of (hepProgram , true ,
244+ requireNonNull (cluster .getMetadataProvider ()));
245+ final RelNode before =
246+ program .run (cluster .getPlanner (), originalRel , cluster .traitSet (),
247+ Collections .emptyList (), Collections .emptyList ());
248+
249+ // before fix:
250+ //
251+ // LogicalSort(sort0=[$0], dir0=[ASC])
252+ // LogicalProject(ENAME_FROM_DEPT=[$1])
253+ // LogicalCorrelate(correlation=[$cor2], joinType=[left], requiredColumns=[{0}])
254+ // LogicalValues(tuples=[[{ 7369 }, { 7499 }]])
255+ // LogicalAggregate(group=[{}], agg#0=[SINGLE_VALUE($0)])
256+ // LogicalProject(EXPR$0=[||(||($1, ' from dept '), $8)])
257+ // LogicalJoin(condition=[true], joinType=[left], variablesSet=[[$cor0, $cor2]])
258+ // LogicalTableScan(table=[[scott, EMP]])
259+ // LogicalAggregate(group=[{}], agg#0=[SINGLE_VALUE($0)])
260+ // LogicalProject(DNAME=[$1])
261+ // LogicalFilter(condition=[AND(=($0, $cor0.DEPTNO),
262+ // =(CAST($cor0.EMPNO):INTEGER NOT NULL, $cor2.EMPNO))])
263+ // LogicalTableScan(table=[[scott, DEPT]])
264+ //
265+ final String planBefore = ""
266+ + "LogicalSort(sort0=[$0], dir0=[ASC])\n "
267+ + " LogicalProject(ENAME_FROM_DEPT=[$1])\n "
268+ + " LogicalCorrelate(correlation=[$cor2], joinType=[left], requiredColumns=[{0}])\n "
269+ + " LogicalValues(tuples=[[{ 7369 }, { 7499 }]])\n "
270+ + " LogicalAggregate(group=[{}], agg#0=[SINGLE_VALUE($0)])\n "
271+ + " LogicalProject(EXPR$0=[||(||($1, ' from dept '), $8)])\n "
272+ + " LogicalCorrelate(correlation=[$cor0], joinType=[left], requiredColumns=[{0, 7}])\n "
273+ + " LogicalTableScan(table=[[scott, EMP]])\n "
274+ + " LogicalAggregate(group=[{}], agg#0=[SINGLE_VALUE($0)])\n "
275+ + " LogicalProject(DNAME=[$1])\n "
276+ + " LogicalFilter(condition=[AND(=($0, $cor0.DEPTNO), =(CAST($cor0.EMPNO):INTEGER NOT NULL, $cor2.EMPNO))])\n "
277+ + " LogicalTableScan(table=[[scott, DEPT]])\n " ;
278+ assertThat (before , hasTree (planBefore ));
279+
280+ // Decorrelate without any rules, just "purely" decorrelation algorithm on RelDecorrelator
281+ final RelNode after =
282+ RelDecorrelator .decorrelateQuery (before , builder , RuleSets .ofList (Collections .emptyList ()),
283+ RuleSets .ofList (Collections .emptyList ()));
284+ final String planAfter = ""
285+ + "LogicalSort(sort0=[$0], dir0=[ASC])\n "
286+ + " LogicalProject(ENAME_FROM_DEPT=[$2])\n "
287+ + " LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $1)], joinType=[left])\n "
288+ + " LogicalValues(tuples=[[{ 7369 }, { 7499 }]])\n "
289+ + " LogicalAggregate(group=[{0}], agg#0=[SINGLE_VALUE($1)])\n "
290+ + " LogicalProject(EMPNO1=[$12], EXPR$0=[||(||($1, ' from dept '), $13)])\n "
291+ + " LogicalJoin(condition=[AND(=($7, $10), =($9, $11))], joinType=[left])\n "
292+ + " LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], DEPTNO0=[$7], EMPNO0=[CAST($0):INTEGER NOT NULL])\n "
293+ + " LogicalTableScan(table=[[scott, EMP]])\n "
294+ + " LogicalAggregate(group=[{0, 1, 2}], agg#0=[SINGLE_VALUE($3)])\n "
295+ + " LogicalProject(DEPTNO0=[$3], EMPNO0=[$4], EMPNO=[$5], DNAME=[$1])\n "
296+ + " LogicalJoin(condition=[=($0, $3)], joinType=[inner])\n "
297+ + " LogicalTableScan(table=[[scott, DEPT]])\n "
298+ + " LogicalJoin(condition=[=($1, $2)], joinType=[inner])\n "
299+ + " LogicalAggregate(group=[{0, 1}])\n "
300+ + " LogicalProject(DEPTNO=[$7], EMPNO0=[CAST($0):INTEGER NOT NULL])\n "
301+ + " LogicalTableScan(table=[[scott, EMP]])\n "
302+ + " LogicalValues(tuples=[[{ 7369 }, { 7499 }]])\n " ;
303+ assertThat (after , hasTree (planAfter ));
304+ }
305+
211306 @ Test void testDecorrelateCountBug () {
212307 final FrameworkConfig frameworkConfig = config ().build ();
213308 final RelBuilder builder = RelBuilder .create (frameworkConfig );
0 commit comments