@@ -584,4 +584,203 @@ public static Frameworks.ConfigBuilder config() {
584584 + " LogicalTableScan(table=[[scott, DEPT]])\n " ;
585585 assertThat (decorrelatedNoRules , hasTree (planDecorrelatedNoRules ));
586586 }
587+
588+ @ Test void testDecorrelateCorrelatedOrderByLimitToRowNumber () {
589+ final FrameworkConfig frameworkConfig = config ().build ();
590+ final RelBuilder builder = RelBuilder .create (frameworkConfig );
591+ final RelOptCluster cluster = builder .getCluster ();
592+ final Planner planner = Frameworks .getPlanner (frameworkConfig );
593+ final String sql = ""
594+ + "SELECT dname FROM dept WHERE 2000 > (\n "
595+ + "SELECT emp.sal FROM emp where dept.deptno = emp.deptno\n "
596+ + "ORDER BY year(hiredate), emp.sal limit 1)" ;
597+ final RelNode originalRel ;
598+ try {
599+ final SqlNode parse = planner .parse (sql );
600+ final SqlNode validate = planner .validate (parse );
601+ originalRel = planner .rel (validate ).rel ;
602+ } catch (Exception e ) {
603+ throw TestUtil .rethrow (e );
604+ }
605+
606+ final HepProgram hepProgram = HepProgram .builder ()
607+ .addRuleCollection (
608+ ImmutableList .of (
609+ // SubQuery program rules
610+ CoreRules .FILTER_SUB_QUERY_TO_CORRELATE ,
611+ CoreRules .PROJECT_SUB_QUERY_TO_CORRELATE ,
612+ CoreRules .JOIN_SUB_QUERY_TO_CORRELATE ))
613+ .build ();
614+ final Program program =
615+ Programs .of (hepProgram , true ,
616+ requireNonNull (cluster .getMetadataProvider ()));
617+ final RelNode before =
618+ program .run (cluster .getPlanner (), originalRel , cluster .traitSet (),
619+ Collections .emptyList (), Collections .emptyList ());
620+ final String planBefore = ""
621+ + "LogicalProject(DNAME=[$1])\n "
622+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2])\n "
623+ + " LogicalFilter(condition=[>(2000.00, CAST($3):DECIMAL(12, 2))])\n "
624+ + " LogicalCorrelate(correlation=[$cor0], joinType=[left], requiredColumns=[{0}])\n "
625+ + " LogicalTableScan(table=[[scott, DEPT]])\n "
626+ + " LogicalProject(SAL=[$0])\n "
627+ + " LogicalSort(sort0=[$1], sort1=[$0], dir0=[ASC], dir1=[ASC], fetch=[1])\n "
628+ + " LogicalProject(SAL=[$5], EXPR$1=[EXTRACT(FLAG(YEAR), $4)])\n "
629+ + " LogicalFilter(condition=[=($cor0.DEPTNO, $7)])\n "
630+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
631+ assertThat (before , hasTree (planBefore ));
632+
633+ // Decorrelate without any rules, just "purely" decorrelation algorithm on RelDecorrelator
634+ final RelNode after =
635+ RelDecorrelator .decorrelateQuery (before , builder , RuleSets .ofList (Collections .emptyList ()),
636+ RuleSets .ofList (Collections .emptyList ()));
637+ // Verify plan
638+ final String planAfter = ""
639+ + "LogicalProject(DNAME=[$1])\n "
640+ + " LogicalJoin(condition=[=($0, $4)], joinType=[inner])\n "
641+ + " LogicalTableScan(table=[[scott, DEPT]])\n "
642+ + " LogicalFilter(condition=[>(2000.00, CAST($0):DECIMAL(12, 2))])\n "
643+ + " LogicalProject(SAL=[$0], DEPTNO=[$2])\n "
644+ + " LogicalFilter(condition=[<=($3, 1)])\n "
645+ + " LogicalProject(SAL=[$5], EXPR$1=[EXTRACT(FLAG(YEAR), $4)], DEPTNO=[$7], rn=[ROW_NUMBER() OVER (PARTITION BY $7 ORDER BY EXTRACT(FLAG(YEAR), $4) NULLS LAST, $5 NULLS LAST)])\n "
646+ + " LogicalFilter(condition=[IS NOT NULL($7)])\n "
647+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
648+ assertThat (after , hasTree (planAfter ));
649+ }
650+
651+ @ Test void testDecorrelateCorrelatedOrderByLimitToRowNumber2 () {
652+ final FrameworkConfig frameworkConfig = config ().build ();
653+ final RelBuilder builder = RelBuilder .create (frameworkConfig );
654+ final RelOptCluster cluster = builder .getCluster ();
655+ final Planner planner = Frameworks .getPlanner (frameworkConfig );
656+ final String sql = ""
657+ + "SELECT *\n "
658+ + "FROM dept d\n "
659+ + "WHERE d.deptno IN (\n "
660+ + " SELECT e.deptno\n "
661+ + " FROM emp e\n "
662+ + " WHERE d.deptno = e.deptno\n "
663+ + " LIMIT 10\n "
664+ + " OFFSET 2\n "
665+ + ")\n "
666+ + "LIMIT 2\n "
667+ + "OFFSET 1" ;
668+ final RelNode originalRel ;
669+ try {
670+ final SqlNode parse = planner .parse (sql );
671+ final SqlNode validate = planner .validate (parse );
672+ originalRel = planner .rel (validate ).rel ;
673+ } catch (Exception e ) {
674+ throw TestUtil .rethrow (e );
675+ }
676+
677+ final HepProgram hepProgram = HepProgram .builder ()
678+ .addRuleCollection (
679+ ImmutableList .of (
680+ // SubQuery program rules
681+ CoreRules .FILTER_SUB_QUERY_TO_CORRELATE ,
682+ CoreRules .PROJECT_SUB_QUERY_TO_CORRELATE ,
683+ CoreRules .JOIN_SUB_QUERY_TO_CORRELATE ))
684+ .build ();
685+ final Program program =
686+ Programs .of (hepProgram , true ,
687+ requireNonNull (cluster .getMetadataProvider ()));
688+ final RelNode before =
689+ program .run (cluster .getPlanner (), originalRel , cluster .traitSet (),
690+ Collections .emptyList (), Collections .emptyList ());
691+ final String planBefore = ""
692+ + "LogicalSort(offset=[1], fetch=[2])\n "
693+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2])\n "
694+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2])\n "
695+ + " LogicalFilter(condition=[=($0, $3)])\n "
696+ + " LogicalCorrelate(correlation=[$cor0], joinType=[inner], requiredColumns=[{0}])\n "
697+ + " LogicalTableScan(table=[[scott, DEPT]])\n "
698+ + " LogicalAggregate(group=[{0}])\n "
699+ + " LogicalSort(offset=[2], fetch=[10])\n "
700+ + " LogicalProject(DEPTNO=[$7])\n "
701+ + " LogicalFilter(condition=[=($cor0.DEPTNO, $7)])\n "
702+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
703+ assertThat (before , hasTree (planBefore ));
704+
705+ // Decorrelate without any rules, just "purely" decorrelation algorithm on RelDecorrelator
706+ final RelNode after =
707+ RelDecorrelator .decorrelateQuery (before , builder , RuleSets .ofList (Collections .emptyList ()),
708+ RuleSets .ofList (Collections .emptyList ()));
709+ // Verify plan
710+ final String planAfter = ""
711+ + "LogicalSort(offset=[1], fetch=[2])\n "
712+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2])\n "
713+ + " LogicalJoin(condition=[=($0, $4)], joinType=[inner])\n "
714+ + " LogicalTableScan(table=[[scott, DEPT]])\n "
715+ + " LogicalFilter(condition=[=($1, $0)])\n "
716+ + " LogicalAggregate(group=[{0, 1}])\n "
717+ + " LogicalProject(DEPTNO=[$0], DEPTNO1=[$1])\n "
718+ + " LogicalFilter(condition=[AND(>($2, 2), <=($2, +(2, 10)))])\n "
719+ + " LogicalProject(DEPTNO=[$7], DEPTNO1=[$7], rn=[ROW_NUMBER() OVER (PARTITION BY $7)])\n "
720+ + " LogicalFilter(condition=[IS NOT NULL($7)])\n "
721+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
722+ assertThat (after , hasTree (planAfter ));
723+ }
724+
725+ @ Test void testDecorrelateCorrelatedOrderByLimitToRowNumber3 () {
726+ final FrameworkConfig frameworkConfig = config ().build ();
727+ final RelBuilder builder = RelBuilder .create (frameworkConfig );
728+ final RelOptCluster cluster = builder .getCluster ();
729+ final Planner planner = Frameworks .getPlanner (frameworkConfig );
730+ final String sql = ""
731+ + "SELECT deptno FROM dept WHERE 1000.00 >\n "
732+ + "(SELECT sal FROM emp WHERE dept.deptno = emp.deptno\n "
733+ + "order by emp.sal limit 1 offset 10)" ;
734+ final RelNode originalRel ;
735+ try {
736+ final SqlNode parse = planner .parse (sql );
737+ final SqlNode validate = planner .validate (parse );
738+ originalRel = planner .rel (validate ).rel ;
739+ } catch (Exception e ) {
740+ throw TestUtil .rethrow (e );
741+ }
742+
743+ final HepProgram hepProgram = HepProgram .builder ()
744+ .addRuleCollection (
745+ ImmutableList .of (
746+ // SubQuery program rules
747+ CoreRules .FILTER_SUB_QUERY_TO_CORRELATE ,
748+ CoreRules .PROJECT_SUB_QUERY_TO_CORRELATE ,
749+ CoreRules .JOIN_SUB_QUERY_TO_CORRELATE ))
750+ .build ();
751+ final Program program =
752+ Programs .of (hepProgram , true ,
753+ requireNonNull (cluster .getMetadataProvider ()));
754+ final RelNode before =
755+ program .run (cluster .getPlanner (), originalRel , cluster .traitSet (),
756+ Collections .emptyList (), Collections .emptyList ());
757+ final String planBefore = ""
758+ + "LogicalProject(DEPTNO=[$0])\n "
759+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2])\n "
760+ + " LogicalFilter(condition=[>(1000.00, $3)])\n "
761+ + " LogicalCorrelate(correlation=[$cor0], joinType=[left], requiredColumns=[{0}])\n "
762+ + " LogicalTableScan(table=[[scott, DEPT]])\n "
763+ + " LogicalSort(sort0=[$0], dir0=[ASC], offset=[10], fetch=[1])\n "
764+ + " LogicalProject(SAL=[$5])\n "
765+ + " LogicalFilter(condition=[=($cor0.DEPTNO, $7)])\n "
766+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
767+ assertThat (before , hasTree (planBefore ));
768+
769+ // Decorrelate without any rules, just "purely" decorrelation algorithm on RelDecorrelator
770+ final RelNode after =
771+ RelDecorrelator .decorrelateQuery (before , builder , RuleSets .ofList (Collections .emptyList ()),
772+ RuleSets .ofList (Collections .emptyList ()));
773+ // Verify plan
774+ final String planAfter = ""
775+ + "LogicalProject(DEPTNO=[$0])\n "
776+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2], SAL=[$3], DEPTNO0=[$4], rn=[CAST($5):BIGINT])\n "
777+ + " LogicalJoin(condition=[=($0, $4)], joinType=[inner])\n "
778+ + " LogicalTableScan(table=[[scott, DEPT]])\n "
779+ + " LogicalFilter(condition=[>(1000.00, $0)])\n "
780+ + " LogicalFilter(condition=[AND(>($2, 10), <=($2, +(10, 1)))])\n "
781+ + " LogicalProject(SAL=[$5], DEPTNO=[$7], rn=[ROW_NUMBER() OVER (PARTITION BY $7 ORDER BY $5 NULLS LAST)])\n "
782+ + " LogicalFilter(condition=[IS NOT NULL($7)])\n "
783+ + " LogicalTableScan(table=[[scott, EMP]])\n " ;
784+ assertThat (after , hasTree (planAfter ));
785+ }
587786}
0 commit comments