@@ -36,12 +36,21 @@ int from_collapse_limit;
3636int join_collapse_limit ;
3737
3838
39+ /* Elements of the postponed_qual_list used during deconstruct_recurse */
40+ typedef struct PostponedQual
41+ {
42+ Node * qual ; /* a qual clause waiting to be processed */
43+ Relids relids ; /* the set of baserels it references */
44+ } PostponedQual ;
45+
46+
3947static void extract_lateral_references (PlannerInfo * root , RelOptInfo * brel ,
4048 Index rtindex );
4149static void add_lateral_info (PlannerInfo * root , Relids lhs , Relids rhs );
4250static List * deconstruct_recurse (PlannerInfo * root , Node * jtnode ,
4351 bool below_outer_join ,
44- Relids * qualscope , Relids * inner_join_rels );
52+ Relids * qualscope , Relids * inner_join_rels ,
53+ List * * postponed_qual_list );
4554static SpecialJoinInfo * make_outerjoininfo (PlannerInfo * root ,
4655 Relids left_rels , Relids right_rels ,
4756 Relids inner_join_rels ,
@@ -53,7 +62,8 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
5362 Relids qualscope ,
5463 Relids ojscope ,
5564 Relids outerjoin_nonnullable ,
56- Relids deduced_nullable_relids );
65+ Relids deduced_nullable_relids ,
66+ List * * postponed_qual_list );
5767static bool check_outerjoin_delay (PlannerInfo * root , Relids * relids_p ,
5868 Relids * nullable_relids_p , bool is_pushed_down );
5969static bool check_equivalence_delay (PlannerInfo * root ,
@@ -630,15 +640,23 @@ add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs)
630640List *
631641deconstruct_jointree (PlannerInfo * root )
632642{
643+ List * result ;
633644 Relids qualscope ;
634645 Relids inner_join_rels ;
646+ List * postponed_qual_list = NIL ;
635647
636648 /* Start recursion at top of jointree */
637649 Assert (root -> parse -> jointree != NULL &&
638650 IsA (root -> parse -> jointree , FromExpr ));
639651
640- return deconstruct_recurse (root , (Node * ) root -> parse -> jointree , false,
641- & qualscope , & inner_join_rels );
652+ result = deconstruct_recurse (root , (Node * ) root -> parse -> jointree , false,
653+ & qualscope , & inner_join_rels ,
654+ & postponed_qual_list );
655+
656+ /* Shouldn't be any leftover quals */
657+ Assert (postponed_qual_list == NIL );
658+
659+ return result ;
642660}
643661
644662/*
@@ -656,13 +674,16 @@ deconstruct_jointree(PlannerInfo *root)
656674 * *inner_join_rels gets the set of base Relids syntactically included in
657675 * inner joins appearing at or below this jointree node (do not modify
658676 * or free this, either)
677+ * *postponed_qual_list is a list of PostponedQual structs, which we can
678+ * add quals to if they turn out to belong to a higher join level
659679 * Return value is the appropriate joinlist for this jointree node
660680 *
661681 * In addition, entries will be added to root->join_info_list for outer joins.
662682 */
663683static List *
664684deconstruct_recurse (PlannerInfo * root , Node * jtnode , bool below_outer_join ,
665- Relids * qualscope , Relids * inner_join_rels )
685+ Relids * qualscope , Relids * inner_join_rels ,
686+ List * * postponed_qual_list )
666687{
667688 List * joinlist ;
668689
@@ -685,6 +706,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
685706 else if (IsA (jtnode , FromExpr ))
686707 {
687708 FromExpr * f = (FromExpr * ) jtnode ;
709+ List * child_postponed_quals = NIL ;
688710 int remaining ;
689711 ListCell * l ;
690712
@@ -707,7 +729,8 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
707729 sub_joinlist = deconstruct_recurse (root , lfirst (l ),
708730 below_outer_join ,
709731 & sub_qualscope ,
710- inner_join_rels );
732+ inner_join_rels ,
733+ & child_postponed_quals );
711734 * qualscope = bms_add_members (* qualscope , sub_qualscope );
712735 sub_members = list_length (sub_joinlist );
713736 remaining -- ;
@@ -728,6 +751,23 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
728751 if (list_length (f -> fromlist ) > 1 )
729752 * inner_join_rels = * qualscope ;
730753
754+ /*
755+ * Try to process any quals postponed by children. If they need
756+ * further postponement, add them to my output postponed_qual_list.
757+ */
758+ foreach (l , child_postponed_quals )
759+ {
760+ PostponedQual * pq = (PostponedQual * ) lfirst (l );
761+
762+ if (bms_is_subset (pq -> relids , * qualscope ))
763+ distribute_qual_to_rels (root , pq -> qual ,
764+ false, below_outer_join , JOIN_INNER ,
765+ * qualscope , NULL , NULL , NULL ,
766+ NULL );
767+ else
768+ * postponed_qual_list = lappend (* postponed_qual_list , pq );
769+ }
770+
731771 /*
732772 * Now process the top-level quals.
733773 */
@@ -737,12 +777,14 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
737777
738778 distribute_qual_to_rels (root , qual ,
739779 false, below_outer_join , JOIN_INNER ,
740- * qualscope , NULL , NULL , NULL );
780+ * qualscope , NULL , NULL , NULL ,
781+ postponed_qual_list );
741782 }
742783 }
743784 else if (IsA (jtnode , JoinExpr ))
744785 {
745786 JoinExpr * j = (JoinExpr * ) jtnode ;
787+ List * child_postponed_quals = NIL ;
746788 Relids leftids ,
747789 rightids ,
748790 left_inners ,
@@ -771,10 +813,12 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
771813 case JOIN_INNER :
772814 leftjoinlist = deconstruct_recurse (root , j -> larg ,
773815 below_outer_join ,
774- & leftids , & left_inners );
816+ & leftids , & left_inners ,
817+ & child_postponed_quals );
775818 rightjoinlist = deconstruct_recurse (root , j -> rarg ,
776819 below_outer_join ,
777- & rightids , & right_inners );
820+ & rightids , & right_inners ,
821+ & child_postponed_quals );
778822 * qualscope = bms_union (leftids , rightids );
779823 * inner_join_rels = * qualscope ;
780824 /* Inner join adds no restrictions for quals */
@@ -784,21 +828,25 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
784828 case JOIN_ANTI :
785829 leftjoinlist = deconstruct_recurse (root , j -> larg ,
786830 below_outer_join ,
787- & leftids , & left_inners );
831+ & leftids , & left_inners ,
832+ & child_postponed_quals );
788833 rightjoinlist = deconstruct_recurse (root , j -> rarg ,
789834 true,
790- & rightids , & right_inners );
835+ & rightids , & right_inners ,
836+ & child_postponed_quals );
791837 * qualscope = bms_union (leftids , rightids );
792838 * inner_join_rels = bms_union (left_inners , right_inners );
793839 nonnullable_rels = leftids ;
794840 break ;
795841 case JOIN_SEMI :
796842 leftjoinlist = deconstruct_recurse (root , j -> larg ,
797843 below_outer_join ,
798- & leftids , & left_inners );
844+ & leftids , & left_inners ,
845+ & child_postponed_quals );
799846 rightjoinlist = deconstruct_recurse (root , j -> rarg ,
800847 below_outer_join ,
801- & rightids , & right_inners );
848+ & rightids , & right_inners ,
849+ & child_postponed_quals );
802850 * qualscope = bms_union (leftids , rightids );
803851 * inner_join_rels = bms_union (left_inners , right_inners );
804852 /* Semi join adds no restrictions for quals */
@@ -807,10 +855,12 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
807855 case JOIN_FULL :
808856 leftjoinlist = deconstruct_recurse (root , j -> larg ,
809857 true,
810- & leftids , & left_inners );
858+ & leftids , & left_inners ,
859+ & child_postponed_quals );
811860 rightjoinlist = deconstruct_recurse (root , j -> rarg ,
812861 true,
813- & rightids , & right_inners );
862+ & rightids , & right_inners ,
863+ & child_postponed_quals );
814864 * qualscope = bms_union (leftids , rightids );
815865 * inner_join_rels = bms_union (left_inners , right_inners );
816866 /* each side is both outer and inner */
@@ -853,15 +903,41 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
853903 ojscope = NULL ;
854904 }
855905
856- /* Process the qual clauses */
906+ /*
907+ * Try to process any quals postponed by children. If they need
908+ * further postponement, add them to my output postponed_qual_list.
909+ */
910+ foreach (l , child_postponed_quals )
911+ {
912+ PostponedQual * pq = (PostponedQual * ) lfirst (l );
913+
914+ if (bms_is_subset (pq -> relids , * qualscope ))
915+ distribute_qual_to_rels (root , pq -> qual ,
916+ false, below_outer_join , j -> jointype ,
917+ * qualscope ,
918+ ojscope , nonnullable_rels , NULL ,
919+ NULL );
920+ else
921+ {
922+ /*
923+ * We should not be postponing any quals past an outer join.
924+ * If this Assert fires, pull_up_subqueries() messed up.
925+ */
926+ Assert (j -> jointype == JOIN_INNER );
927+ * postponed_qual_list = lappend (* postponed_qual_list , pq );
928+ }
929+ }
930+
931+ /* Process the JOIN's qual clauses */
857932 foreach (l , (List * ) j -> quals )
858933 {
859934 Node * qual = (Node * ) lfirst (l );
860935
861936 distribute_qual_to_rels (root , qual ,
862937 false, below_outer_join , j -> jointype ,
863938 * qualscope ,
864- ojscope , nonnullable_rels , NULL );
939+ ojscope , nonnullable_rels , NULL ,
940+ postponed_qual_list );
865941 }
866942
867943 /* Now we can add the SpecialJoinInfo to join_info_list */
@@ -1154,7 +1230,8 @@ make_outerjoininfo(PlannerInfo *root,
11541230 * the appropriate list for each rel. Alternatively, if the clause uses a
11551231 * mergejoinable operator and is not delayed by outer-join rules, enter
11561232 * the left- and right-side expressions into the query's list of
1157- * EquivalenceClasses.
1233+ * EquivalenceClasses. Alternatively, if the clause needs to be treated
1234+ * as belonging to a higher join level, just add it to postponed_qual_list.
11581235 *
11591236 * 'clause': the qual clause to be distributed
11601237 * 'is_deduced': TRUE if the qual came from implied-equality deduction
@@ -1170,6 +1247,9 @@ make_outerjoininfo(PlannerInfo *root,
11701247 * equal qualscope)
11711248 * 'deduced_nullable_relids': if is_deduced is TRUE, the nullable relids to
11721249 * impute to the clause; otherwise NULL
1250+ * 'postponed_qual_list': list of PostponedQual structs, which we can add
1251+ * this qual to if it turns out to belong to a higher join level.
1252+ * Can be NULL if caller knows postponement is impossible.
11731253 *
11741254 * 'qualscope' identifies what level of JOIN the qual came from syntactically.
11751255 * 'ojscope' is needed if we decide to force the qual up to the outer-join
@@ -1190,7 +1270,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
11901270 Relids qualscope ,
11911271 Relids ojscope ,
11921272 Relids outerjoin_nonnullable ,
1193- Relids deduced_nullable_relids )
1273+ Relids deduced_nullable_relids ,
1274+ List * * postponed_qual_list )
11941275{
11951276 Relids relids ;
11961277 bool is_pushed_down ;
@@ -1207,20 +1288,33 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
12071288 relids = pull_varnos (clause );
12081289
12091290 /*
1210- * Normally relids is a subset of qualscope, and we like to check that
1211- * here as a crosscheck on the parser and rewriter. That need not be the
1212- * case when there are LATERAL RTEs, however: the clause could contain
1213- * references to rels outside its syntactic scope as a consequence of
1214- * pull-up of such references from a LATERAL subquery below it. So, only
1215- * check if the query contains no LATERAL RTEs.
1216- *
1217- * However, if it's an outer-join clause, we always insist that relids be
1218- * a subset of ojscope. This is safe because is_simple_subquery()
1219- * disallows pullup of LATERAL subqueries that could cause the restriction
1220- * to be violated.
1291+ * In ordinary SQL, a WHERE or JOIN/ON clause can't reference any rels
1292+ * that aren't within its syntactic scope; however, if we pulled up a
1293+ * LATERAL subquery then we might find such references in quals that have
1294+ * been pulled up. We need to treat such quals as belonging to the join
1295+ * level that includes every rel they reference. Although we could make
1296+ * pull_up_subqueries() place such quals correctly to begin with, it's
1297+ * easier to handle it here. When we find a clause that contains Vars
1298+ * outside its syntactic scope, we add it to the postponed-quals list, and
1299+ * process it once we've recursed back up to the appropriate join level.
1300+ */
1301+ if (!bms_is_subset (relids , qualscope ))
1302+ {
1303+ PostponedQual * pq = (PostponedQual * ) palloc (sizeof (PostponedQual ));
1304+
1305+ Assert (root -> hasLateralRTEs ); /* shouldn't happen otherwise */
1306+ Assert (jointype == JOIN_INNER ); /* mustn't postpone past outer join */
1307+ Assert (!is_deduced ); /* shouldn't be deduced, either */
1308+ pq -> qual = clause ;
1309+ pq -> relids = relids ;
1310+ * postponed_qual_list = lappend (* postponed_qual_list , pq );
1311+ return ;
1312+ }
1313+
1314+ /*
1315+ * If it's an outer-join clause, also check that relids is a subset of
1316+ * ojscope. (This should not fail if the syntactic scope check passed.)
12211317 */
1222- if (!root -> hasLateralRTEs && !bms_is_subset (relids , qualscope ))
1223- elog (ERROR , "JOIN qualification cannot refer to other relations" );
12241318 if (ojscope && !bms_is_subset (relids , ojscope ))
12251319 elog (ERROR , "JOIN qualification cannot refer to other relations" );
12261320
@@ -1874,7 +1968,8 @@ process_implied_equality(PlannerInfo *root,
18741968 */
18751969 distribute_qual_to_rels (root , (Node * ) clause ,
18761970 true, below_outer_join , JOIN_INNER ,
1877- qualscope , NULL , NULL , nullable_relids );
1971+ qualscope , NULL , NULL , nullable_relids ,
1972+ NULL );
18781973}
18791974
18801975/*
0 commit comments