@@ -120,7 +120,9 @@ static void reduce_outer_joins_pass2(Node *jtnode,
120
120
static Node * remove_useless_results_recurse (PlannerInfo * root , Node * jtnode );
121
121
static int get_result_relid (PlannerInfo * root , Node * jtnode );
122
122
static void remove_result_refs (PlannerInfo * root , int varno , Node * newjtloc );
123
- static bool find_dependent_phvs (Node * node , int varno );
123
+ static bool find_dependent_phvs (PlannerInfo * root , int varno );
124
+ static bool find_dependent_phvs_in_jointree (PlannerInfo * root ,
125
+ Node * node , int varno );
124
126
static void substitute_phv_relids (Node * node ,
125
127
int varno , Relids subrelids );
126
128
static void fix_append_rel_relids (List * append_rel_list , int varno ,
@@ -2957,9 +2959,12 @@ reduce_outer_joins_pass2(Node *jtnode,
2957
2959
* and not remove the RTE_RESULT: there is noplace else to evaluate the
2958
2960
* PlaceHolderVar. (That is, in such cases the RTE_RESULT *does* have output
2959
2961
* columns.) But if the RTE_RESULT is an immediate child of an inner join,
2960
- * we can change the PlaceHolderVar's phrels so as to evaluate it at the
2961
- * inner join instead. This is OK because we really only care that PHVs are
2962
- * evaluated above or below the correct outer joins.
2962
+ * we can usually change the PlaceHolderVar's phrels so as to evaluate it at
2963
+ * the inner join instead. This is OK because we really only care that PHVs
2964
+ * are evaluated above or below the correct outer joins. We can't, however,
2965
+ * postpone the evaluation of a PHV to above where it is used; so there are
2966
+ * some checks below on whether output PHVs are laterally referenced in the
2967
+ * other join input rel(s).
2963
2968
*
2964
2969
* We used to try to do this work as part of pull_up_subqueries() where the
2965
2970
* potentially-optimizable cases get introduced; but it's way simpler, and
@@ -3021,8 +3026,11 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
3021
3026
/*
3022
3027
* We can drop RTE_RESULT rels from the fromlist so long as at least
3023
3028
* one child remains, since joining to a one-row table changes
3024
- * nothing. The easiest way to mechanize this rule is to modify the
3025
- * list in-place.
3029
+ * nothing. (But we can't drop a RTE_RESULT that computes PHV(s) that
3030
+ * are needed by some sibling. The cleanup transformation below would
3031
+ * reassign the PHVs to be computed at the join, which is too late for
3032
+ * the sibling's use.) The easiest way to mechanize this rule is to
3033
+ * modify the list in-place.
3026
3034
*/
3027
3035
foreach (cell , f -> fromlist )
3028
3036
{
@@ -3035,12 +3043,14 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
3035
3043
lfirst (cell ) = child ;
3036
3044
3037
3045
/*
3038
- * If it's an RTE_RESULT with at least one sibling, we can drop
3039
- * it. We don't yet know what the inner join's final relid set
3040
- * will be, so postpone cleanup of PHVs etc till after this loop.
3046
+ * If it's an RTE_RESULT with at least one sibling, and no sibling
3047
+ * references dependent PHVs, we can drop it. We don't yet know
3048
+ * what the inner join's final relid set will be, so postpone
3049
+ * cleanup of PHVs etc till after this loop.
3041
3050
*/
3042
3051
if (list_length (f -> fromlist ) > 1 &&
3043
- (varno = get_result_relid (root , child )) != 0 )
3052
+ (varno = get_result_relid (root , child )) != 0 &&
3053
+ !find_dependent_phvs_in_jointree (root , (Node * ) f , varno ))
3044
3054
{
3045
3055
f -> fromlist = foreach_delete_current (f -> fromlist , cell );
3046
3056
result_relids = bms_add_member (result_relids , varno );
@@ -3091,8 +3101,18 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
3091
3101
* the join with a FromExpr with just the other side; and if
3092
3102
* the qual is empty (JOIN ON TRUE) then we can omit the
3093
3103
* FromExpr as well.
3104
+ *
3105
+ * Just as in the FromExpr case, we can't simplify if the
3106
+ * other input rel references any PHVs that are marked as to
3107
+ * be evaluated at the RTE_RESULT rel, because we can't
3108
+ * postpone their evaluation in that case. But we only have
3109
+ * to check this in cases where it's syntactically legal for
3110
+ * the other input to have a LATERAL reference to the
3111
+ * RTE_RESULT rel. Only RHSes of inner and left joins are
3112
+ * allowed to have such refs.
3094
3113
*/
3095
- if ((varno = get_result_relid (root , j -> larg )) != 0 )
3114
+ if ((varno = get_result_relid (root , j -> larg )) != 0 &&
3115
+ !find_dependent_phvs_in_jointree (root , j -> rarg , varno ))
3096
3116
{
3097
3117
remove_result_refs (root , varno , j -> rarg );
3098
3118
if (j -> quals )
@@ -3121,6 +3141,8 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
3121
3141
* strength-reduced to a plain inner join, since each LHS row
3122
3142
* necessarily has exactly one join partner. So we can always
3123
3143
* discard the RHS, much as in the JOIN_INNER case above.
3144
+ * (Again, the LHS could not contain a lateral reference to
3145
+ * the RHS.)
3124
3146
*
3125
3147
* Otherwise, it's still true that each LHS row should be
3126
3148
* returned exactly once, and since the RHS returns no columns
@@ -3131,7 +3153,7 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
3131
3153
*/
3132
3154
if ((varno = get_result_relid (root , j -> rarg )) != 0 &&
3133
3155
(j -> quals == NULL ||
3134
- !find_dependent_phvs (( Node * ) root -> parse , varno )))
3156
+ !find_dependent_phvs (root , varno )))
3135
3157
{
3136
3158
remove_result_refs (root , varno , j -> larg );
3137
3159
jtnode = j -> larg ;
@@ -3141,7 +3163,7 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
3141
3163
/* Mirror-image of the JOIN_LEFT case */
3142
3164
if ((varno = get_result_relid (root , j -> larg )) != 0 &&
3143
3165
(j -> quals == NULL ||
3144
- !find_dependent_phvs (( Node * ) root -> parse , varno )))
3166
+ !find_dependent_phvs (root , varno )))
3145
3167
{
3146
3168
remove_result_refs (root , varno , j -> rarg );
3147
3169
jtnode = j -> rarg ;
@@ -3162,7 +3184,7 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode)
3162
3184
*/
3163
3185
if ((varno = get_result_relid (root , j -> rarg )) != 0 )
3164
3186
{
3165
- Assert (!find_dependent_phvs (( Node * ) root -> parse , varno ));
3187
+ Assert (!find_dependent_phvs (root , varno ));
3166
3188
remove_result_refs (root , varno , j -> larg );
3167
3189
if (j -> quals )
3168
3190
jtnode = (Node * )
@@ -3246,6 +3268,12 @@ remove_result_refs(PlannerInfo *root, int varno, Node *newjtloc)
3246
3268
/*
3247
3269
* find_dependent_phvs - are there any PlaceHolderVars whose relids are
3248
3270
* exactly the given varno?
3271
+ *
3272
+ * find_dependent_phvs should be used when we want to see if there are
3273
+ * any such PHVs anywhere in the Query. Another use-case is to see if
3274
+ * a subtree of the join tree contains such PHVs; but for that, we have
3275
+ * to look not only at the join tree nodes themselves but at the
3276
+ * referenced RTEs. For that, use find_dependent_phvs_in_jointree.
3249
3277
*/
3250
3278
3251
3279
typedef struct
@@ -3292,20 +3320,65 @@ find_dependent_phvs_walker(Node *node,
3292
3320
}
3293
3321
3294
3322
static bool
3295
- find_dependent_phvs (Node * node , int varno )
3323
+ find_dependent_phvs (PlannerInfo * root , int varno )
3324
+ {
3325
+ find_dependent_phvs_context context ;
3326
+
3327
+ /* If there are no PHVs anywhere, we needn't work hard */
3328
+ if (root -> glob -> lastPHId == 0 )
3329
+ return false;
3330
+
3331
+ context .relids = bms_make_singleton (varno );
3332
+ context .sublevels_up = 0 ;
3333
+
3334
+ return query_tree_walker (root -> parse ,
3335
+ find_dependent_phvs_walker ,
3336
+ (void * ) & context ,
3337
+ 0 );
3338
+ }
3339
+
3340
+ static bool
3341
+ find_dependent_phvs_in_jointree (PlannerInfo * root , Node * node , int varno )
3296
3342
{
3297
3343
find_dependent_phvs_context context ;
3344
+ Relids subrelids ;
3345
+ int relid ;
3346
+
3347
+ /* If there are no PHVs anywhere, we needn't work hard */
3348
+ if (root -> glob -> lastPHId == 0 )
3349
+ return false;
3298
3350
3299
3351
context .relids = bms_make_singleton (varno );
3300
3352
context .sublevels_up = 0 ;
3301
3353
3302
3354
/*
3303
- * Must be prepared to start with a Query or a bare expression tree.
3355
+ * See if the jointree fragment itself contains references (in join quals)
3356
+ */
3357
+ if (find_dependent_phvs_walker (node , & context ))
3358
+ return true;
3359
+
3360
+ /*
3361
+ * Otherwise, identify the set of referenced RTEs (we can ignore joins,
3362
+ * since they should be flattened already, so their join alias lists no
3363
+ * longer matter), and tediously check each RTE. We can ignore RTEs that
3364
+ * are not marked LATERAL, though, since they couldn't possibly contain
3365
+ * any cross-references to other RTEs.
3304
3366
*/
3305
- return query_or_expression_tree_walker (node ,
3306
- find_dependent_phvs_walker ,
3307
- (void * ) & context ,
3308
- 0 );
3367
+ subrelids = get_relids_in_jointree (node , false);
3368
+ relid = -1 ;
3369
+ while ((relid = bms_next_member (subrelids , relid )) >= 0 )
3370
+ {
3371
+ RangeTblEntry * rte = rt_fetch (relid , root -> parse -> rtable );
3372
+
3373
+ if (rte -> lateral &&
3374
+ range_table_entry_walker (rte ,
3375
+ find_dependent_phvs_walker ,
3376
+ (void * ) & context ,
3377
+ 0 ))
3378
+ return true;
3379
+ }
3380
+
3381
+ return false;
3309
3382
}
3310
3383
3311
3384
/*
0 commit comments