@@ -111,7 +111,9 @@ static void reduce_outer_joins_pass2(Node *jtnode,
111
111
static Node * remove_useless_results_recurse (PlannerInfo * root , Node * jtnode );
112
112
static int get_result_relid (PlannerInfo * root , Node * jtnode );
113
113
static void remove_result_refs (PlannerInfo * root , int varno , Node * newjtloc );
114
- static bool find_dependent_phvs (Node * node , int varno );
114
+ static bool find_dependent_phvs (PlannerInfo * root , int varno );
115
+ static bool find_dependent_phvs_in_jointree (PlannerInfo * root ,
116
+ Node * node , int varno );
115
117
static void substitute_phv_relids (Node * node ,
116
118
int varno , Relids subrelids );
117
119
static void fix_append_rel_relids (List * append_rel_list , int varno ,
@@ -2779,9 +2781,12 @@ reduce_outer_joins_pass2(Node *jtnode,
2779
2781
* and not remove the RTE_RESULT: there is noplace else to evaluate the
2780
2782
* PlaceHolderVar. (That is, in such cases the RTE_RESULT *does* have output
2781
2783
* columns.) But if the RTE_RESULT is an immediate child of an inner join,
2782
- * we can change the PlaceHolderVar's phrels so as to evaluate it at the
2783
- * inner join instead. This is OK because we really only care that PHVs are
2784
- * evaluated above or below the correct outer joins.
2784
+ * we can usually change the PlaceHolderVar's phrels so as to evaluate it at
2785
+ * the inner join instead. This is OK because we really only care that PHVs
2786
+ * are evaluated above or below the correct outer joins. We can't, however,
2787
+ * postpone the evaluation of a PHV to above where it is used; so there are
2788
+ * some checks below on whether output PHVs are laterally referenced in the
2789
+ * other join input rel(s).
2785
2790
*
2786
2791
* We used to try to do this work as part of pull_up_subqueries() where the
2787
2792
* potentially-optimizable cases get introduced; but it's way simpler, and
@@ -2851,8 +2856,11 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
2851
2856
/*
2852
2857
* We can drop RTE_RESULT rels from the fromlist so long as at least
2853
2858
* one child remains, since joining to a one-row table changes
2854
- * nothing. The easiest way to mechanize this rule is to modify the
2855
- * list in-place, using list_delete_cell.
2859
+ * nothing. (But we can't drop a RTE_RESULT that computes PHV(s) that
2860
+ * are needed by some sibling. The cleanup transformation below would
2861
+ * reassign the PHVs to be computed at the join, which is too late for
2862
+ * the sibling's use.) The easiest way to mechanize this rule is to
2863
+ * modify the list in-place, using list_delete_cell.
2856
2864
*/
2857
2865
prev = NULL ;
2858
2866
for (cell = list_head (f -> fromlist ); cell ; cell = next )
@@ -2867,12 +2875,14 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
2867
2875
next = lnext (cell );
2868
2876
2869
2877
/*
2870
- * If it's an RTE_RESULT with at least one sibling, we can drop
2871
- * it. We don't yet know what the inner join's final relid set
2872
- * will be, so postpone cleanup of PHVs etc till after this loop.
2878
+ * If it's an RTE_RESULT with at least one sibling, and no sibling
2879
+ * references dependent PHVs, we can drop it. We don't yet know
2880
+ * what the inner join's final relid set will be, so postpone
2881
+ * cleanup of PHVs etc till after this loop.
2873
2882
*/
2874
2883
if (list_length (f -> fromlist ) > 1 &&
2875
- (varno = get_result_relid (root , child )) != 0 )
2884
+ (varno = get_result_relid (root , child )) != 0 &&
2885
+ !find_dependent_phvs_in_jointree (root , (Node * ) f , varno ))
2876
2886
{
2877
2887
f -> fromlist = list_delete_cell (f -> fromlist , cell , prev );
2878
2888
result_relids = bms_add_member (result_relids , varno );
@@ -2925,8 +2935,18 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
2925
2935
* the join with a FromExpr with just the other side; and if
2926
2936
* the qual is empty (JOIN ON TRUE) then we can omit the
2927
2937
* FromExpr as well.
2938
+ *
2939
+ * Just as in the FromExpr case, we can't simplify if the
2940
+ * other input rel references any PHVs that are marked as to
2941
+ * be evaluated at the RTE_RESULT rel, because we can't
2942
+ * postpone their evaluation in that case. But we only have
2943
+ * to check this in cases where it's syntactically legal for
2944
+ * the other input to have a LATERAL reference to the
2945
+ * RTE_RESULT rel. Only RHSes of inner and left joins are
2946
+ * allowed to have such refs.
2928
2947
*/
2929
- if ((varno = get_result_relid (root , j -> larg )) != 0 )
2948
+ if ((varno = get_result_relid (root , j -> larg )) != 0 &&
2949
+ !find_dependent_phvs_in_jointree (root , j -> rarg , varno ))
2930
2950
{
2931
2951
remove_result_refs (root , varno , j -> rarg );
2932
2952
if (j -> quals )
@@ -2955,6 +2975,8 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
2955
2975
* strength-reduced to a plain inner join, since each LHS row
2956
2976
* necessarily has exactly one join partner. So we can always
2957
2977
* discard the RHS, much as in the JOIN_INNER case above.
2978
+ * (Again, the LHS could not contain a lateral reference to
2979
+ * the RHS.)
2958
2980
*
2959
2981
* Otherwise, it's still true that each LHS row should be
2960
2982
* returned exactly once, and since the RHS returns no columns
@@ -2965,7 +2987,7 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
2965
2987
*/
2966
2988
if ((varno = get_result_relid (root , j -> rarg )) != 0 &&
2967
2989
(j -> quals == NULL ||
2968
- !find_dependent_phvs (( Node * ) root -> parse , varno )))
2990
+ !find_dependent_phvs (root , varno )))
2969
2991
{
2970
2992
remove_result_refs (root , varno , j -> larg );
2971
2993
jtnode = j -> larg ;
@@ -2975,7 +2997,7 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
2975
2997
/* Mirror-image of the JOIN_LEFT case */
2976
2998
if ((varno = get_result_relid (root , j -> larg )) != 0 &&
2977
2999
(j -> quals == NULL ||
2978
- !find_dependent_phvs (( Node * ) root -> parse , varno )))
3000
+ !find_dependent_phvs (root , varno )))
2979
3001
{
2980
3002
remove_result_refs (root , varno , j -> rarg );
2981
3003
jtnode = j -> rarg ;
@@ -2996,7 +3018,7 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
2996
3018
*/
2997
3019
if ((varno = get_result_relid (root , j -> rarg )) != 0 )
2998
3020
{
2999
- Assert (!find_dependent_phvs (( Node * ) root -> parse , varno ));
3021
+ Assert (!find_dependent_phvs (root , varno ));
3000
3022
remove_result_refs (root , varno , j -> larg );
3001
3023
if (j -> quals )
3002
3024
jtnode = (Node * )
@@ -3080,6 +3102,12 @@ remove_result_refs(PlannerInfo *root, int varno, Node *newjtloc)
3080
3102
/*
3081
3103
* find_dependent_phvs - are there any PlaceHolderVars whose relids are
3082
3104
* exactly the given varno?
3105
+ *
3106
+ * find_dependent_phvs should be used when we want to see if there are
3107
+ * any such PHVs anywhere in the Query. Another use-case is to see if
3108
+ * a subtree of the join tree contains such PHVs; but for that, we have
3109
+ * to look not only at the join tree nodes themselves but at the
3110
+ * referenced RTEs. For that, use find_dependent_phvs_in_jointree.
3083
3111
*/
3084
3112
3085
3113
typedef struct
@@ -3126,20 +3154,65 @@ find_dependent_phvs_walker(Node *node,
3126
3154
}
3127
3155
3128
3156
static bool
3129
- find_dependent_phvs (Node * node , int varno )
3157
+ find_dependent_phvs (PlannerInfo * root , int varno )
3158
+ {
3159
+ find_dependent_phvs_context context ;
3160
+
3161
+ /* If there are no PHVs anywhere, we needn't work hard */
3162
+ if (root -> glob -> lastPHId == 0 )
3163
+ return false;
3164
+
3165
+ context .relids = bms_make_singleton (varno );
3166
+ context .sublevels_up = 0 ;
3167
+
3168
+ return query_tree_walker (root -> parse ,
3169
+ find_dependent_phvs_walker ,
3170
+ (void * ) & context ,
3171
+ 0 );
3172
+ }
3173
+
3174
+ static bool
3175
+ find_dependent_phvs_in_jointree (PlannerInfo * root , Node * node , int varno )
3130
3176
{
3131
3177
find_dependent_phvs_context context ;
3178
+ Relids subrelids ;
3179
+ int relid ;
3180
+
3181
+ /* If there are no PHVs anywhere, we needn't work hard */
3182
+ if (root -> glob -> lastPHId == 0 )
3183
+ return false;
3132
3184
3133
3185
context .relids = bms_make_singleton (varno );
3134
3186
context .sublevels_up = 0 ;
3135
3187
3136
3188
/*
3137
- * Must be prepared to start with a Query or a bare expression tree.
3189
+ * See if the jointree fragment itself contains references (in join quals)
3190
+ */
3191
+ if (find_dependent_phvs_walker (node , & context ))
3192
+ return true;
3193
+
3194
+ /*
3195
+ * Otherwise, identify the set of referenced RTEs (we can ignore joins,
3196
+ * since they should be flattened already, so their join alias lists no
3197
+ * longer matter), and tediously check each RTE. We can ignore RTEs that
3198
+ * are not marked LATERAL, though, since they couldn't possibly contain
3199
+ * any cross-references to other RTEs.
3138
3200
*/
3139
- return query_or_expression_tree_walker (node ,
3140
- find_dependent_phvs_walker ,
3141
- (void * ) & context ,
3142
- 0 );
3201
+ subrelids = get_relids_in_jointree (node , false);
3202
+ relid = -1 ;
3203
+ while ((relid = bms_next_member (subrelids , relid )) >= 0 )
3204
+ {
3205
+ RangeTblEntry * rte = rt_fetch (relid , root -> parse -> rtable );
3206
+
3207
+ if (rte -> lateral &&
3208
+ range_table_entry_walker (rte ,
3209
+ find_dependent_phvs_walker ,
3210
+ (void * ) & context ,
3211
+ 0 ))
3212
+ return true;
3213
+ }
3214
+
3215
+ return false;
3143
3216
}
3144
3217
3145
3218
/*
0 commit comments