Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * placeholder.c
4 : : * PlaceHolderVar and PlaceHolderInfo manipulation routines
5 : : *
6 : : *
7 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : *
11 : : * IDENTIFICATION
12 : : * src/backend/optimizer/util/placeholder.c
13 : : *
14 : : *-------------------------------------------------------------------------
15 : : */
16 : : #include "postgres.h"
17 : :
18 : : #include "nodes/nodeFuncs.h"
19 : : #include "optimizer/cost.h"
20 : : #include "optimizer/optimizer.h"
21 : : #include "optimizer/pathnode.h"
22 : : #include "optimizer/placeholder.h"
23 : : #include "optimizer/planmain.h"
24 : : #include "utils/lsyscache.h"
25 : :
26 : :
27 : : typedef struct contain_placeholder_references_context
28 : : {
29 : : int relid;
30 : : int sublevels_up;
31 : : } contain_placeholder_references_context;
32 : :
33 : : /* Local functions */
34 : : static void find_placeholders_recurse(PlannerInfo *root, Node *jtnode);
35 : : static void find_placeholders_in_expr(PlannerInfo *root, Node *expr);
36 : : static bool contain_placeholder_references_walker(Node *node,
37 : : contain_placeholder_references_context *context);
38 : :
39 : :
40 : : /*
41 : : * make_placeholder_expr
42 : : * Make a PlaceHolderVar for the given expression.
43 : : *
44 : : * phrels is the syntactic location (as a set of relids) to attribute
45 : : * to the expression.
46 : : *
47 : : * The caller is responsible for adjusting phlevelsup and phnullingrels
48 : : * as needed. Because we do not know here which query level the PHV
49 : : * will be associated with, it's important that this function touches
50 : : * only root->glob; messing with other parts of PlannerInfo would be
51 : : * likely to do the wrong thing.
52 : : */
53 : : PlaceHolderVar *
6164 tgl@sss.pgh.pa.us 54 :CBC 1292 : make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels)
55 : : {
56 : 1292 : PlaceHolderVar *phv = makeNode(PlaceHolderVar);
57 : :
58 : 1292 : phv->phexpr = expr;
59 : 1292 : phv->phrels = phrels;
950 60 : 1292 : phv->phnullingrels = NULL; /* caller may change this later */
6164 61 : 1292 : phv->phid = ++(root->glob->lastPHId);
950 62 : 1292 : phv->phlevelsup = 0; /* caller may change this later */
63 : :
6164 64 : 1292 : return phv;
65 : : }
66 : :
67 : : /*
68 : : * find_placeholder_info
69 : : * Fetch the PlaceHolderInfo for the given PHV
70 : : *
71 : : * If the PlaceHolderInfo doesn't exist yet, create it if we haven't yet
72 : : * frozen the set of PlaceHolderInfos for the query; else throw an error.
73 : : *
74 : : * This is separate from make_placeholder_expr because subquery pullup has
75 : : * to make PlaceHolderVars for expressions that might not be used at all in
76 : : * the upper query, or might not remain after const-expression simplification.
77 : : * We build PlaceHolderInfos only for PHVs that are still present in the
78 : : * simplified query passed to query_planner().
79 : : *
80 : : * Note: this should only be called after query_planner() has started.
81 : : */
82 : : PlaceHolderInfo *
1116 83 : 4733 : find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv)
84 : : {
85 : : PlaceHolderInfo *phinfo;
86 : : Relids rels_used;
87 : :
88 : : /* if this ever isn't true, we'd need to be able to look in parent lists */
6164 89 [ - + ]: 4733 : Assert(phv->phlevelsup == 0);
90 : :
91 : : /* Use placeholder_array to look up existing PlaceHolderInfo quickly */
1116 92 [ + + ]: 4733 : if (phv->phid < root->placeholder_array_size)
93 : 4062 : phinfo = root->placeholder_array[phv->phid];
94 : : else
95 : 671 : phinfo = NULL;
96 [ + + ]: 4733 : if (phinfo != NULL)
97 : : {
98 [ - + ]: 3684 : Assert(phinfo->phid == phv->phid);
99 : 3684 : return phinfo;
100 : : }
101 : :
102 : : /* Not found, so create it */
103 [ - + ]: 1049 : if (root->placeholdersFrozen)
5142 tgl@sss.pgh.pa.us 104 [ # # ]:UBC 0 : elog(ERROR, "too late to create a new PlaceHolderInfo");
105 : :
6163 tgl@sss.pgh.pa.us 106 :CBC 1049 : phinfo = makeNode(PlaceHolderInfo);
107 : :
108 : 1049 : phinfo->phid = phv->phid;
109 : 1049 : phinfo->ph_var = copyObject(phv);
110 : :
111 : : /*
112 : : * By convention, phinfo->ph_var->phnullingrels is always empty, since the
113 : : * PlaceHolderInfo represents the initially-calculated state of the
114 : : * PlaceHolderVar. PlaceHolderVars appearing in the query tree might have
115 : : * varying values of phnullingrels, reflecting outer joins applied above
116 : : * the calculation level.
117 : : */
950 118 : 1049 : phinfo->ph_var->phnullingrels = NULL;
119 : :
120 : : /*
121 : : * Any referenced rels that are outside the PHV's syntactic scope are
122 : : * LATERAL references, which should be included in ph_lateral but not in
123 : : * ph_eval_at. If no referenced rels are within the syntactic scope,
124 : : * force evaluation at the syntactic location.
125 : : */
1689 126 : 1049 : rels_used = pull_varnos(root, (Node *) phv->phexpr);
4403 127 : 1049 : phinfo->ph_lateral = bms_difference(rels_used, phv->phrels);
128 : 1049 : phinfo->ph_eval_at = bms_int_members(rels_used, phv->phrels);
129 : : /* If no contained vars, force evaluation at syntactic location */
130 [ + + ]: 1049 : if (bms_is_empty(phinfo->ph_eval_at))
131 : : {
132 : 539 : phinfo->ph_eval_at = bms_copy(phv->phrels);
133 [ - + ]: 539 : Assert(!bms_is_empty(phinfo->ph_eval_at));
134 : : }
5931 bruce@momjian.us 135 : 1049 : phinfo->ph_needed = NULL; /* initially it's unused */
136 : : /* for the moment, estimate width using just the datatype info */
6163 tgl@sss.pgh.pa.us 137 : 1049 : phinfo->ph_width = get_typavgwidth(exprType((Node *) phv->phexpr),
138 : 1049 : exprTypmod((Node *) phv->phexpr));
139 : :
140 : : /*
141 : : * Add to both placeholder_list and placeholder_array. Note: because we
142 : : * store pointers to the PlaceHolderInfos in two data structures, it'd be
143 : : * unsafe to pass the whole placeholder_list structure through
144 : : * expression_tree_mutator or the like --- or at least, you'd have to
145 : : * rebuild the placeholder_array afterwards.
146 : : */
147 : 1049 : root->placeholder_list = lappend(root->placeholder_list, phinfo);
148 : :
1116 149 [ + + ]: 1049 : if (phinfo->phid >= root->placeholder_array_size)
150 : : {
151 : : /* Must allocate or enlarge placeholder_array */
152 : : int new_size;
153 : :
154 [ - + ]: 671 : new_size = root->placeholder_array_size ? root->placeholder_array_size * 2 : 8;
155 [ - + ]: 671 : while (phinfo->phid >= new_size)
1116 tgl@sss.pgh.pa.us 156 :UBC 0 : new_size *= 2;
1116 tgl@sss.pgh.pa.us 157 [ - + ]:CBC 671 : if (root->placeholder_array)
1029 peter@eisentraut.org 158 :UBC 0 : root->placeholder_array =
159 : 0 : repalloc0_array(root->placeholder_array, PlaceHolderInfo *, root->placeholder_array_size, new_size);
160 : : else
1029 peter@eisentraut.org 161 :CBC 671 : root->placeholder_array =
162 : 671 : palloc0_array(PlaceHolderInfo *, new_size);
1116 tgl@sss.pgh.pa.us 163 : 671 : root->placeholder_array_size = new_size;
164 : : }
165 : 1049 : root->placeholder_array[phinfo->phid] = phinfo;
166 : :
167 : : /*
168 : : * The PHV's contained expression may contain other, lower-level PHVs. We
169 : : * now know we need to get those into the PlaceHolderInfo list, too, so we
170 : : * may as well do that immediately.
171 : : */
4406 172 : 1049 : find_placeholders_in_expr(root, (Node *) phinfo->ph_var->phexpr);
173 : :
6163 174 : 1049 : return phinfo;
175 : : }
176 : :
177 : : /*
178 : : * find_placeholders_in_jointree
179 : : * Search the jointree for PlaceHolderVars, and build PlaceHolderInfos
180 : : *
181 : : * We don't need to look at the targetlist because build_base_rel_tlists()
182 : : * will already have made entries for any PHVs in the tlist.
183 : : */
184 : : void
4922 185 : 159353 : find_placeholders_in_jointree(PlannerInfo *root)
186 : : {
187 : : /* This must be done before freezing the set of PHIs */
1116 188 [ - + ]: 159353 : Assert(!root->placeholdersFrozen);
189 : :
190 : : /* We need do nothing if the query contains no PlaceHolderVars */
5457 191 [ + + ]: 159353 : if (root->glob->lastPHId != 0)
192 : : {
193 : : /* Start recursion at top of jointree */
194 [ + - - + ]: 904 : Assert(root->parse->jointree != NULL &&
195 : : IsA(root->parse->jointree, FromExpr));
4406 196 : 904 : find_placeholders_recurse(root, (Node *) root->parse->jointree);
197 : : }
5457 198 : 159353 : }
199 : :
200 : : /*
201 : : * find_placeholders_recurse
202 : : * One recursion level of find_placeholders_in_jointree.
203 : : *
204 : : * jtnode is the current jointree node to examine.
205 : : */
206 : : static void
207 : 4376 : find_placeholders_recurse(PlannerInfo *root, Node *jtnode)
208 : : {
209 [ - + ]: 4376 : if (jtnode == NULL)
4406 tgl@sss.pgh.pa.us 210 :UBC 0 : return;
5457 tgl@sss.pgh.pa.us 211 [ + + ]:CBC 4376 : if (IsA(jtnode, RangeTblRef))
212 : : {
213 : : /* No quals to deal with here */
214 : : }
215 [ + + ]: 2213 : else if (IsA(jtnode, FromExpr))
216 : : {
217 : 1088 : FromExpr *f = (FromExpr *) jtnode;
218 : : ListCell *l;
219 : :
220 : : /*
221 : : * First, recurse to handle child joins.
222 : : */
223 [ + - + + : 2310 : foreach(l, f->fromlist)
+ + ]
224 : : {
4406 225 : 1222 : find_placeholders_recurse(root, lfirst(l));
226 : : }
227 : :
228 : : /*
229 : : * Now process the top-level quals.
230 : : */
231 : 1088 : find_placeholders_in_expr(root, f->quals);
232 : : }
5457 233 [ + - ]: 1125 : else if (IsA(jtnode, JoinExpr))
234 : : {
235 : 1125 : JoinExpr *j = (JoinExpr *) jtnode;
236 : :
237 : : /*
238 : : * First, recurse to handle child joins.
239 : : */
4406 240 : 1125 : find_placeholders_recurse(root, j->larg);
241 : 1125 : find_placeholders_recurse(root, j->rarg);
242 : :
243 : : /* Process the qual clauses */
244 : 1125 : find_placeholders_in_expr(root, j->quals);
245 : : }
246 : : else
5457 tgl@sss.pgh.pa.us 247 [ # # ]:UBC 0 : elog(ERROR, "unrecognized node type: %d",
248 : : (int) nodeTag(jtnode));
249 : : }
250 : :
251 : : /*
252 : : * find_placeholders_in_expr
253 : : * Find all PlaceHolderVars in the given expression, and create
254 : : * PlaceHolderInfo entries for them.
255 : : */
256 : : static void
4406 tgl@sss.pgh.pa.us 257 :CBC 3262 : find_placeholders_in_expr(PlannerInfo *root, Node *expr)
258 : : {
259 : : List *vars;
260 : : ListCell *vl;
261 : :
262 : : /*
263 : : * pull_var_clause does more than we need here, but it'll do and it's
264 : : * convenient to use.
265 : : */
5142 266 : 3262 : vars = pull_var_clause(expr,
267 : : PVC_RECURSE_AGGREGATES |
268 : : PVC_RECURSE_WINDOWFUNCS |
269 : : PVC_INCLUDE_PLACEHOLDERS);
5457 270 [ + + + + : 7156 : foreach(vl, vars)
+ + ]
271 : : {
272 : 3894 : PlaceHolderVar *phv = (PlaceHolderVar *) lfirst(vl);
273 : :
274 : : /* Ignore any plain Vars */
275 [ + + ]: 3894 : if (!IsA(phv, PlaceHolderVar))
276 : 3472 : continue;
277 : :
278 : : /* Create a PlaceHolderInfo entry if there's not one already */
1116 279 : 422 : (void) find_placeholder_info(root, phv);
280 : : }
5457 281 : 3262 : list_free(vars);
282 : 3262 : }
283 : :
284 : : /*
285 : : * fix_placeholder_input_needed_levels
286 : : * Adjust the "needed at" levels for placeholder inputs
287 : : *
288 : : * This is called after we've finished determining the eval_at levels for
289 : : * all placeholders. We need to make sure that all vars and placeholders
290 : : * needed to evaluate each placeholder will be available at the scan or join
291 : : * level where the evaluation will be done. (It might seem that scan-level
292 : : * evaluations aren't interesting, but that's not so: a LATERAL reference
293 : : * within a placeholder's expression needs to cause the referenced var or
294 : : * placeholder to be marked as needed in the scan where it's evaluated.)
295 : : * Note that this loop can have side-effects on the ph_needed sets of other
296 : : * PlaceHolderInfos; that's okay because we don't examine ph_needed here, so
297 : : * there are no ordering issues to worry about.
298 : : */
299 : : void
300 : 159353 : fix_placeholder_input_needed_levels(PlannerInfo *root)
301 : : {
302 : : ListCell *lc;
303 : :
304 [ + + + + : 160402 : foreach(lc, root->placeholder_list)
+ + ]
305 : : {
306 : 1049 : PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
4403 307 : 1049 : List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
308 : : PVC_RECURSE_AGGREGATES |
309 : : PVC_RECURSE_WINDOWFUNCS |
310 : : PVC_INCLUDE_PLACEHOLDERS);
311 : :
1116 312 : 1049 : add_vars_to_targetlist(root, vars, phinfo->ph_eval_at);
4403 313 : 1049 : list_free(vars);
314 : : }
5641 315 : 159353 : }
316 : :
317 : : /*
318 : : * rebuild_placeholder_attr_needed
319 : : * Put back attr_needed bits for Vars/PHVs needed in PlaceHolderVars.
320 : : *
321 : : * This is used to rebuild attr_needed/ph_needed sets after removal of a
322 : : * useless outer join. It should match what
323 : : * fix_placeholder_input_needed_levels did, except that we call
324 : : * add_vars_to_attr_needed not add_vars_to_targetlist.
325 : : */
326 : : void
344 327 : 5587 : rebuild_placeholder_attr_needed(PlannerInfo *root)
328 : : {
329 : : ListCell *lc;
330 : :
331 [ + + + + : 5694 : foreach(lc, root->placeholder_list)
+ + ]
332 : : {
333 : 107 : PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
334 : 107 : List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
335 : : PVC_RECURSE_AGGREGATES |
336 : : PVC_RECURSE_WINDOWFUNCS |
337 : : PVC_INCLUDE_PLACEHOLDERS);
338 : :
339 : 107 : add_vars_to_attr_needed(root, vars, phinfo->ph_eval_at);
340 : 107 : list_free(vars);
341 : : }
342 : 5587 : }
343 : :
344 : : /*
345 : : * add_placeholders_to_base_rels
346 : : * Add any required PlaceHolderVars to base rels' targetlists.
347 : : *
348 : : * If any placeholder can be computed at a base rel and is needed above it,
349 : : * add it to that rel's targetlist. This might look like it could be merged
350 : : * with fix_placeholder_input_needed_levels, but it must be separate because
351 : : * join removal happens in between, and can change the ph_eval_at sets. There
352 : : * is essentially the same logic in add_placeholders_to_joinrel, but we can't
353 : : * do that part until joinrels are formed.
354 : : */
355 : : void
5641 356 : 159353 : add_placeholders_to_base_rels(PlannerInfo *root)
357 : : {
358 : : ListCell *lc;
359 : :
360 [ + + + + : 160399 : foreach(lc, root->placeholder_list)
+ + ]
361 : : {
362 : 1046 : PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
6163 363 : 1046 : Relids eval_at = phinfo->ph_eval_at;
364 : : int varno;
365 : :
3557 366 [ + + + + ]: 1884 : if (bms_get_singleton_member(eval_at, &varno) &&
367 : 838 : bms_nonempty_difference(phinfo->ph_needed, eval_at))
368 : : {
6164 369 : 805 : RelOptInfo *rel = find_base_rel(root, varno);
370 : :
371 : : /*
372 : : * As in add_vars_to_targetlist(), a value computed at scan level
373 : : * has not yet been nulled by any outer join, so its phnullingrels
374 : : * should be empty.
375 : : */
950 376 [ - + ]: 805 : Assert(phinfo->ph_var->phnullingrels == NULL);
377 : :
378 : : /* Copying the PHV might be unnecessary here, but be safe */
3463 379 : 805 : rel->reltarget->exprs = lappend(rel->reltarget->exprs,
380 : 805 : copyObject(phinfo->ph_var));
381 : : /* reltarget's cost and width fields will be updated later */
382 : : }
383 : : }
6164 384 : 159353 : }
385 : :
386 : : /*
387 : : * add_placeholders_to_joinrel
388 : : * Add any newly-computable PlaceHolderVars to a join rel's targetlist;
389 : : * and if computable PHVs contain lateral references, add those
390 : : * references to the joinrel's direct_lateral_relids.
391 : : *
392 : : * A join rel should emit a PlaceHolderVar if (a) the PHV can be computed
393 : : * at or below this join level and (b) the PHV is needed above this level.
394 : : * Our caller build_join_rel() has already added any PHVs that were computed
395 : : * in either join input rel, so we need add only newly-computable ones to
396 : : * the targetlist. However, direct_lateral_relids must be updated for every
397 : : * PHV computable at or below this join, as explained below.
398 : : */
399 : : void
3488 400 : 101321 : add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
401 : : RelOptInfo *outer_rel, RelOptInfo *inner_rel,
402 : : SpecialJoinInfo *sjinfo)
403 : : {
6164 404 : 101321 : Relids relids = joinrel->relids;
627 405 : 101321 : int64 tuple_width = joinrel->reltarget->width;
406 : : ListCell *lc;
407 : :
6164 408 [ + + + + : 103515 : foreach(lc, root->placeholder_list)
+ + ]
409 : : {
410 : 2194 : PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
411 : :
412 : : /* Is it computable here? */
1741 413 [ + + ]: 2194 : if (bms_is_subset(phinfo->ph_eval_at, relids))
414 : : {
415 : : /* Is it still needed above this joinrel? */
416 [ + + ]: 1506 : if (bms_nonempty_difference(phinfo->ph_needed, relids))
417 : : {
418 : : /*
419 : : * Yes, but only add to tlist if it wasn't computed in either
420 : : * input; otherwise it should be there already. Also, we
421 : : * charge the cost of evaluating the contained expression if
422 : : * the PHV can be computed here but not in either input. This
423 : : * is a bit bogus because we make the decision based on the
424 : : * first pair of possible input relations considered for the
425 : : * joinrel. With other pairs, it might be possible to compute
426 : : * the PHV in one input or the other, and then we'd be double
427 : : * charging the PHV's cost for some join paths. For now, live
428 : : * with that; but we might want to improve it later by
429 : : * refiguring the reltarget costs for each pair of inputs.
430 : : */
3488 431 [ + + ]: 1015 : if (!bms_is_subset(phinfo->ph_eval_at, outer_rel->relids) &&
432 [ + + ]: 762 : !bms_is_subset(phinfo->ph_eval_at, inner_rel->relids))
433 : : {
434 : : /* Copying might be unnecessary here, but be safe */
950 435 : 245 : PlaceHolderVar *phv = copyObject(phinfo->ph_var);
436 : : QualCost cost;
437 : :
438 : : /*
439 : : * It'll start out not nulled by anything. Joins above
440 : : * this one might add to its phnullingrels later, in much
441 : : * the same way as for Vars.
442 : : */
443 [ - + ]: 245 : Assert(phv->phnullingrels == NULL);
444 : :
1116 445 : 245 : joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs,
446 : : phv);
447 : 245 : cost_qual_eval_node(&cost, (Node *) phv->phexpr, root);
3463 448 : 245 : joinrel->reltarget->cost.startup += cost.startup;
449 : 245 : joinrel->reltarget->cost.per_tuple += cost.per_tuple;
627 450 : 245 : tuple_width += phinfo->ph_width;
451 : : }
452 : : }
453 : :
454 : : /*
455 : : * Also adjust joinrel's direct_lateral_relids to include the
456 : : * PHV's source rel(s). We must do this even if we're not
457 : : * actually going to emit the PHV, otherwise join_is_legal() will
458 : : * reject valid join orderings. (In principle maybe we could
459 : : * instead remove the joinrel's lateral_relids dependency; but
460 : : * that's complicated to get right, and cases where we're not
461 : : * going to emit the PHV are too rare to justify the work.)
462 : : *
463 : : * In principle we should only do this if the join doesn't yet
464 : : * include the PHV's source rel(s). But our caller
465 : : * build_join_rel() will clean things up by removing the join's
466 : : * own relids from its direct_lateral_relids, so we needn't
467 : : * account for that here.
468 : : */
1741 469 : 1506 : joinrel->direct_lateral_relids =
470 : 1506 : bms_add_members(joinrel->direct_lateral_relids,
471 : 1506 : phinfo->ph_lateral);
472 : : }
473 : : }
474 : :
627 475 : 101321 : joinrel->reltarget->width = clamp_width_est(tuple_width);
6164 476 : 101321 : }
477 : :
478 : : /*
479 : : * contain_placeholder_references_to
480 : : * Detect whether any PlaceHolderVars in the given clause contain
481 : : * references to the given relid (typically an OJ relid).
482 : : *
483 : : * "Contain" means that there's a use of the relid inside the PHV's
484 : : * contained expression, so that changing the nullability status of
485 : : * the rel might change what the PHV computes.
486 : : *
487 : : * The code here to cope with upper-level PHVs is likely dead, but keep it
488 : : * anyway just in case.
489 : : */
490 : : bool
950 491 : 8527 : contain_placeholder_references_to(PlannerInfo *root, Node *clause,
492 : : int relid)
493 : : {
494 : : contain_placeholder_references_context context;
495 : :
496 : : /* We can answer quickly in the common case that there's no PHVs at all */
497 [ + + ]: 8527 : if (root->glob->lastPHId == 0)
498 : 8211 : return false;
499 : : /* Else run the recursive search */
500 : 316 : context.relid = relid;
501 : 316 : context.sublevels_up = 0;
502 : 316 : return contain_placeholder_references_walker(clause, &context);
503 : : }
504 : :
505 : : static bool
506 : 1075 : contain_placeholder_references_walker(Node *node,
507 : : contain_placeholder_references_context *context)
508 : : {
509 [ + + ]: 1075 : if (node == NULL)
510 : 63 : return false;
511 [ + + ]: 1012 : if (IsA(node, PlaceHolderVar))
512 : : {
513 : 27 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
514 : :
515 : : /* We should just look through PHVs of other query levels */
516 [ + - ]: 27 : if (phv->phlevelsup == context->sublevels_up)
517 : : {
518 : : /* If phrels matches, we found what we came for */
519 [ + + ]: 27 : if (bms_is_member(context->relid, phv->phrels))
520 : 6 : return true;
521 : :
522 : : /*
523 : : * We should not examine phnullingrels: what we are looking for is
524 : : * references in the contained expression, not OJs that might null
525 : : * the result afterwards. Also, we don't need to recurse into the
526 : : * contained expression, because phrels should adequately
527 : : * summarize what's in there. So we're done here.
528 : : */
529 : 21 : return false;
530 : : }
531 : : }
532 [ - + ]: 985 : else if (IsA(node, Query))
533 : : {
534 : : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
535 : : bool result;
536 : :
950 tgl@sss.pgh.pa.us 537 :UBC 0 : context->sublevels_up++;
538 : 0 : result = query_tree_walker((Query *) node,
539 : : contain_placeholder_references_walker,
540 : : context,
541 : : 0);
542 : 0 : context->sublevels_up--;
543 : 0 : return result;
544 : : }
950 tgl@sss.pgh.pa.us 545 :CBC 985 : return expression_tree_walker(node, contain_placeholder_references_walker,
546 : : context);
547 : : }
548 : :
549 : : /*
550 : : * Compute the set of outer-join relids that can null a placeholder.
551 : : *
552 : : * This is analogous to RelOptInfo.nulling_relids for Vars, but we compute it
553 : : * on-the-fly rather than saving it somewhere. Currently the value is needed
554 : : * at most once per query, so there's little value in doing otherwise. If it
555 : : * ever gains more widespread use, perhaps we should cache the result in
556 : : * PlaceHolderInfo.
557 : : */
558 : : Relids
69 559 : 126 : get_placeholder_nulling_relids(PlannerInfo *root, PlaceHolderInfo *phinfo)
560 : : {
561 : 126 : Relids result = NULL;
562 : 126 : int relid = -1;
563 : :
564 : : /*
565 : : * Form the union of all potential nulling OJs for each baserel included
566 : : * in ph_eval_at.
567 : : */
568 [ + + ]: 288 : while ((relid = bms_next_member(phinfo->ph_eval_at, relid)) > 0)
569 : : {
570 : 162 : RelOptInfo *rel = root->simple_rel_array[relid];
571 : :
572 : : /* ignore the RTE_GROUP RTE */
573 [ - + ]: 162 : if (relid == root->group_rtindex)
69 tgl@sss.pgh.pa.us 574 :UBC 0 : continue;
575 : :
69 tgl@sss.pgh.pa.us 576 [ + + ]:CBC 162 : if (rel == NULL) /* must be an outer join */
577 : : {
578 [ - + ]: 6 : Assert(bms_is_member(relid, root->outer_join_rels));
579 : 6 : continue;
580 : : }
581 : 156 : result = bms_add_members(result, rel->nulling_relids);
582 : : }
583 : :
584 : : /* Now remove any OJs already included in ph_eval_at, and we're done. */
585 : 126 : result = bms_del_members(result, phinfo->ph_eval_at);
586 : 126 : return result;
587 : : }
|