@@ -36,12 +36,21 @@ int from_collapse_limit;
36
36
int join_collapse_limit ;
37
37
38
38
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
+
39
47
static void extract_lateral_references (PlannerInfo * root , RelOptInfo * brel ,
40
48
Index rtindex );
41
49
static void add_lateral_info (PlannerInfo * root , Relids lhs , Relids rhs );
42
50
static List * deconstruct_recurse (PlannerInfo * root , Node * jtnode ,
43
51
bool below_outer_join ,
44
- Relids * qualscope , Relids * inner_join_rels );
52
+ Relids * qualscope , Relids * inner_join_rels ,
53
+ List * * postponed_qual_list );
45
54
static SpecialJoinInfo * make_outerjoininfo (PlannerInfo * root ,
46
55
Relids left_rels , Relids right_rels ,
47
56
Relids inner_join_rels ,
@@ -53,7 +62,8 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
53
62
Relids qualscope ,
54
63
Relids ojscope ,
55
64
Relids outerjoin_nonnullable ,
56
- Relids deduced_nullable_relids );
65
+ Relids deduced_nullable_relids ,
66
+ List * * postponed_qual_list );
57
67
static bool check_outerjoin_delay (PlannerInfo * root , Relids * relids_p ,
58
68
Relids * nullable_relids_p , bool is_pushed_down );
59
69
static bool check_equivalence_delay (PlannerInfo * root ,
@@ -630,15 +640,23 @@ add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs)
630
640
List *
631
641
deconstruct_jointree (PlannerInfo * root )
632
642
{
643
+ List * result ;
633
644
Relids qualscope ;
634
645
Relids inner_join_rels ;
646
+ List * postponed_qual_list = NIL ;
635
647
636
648
/* Start recursion at top of jointree */
637
649
Assert (root -> parse -> jointree != NULL &&
638
650
IsA (root -> parse -> jointree , FromExpr ));
639
651
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 ;
642
660
}
643
661
644
662
/*
@@ -656,13 +674,16 @@ deconstruct_jointree(PlannerInfo *root)
656
674
* *inner_join_rels gets the set of base Relids syntactically included in
657
675
* inner joins appearing at or below this jointree node (do not modify
658
676
* 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
659
679
* Return value is the appropriate joinlist for this jointree node
660
680
*
661
681
* In addition, entries will be added to root->join_info_list for outer joins.
662
682
*/
663
683
static List *
664
684
deconstruct_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 )
666
687
{
667
688
List * joinlist ;
668
689
@@ -685,6 +706,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
685
706
else if (IsA (jtnode , FromExpr ))
686
707
{
687
708
FromExpr * f = (FromExpr * ) jtnode ;
709
+ List * child_postponed_quals = NIL ;
688
710
int remaining ;
689
711
ListCell * l ;
690
712
@@ -707,7 +729,8 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
707
729
sub_joinlist = deconstruct_recurse (root , lfirst (l ),
708
730
below_outer_join ,
709
731
& sub_qualscope ,
710
- inner_join_rels );
732
+ inner_join_rels ,
733
+ & child_postponed_quals );
711
734
* qualscope = bms_add_members (* qualscope , sub_qualscope );
712
735
sub_members = list_length (sub_joinlist );
713
736
remaining -- ;
@@ -728,6 +751,23 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
728
751
if (list_length (f -> fromlist ) > 1 )
729
752
* inner_join_rels = * qualscope ;
730
753
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
+
731
771
/*
732
772
* Now process the top-level quals.
733
773
*/
@@ -737,12 +777,14 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
737
777
738
778
distribute_qual_to_rels (root , qual ,
739
779
false, below_outer_join , JOIN_INNER ,
740
- * qualscope , NULL , NULL , NULL );
780
+ * qualscope , NULL , NULL , NULL ,
781
+ postponed_qual_list );
741
782
}
742
783
}
743
784
else if (IsA (jtnode , JoinExpr ))
744
785
{
745
786
JoinExpr * j = (JoinExpr * ) jtnode ;
787
+ List * child_postponed_quals = NIL ;
746
788
Relids leftids ,
747
789
rightids ,
748
790
left_inners ,
@@ -771,10 +813,12 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
771
813
case JOIN_INNER :
772
814
leftjoinlist = deconstruct_recurse (root , j -> larg ,
773
815
below_outer_join ,
774
- & leftids , & left_inners );
816
+ & leftids , & left_inners ,
817
+ & child_postponed_quals );
775
818
rightjoinlist = deconstruct_recurse (root , j -> rarg ,
776
819
below_outer_join ,
777
- & rightids , & right_inners );
820
+ & rightids , & right_inners ,
821
+ & child_postponed_quals );
778
822
* qualscope = bms_union (leftids , rightids );
779
823
* inner_join_rels = * qualscope ;
780
824
/* Inner join adds no restrictions for quals */
@@ -784,21 +828,25 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
784
828
case JOIN_ANTI :
785
829
leftjoinlist = deconstruct_recurse (root , j -> larg ,
786
830
below_outer_join ,
787
- & leftids , & left_inners );
831
+ & leftids , & left_inners ,
832
+ & child_postponed_quals );
788
833
rightjoinlist = deconstruct_recurse (root , j -> rarg ,
789
834
true,
790
- & rightids , & right_inners );
835
+ & rightids , & right_inners ,
836
+ & child_postponed_quals );
791
837
* qualscope = bms_union (leftids , rightids );
792
838
* inner_join_rels = bms_union (left_inners , right_inners );
793
839
nonnullable_rels = leftids ;
794
840
break ;
795
841
case JOIN_SEMI :
796
842
leftjoinlist = deconstruct_recurse (root , j -> larg ,
797
843
below_outer_join ,
798
- & leftids , & left_inners );
844
+ & leftids , & left_inners ,
845
+ & child_postponed_quals );
799
846
rightjoinlist = deconstruct_recurse (root , j -> rarg ,
800
847
below_outer_join ,
801
- & rightids , & right_inners );
848
+ & rightids , & right_inners ,
849
+ & child_postponed_quals );
802
850
* qualscope = bms_union (leftids , rightids );
803
851
* inner_join_rels = bms_union (left_inners , right_inners );
804
852
/* Semi join adds no restrictions for quals */
@@ -807,10 +855,12 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
807
855
case JOIN_FULL :
808
856
leftjoinlist = deconstruct_recurse (root , j -> larg ,
809
857
true,
810
- & leftids , & left_inners );
858
+ & leftids , & left_inners ,
859
+ & child_postponed_quals );
811
860
rightjoinlist = deconstruct_recurse (root , j -> rarg ,
812
861
true,
813
- & rightids , & right_inners );
862
+ & rightids , & right_inners ,
863
+ & child_postponed_quals );
814
864
* qualscope = bms_union (leftids , rightids );
815
865
* inner_join_rels = bms_union (left_inners , right_inners );
816
866
/* each side is both outer and inner */
@@ -853,15 +903,41 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
853
903
ojscope = NULL ;
854
904
}
855
905
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 */
857
932
foreach (l , (List * ) j -> quals )
858
933
{
859
934
Node * qual = (Node * ) lfirst (l );
860
935
861
936
distribute_qual_to_rels (root , qual ,
862
937
false, below_outer_join , j -> jointype ,
863
938
* qualscope ,
864
- ojscope , nonnullable_rels , NULL );
939
+ ojscope , nonnullable_rels , NULL ,
940
+ postponed_qual_list );
865
941
}
866
942
867
943
/* Now we can add the SpecialJoinInfo to join_info_list */
@@ -1154,7 +1230,8 @@ make_outerjoininfo(PlannerInfo *root,
1154
1230
* the appropriate list for each rel. Alternatively, if the clause uses a
1155
1231
* mergejoinable operator and is not delayed by outer-join rules, enter
1156
1232
* 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.
1158
1235
*
1159
1236
* 'clause': the qual clause to be distributed
1160
1237
* 'is_deduced': TRUE if the qual came from implied-equality deduction
@@ -1170,6 +1247,9 @@ make_outerjoininfo(PlannerInfo *root,
1170
1247
* equal qualscope)
1171
1248
* 'deduced_nullable_relids': if is_deduced is TRUE, the nullable relids to
1172
1249
* 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.
1173
1253
*
1174
1254
* 'qualscope' identifies what level of JOIN the qual came from syntactically.
1175
1255
* '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,
1190
1270
Relids qualscope ,
1191
1271
Relids ojscope ,
1192
1272
Relids outerjoin_nonnullable ,
1193
- Relids deduced_nullable_relids )
1273
+ Relids deduced_nullable_relids ,
1274
+ List * * postponed_qual_list )
1194
1275
{
1195
1276
Relids relids ;
1196
1277
bool is_pushed_down ;
@@ -1207,20 +1288,33 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
1207
1288
relids = pull_varnos (clause );
1208
1289
1209
1290
/*
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.)
1221
1317
*/
1222
- if (!root -> hasLateralRTEs && !bms_is_subset (relids , qualscope ))
1223
- elog (ERROR , "JOIN qualification cannot refer to other relations" );
1224
1318
if (ojscope && !bms_is_subset (relids , ojscope ))
1225
1319
elog (ERROR , "JOIN qualification cannot refer to other relations" );
1226
1320
@@ -1874,7 +1968,8 @@ process_implied_equality(PlannerInfo *root,
1874
1968
*/
1875
1969
distribute_qual_to_rels (root , (Node * ) clause ,
1876
1970
true, below_outer_join , JOIN_INNER ,
1877
- qualscope , NULL , NULL , nullable_relids );
1971
+ qualscope , NULL , NULL , nullable_relids ,
1972
+ NULL );
1878
1973
}
1879
1974
1880
1975
/*
0 commit comments