@@ -584,4 +584,78 @@ public static Frameworks.ConfigBuilder config() {
584584 + " LogicalTableScan(table=[[scott, DEPT]])\n " ;
585585 assertThat (decorrelatedNoRules , hasTree (planDecorrelatedNoRules ));
586586 }
587+
588+ /** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-7257">[CALCITE-7257]
589+ * Subqueries cannot be decorrelated if join condition contains RexFieldAccess</a>. */
590+ @ Test void testJoinConditionContainsRexFieldAccess () {
591+ final FrameworkConfig frameworkConfig = config ().build ();
592+ final RelBuilder builder = RelBuilder .create (frameworkConfig );
593+ final RelOptCluster cluster = builder .getCluster ();
594+ final Planner planner = Frameworks .getPlanner (frameworkConfig );
595+ final String sql = ""
596+ + "SELECT E1.* \n "
597+ + "FROM\n "
598+ + " EMP E1\n "
599+ + "WHERE\n "
600+ + " E1.EMPNO = (\n "
601+ + " SELECT D1.DEPTNO FROM DEPT D1\n "
602+ + " WHERE E1.ENAME IN (SELECT B1.ENAME FROM BONUS B1))" ;
603+ final RelNode originalRel ;
604+ try {
605+ final SqlNode parse = planner .parse (sql );
606+ final SqlNode validate = planner .validate (parse );
607+ originalRel = planner .rel (validate ).rel ;
608+ } catch (Exception e ) {
609+ throw TestUtil .rethrow (e );
610+ }
611+
612+ final HepProgram hepProgram = HepProgram .builder ()
613+ .addRuleCollection (
614+ ImmutableList .of (
615+ // SubQuery program rules
616+ CoreRules .FILTER_SUB_QUERY_TO_CORRELATE ,
617+ CoreRules .PROJECT_SUB_QUERY_TO_CORRELATE ,
618+ CoreRules .JOIN_SUB_QUERY_TO_CORRELATE ))
619+ .build ();
620+ final Program program =
621+ Programs .of (hepProgram , true ,
622+ requireNonNull (cluster .getMetadataProvider ()));
623+ final RelNode before =
624+ program .run (cluster .getPlanner (), originalRel , cluster .traitSet (),
625+ Collections .emptyList (), Collections .emptyList ());
626+ final String planBefore = ""
627+ + "LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7])\n "
628+ + " LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7])\n "
629+ + " LogicalFilter(condition=[=($0, CAST($8):SMALLINT)])\n "
630+ + " LogicalCorrelate(correlation=[$cor0], joinType=[left], requiredColumns=[{1}])\n "
631+ + " LogicalTableScan(table=[[scott, EMP]])\n "
632+ + " LogicalAggregate(group=[{}], agg#0=[SINGLE_VALUE($0)])\n "
633+ + " LogicalProject(DEPTNO=[$0])\n "
634+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2])\n "
635+ + " LogicalJoin(condition=[=($cor0.ENAME, $3)], joinType=[inner])\n "
636+ + " LogicalTableScan(table=[[scott, DEPT]])\n "
637+ + " LogicalProject(ENAME=[$0])\n "
638+ + " LogicalTableScan(table=[[scott, BONUS]])\n " ;
639+ assertThat (before , hasTree (planBefore ));
640+
641+ // Decorrelate without any rules, just "purely" decorrelation algorithm on RelDecorrelator
642+ final RelNode after =
643+ RelDecorrelator .decorrelateQuery (before , builder , RuleSets .ofList (Collections .emptyList ()),
644+ RuleSets .ofList (Collections .emptyList ()));
645+ final String planAfter = ""
646+ + "LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7])\n "
647+ + " LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], ENAME0=[$8], $f1=[CAST($9):TINYINT])\n "
648+ + " LogicalJoin(condition=[AND(=($1, $8), =($0, CAST($9):SMALLINT))], joinType=[inner])\n "
649+ + " LogicalTableScan(table=[[scott, EMP]])\n "
650+ + " LogicalAggregate(group=[{0}], agg#0=[SINGLE_VALUE($1)])\n "
651+ + " LogicalProject(ENAME=[$0], DEPTNO=[$1])\n "
652+ + " LogicalJoin(condition=[=($0, $4)], joinType=[inner])\n "
653+ + " LogicalJoin(condition=[true], joinType=[inner])\n "
654+ + " LogicalProject(ENAME=[$1])\n "
655+ + " LogicalTableScan(table=[[scott, EMP]])\n "
656+ + " LogicalTableScan(table=[[scott, DEPT]])\n "
657+ + " LogicalProject(ENAME=[$0])\n "
658+ + " LogicalTableScan(table=[[scott, BONUS]])\n " ;
659+ assertThat (after , hasTree (planAfter ));
660+ }
587661}
0 commit comments