@@ -208,6 +208,84 @@ 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+ final String planBefore = ""
249+ + "LogicalSort(sort0=[$0], dir0=[ASC])\n "
250+ + " LogicalProject(ENAME_FROM_DEPT=[$1])\n "
251+ + " LogicalCorrelate(correlation=[$cor2], joinType=[left], requiredColumns=[{0}])\n "
252+ + " LogicalValues(tuples=[[{ 7369 }, { 7499 }]])\n "
253+ + " LogicalAggregate(group=[{}], agg#0=[SINGLE_VALUE($0)])\n "
254+ + " LogicalProject(EXPR$0=[||(||($1, ' from dept '), $8)])\n "
255+ + " LogicalCorrelate(correlation=[$cor0], joinType=[left], requiredColumns=[{0, 7}])\n "
256+ + " LogicalTableScan(table=[[scott, EMP]])\n "
257+ + " LogicalAggregate(group=[{}], agg#0=[SINGLE_VALUE($0)])\n "
258+ + " LogicalProject(DNAME=[$1])\n "
259+ + " LogicalFilter(condition=[AND(=($0, $cor0.DEPTNO), =(CAST($cor0.EMPNO):INTEGER NOT NULL, $cor2.EMPNO))])\n "
260+ + " LogicalTableScan(table=[[scott, DEPT]])\n " ;
261+ assertThat (before , hasTree (planBefore ));
262+
263+ // Decorrelate without any rules, just "purely" decorrelation algorithm on RelDecorrelator
264+ final RelNode after =
265+ RelDecorrelator .decorrelateQuery (before , builder , RuleSets .ofList (Collections .emptyList ()),
266+ RuleSets .ofList (Collections .emptyList ()));
267+ final String planAfter = ""
268+ + "LogicalSort(sort0=[$0], dir0=[ASC])\n "
269+ + " LogicalProject(ENAME_FROM_DEPT=[$2])\n "
270+ + " LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $1)], joinType=[left])\n "
271+ + " LogicalValues(tuples=[[{ 7369 }, { 7499 }]])\n "
272+ + " LogicalAggregate(group=[{0}], agg#0=[SINGLE_VALUE($1)])\n "
273+ + " LogicalProject(EMPNO1=[$12], EXPR$0=[||(||($1, ' from dept '), $13)])\n "
274+ + " LogicalJoin(condition=[AND(=($7, $10), =($9, $11))], joinType=[left])\n "
275+ + " 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 "
276+ + " LogicalTableScan(table=[[scott, EMP]])\n "
277+ + " LogicalAggregate(group=[{0, 1, 2}], agg#0=[SINGLE_VALUE($3)])\n "
278+ + " LogicalProject(DEPTNO0=[$3], EMPNO0=[$4], EMPNO=[$5], DNAME=[$1])\n "
279+ + " LogicalJoin(condition=[=($0, $3)], joinType=[inner])\n "
280+ + " LogicalTableScan(table=[[scott, DEPT]])\n "
281+ + " LogicalJoin(condition=[=($1, $2)], joinType=[inner])\n "
282+ + " LogicalAggregate(group=[{0, 1}])\n "
283+ + " LogicalProject(DEPTNO=[$7], EMPNO0=[CAST($0):INTEGER NOT NULL])\n "
284+ + " LogicalTableScan(table=[[scott, EMP]])\n "
285+ + " LogicalValues(tuples=[[{ 7369 }, { 7499 }]])\n " ;
286+ assertThat (after , hasTree (planAfter ));
287+ }
288+
211289 @ Test void testDecorrelateCountBug () {
212290 final FrameworkConfig frameworkConfig = config ().build ();
213291 final RelBuilder builder = RelBuilder .create (frameworkConfig );
0 commit comments