1 /*-------------------------------------------------------------------------
2 *
3 * paramassign.c
4 * Functions for assigning PARAM_EXEC slots during planning.
5 *
6 * This module is responsible for managing three planner data structures:
7 *
8 * root->glob->nParamExec: counts actual assignments of PARAM_EXEC slots.
9 * This counter is global to the whole plan since the executor has only one
10 * PARAM_EXEC array. Assignments are permanent for the plan: we never
11 * de-assign a slot once added.
12 *
13 * root->plan_params: a list of PlannerParamItem nodes, recording Vars and
14 * PlaceHolderVars that the root's query level needs to supply to lower-level
15 * subqueries, along with the PARAM_EXEC number to use for each such value.
16 * Elements are added to this list while planning a subquery, and the list
17 * is reset to empty after completion of each subquery.
18 *
19 * root->curOuterParams: a list of NestLoopParam nodes, recording Vars and
20 * PlaceHolderVars that some outer level of nestloop needs to pass down to
21 * a lower-level plan node in its righthand side. Elements are added to this
22 * list as createplan.c creates lower Plan nodes that need such Params, and
23 * are removed when it creates a NestLoop Plan node that will supply those
24 * values.
25 *
26 * The latter two data structures are used to prevent creating multiple
27 * PARAM_EXEC slots (each requiring work to fill) when the same upper
28 * SubPlan or NestLoop supplies a value that is referenced in more than
29 * one place in its child plan nodes. However, when the same Var has to
30 * be supplied to different subplan trees by different SubPlan or NestLoop
31 * parent nodes, we don't recognize any commonality; a fresh plan_params or
32 * curOuterParams entry will be made (since the old one has been removed
33 * when we finished processing the earlier SubPlan or NestLoop) and a fresh
34 * PARAM_EXEC number will be assigned. At one time we tried to avoid
35 * allocating duplicate PARAM_EXEC numbers in such cases, but it's harder
36 * than it seems to avoid bugs due to overlapping Param lifetimes, so we
37 * don't risk that anymore. Minimizing the number of PARAM_EXEC slots
38 * doesn't really save much executor work anyway.
39 *
40 *
41 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
42 * Portions Copyright (c) 1994, Regents of the University of California
43 *
44 * IDENTIFICATION
45 * src/backend/optimizer/util/paramassign.c
46 *
47 *-------------------------------------------------------------------------
48 */
49 #include "postgres.h"
50
51 #include "nodes/nodeFuncs.h"
52 #include "nodes/plannodes.h"
53 #include "optimizer/paramassign.h"
54 #include "optimizer/placeholder.h"
55 #include "rewrite/rewriteManip.h"
56
57
58 /*
59 * Select a PARAM_EXEC number to identify the given Var as a parameter for
60 * the current subquery. (It might already have one.)
61 * Record the need for the Var in the proper upper-level root->plan_params.
62 */
63 static int
assign_param_for_var(PlannerInfo * root,Var * var)64 assign_param_for_var(PlannerInfo *root, Var *var)
65 {
66 ListCell *ppl;
67 PlannerParamItem *pitem;
68 Index levelsup;
69
70 /* Find the query level the Var belongs to */
71 for (levelsup = var->varlevelsup; levelsup > 0; levelsup--)
72 root = root->parent_root;
73
74 /* If there's already a matching PlannerParamItem there, just use it */
75 foreach(ppl, root->plan_params)
76 {
77 pitem = (PlannerParamItem *) lfirst(ppl);
78 if (IsA(pitem->item, Var))
79 {
80 Var *pvar = (Var *) pitem->item;
81
82 /*
83 * This comparison must match _equalVar(), except for ignoring
84 * varlevelsup. Note that _equalVar() ignores the location.
85 */
86 if (pvar->varno == var->varno &&
87 pvar->varattno == var->varattno &&
88 pvar->vartype == var->vartype &&
89 pvar->vartypmod == var->vartypmod &&
90 pvar->varcollid == var->varcollid &&
91 pvar->varnoold == var->varnoold &&
92 pvar->varoattno == var->varoattno)
93 return pitem->paramId;
94 }
95 }
96
97 /* Nope, so make a new one */
98 var = copyObject(var);
99 var->varlevelsup = 0;
100
101 pitem = makeNode(PlannerParamItem);
102 pitem->item = (Node *) var;
103 pitem->paramId = root->glob->nParamExec++;
104
105 root->plan_params = lappend(root->plan_params, pitem);
106
107 return pitem->paramId;
108 }
109
110 /*
111 * Generate a Param node to replace the given Var,
112 * which is expected to have varlevelsup > 0 (ie, it is not local).
113 * Record the need for the Var in the proper upper-level root->plan_params.
114 */
115 Param *
replace_outer_var(PlannerInfo * root,Var * var)116 replace_outer_var(PlannerInfo *root, Var *var)
117 {
118 Param *retval;
119 int i;
120
121 Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);
122
123 /* Find the Var in the appropriate plan_params, or add it if not present */
124 i = assign_param_for_var(root, var);
125
126 retval = makeNode(Param);
127 retval->paramkind = PARAM_EXEC;
128 retval->paramid = i;
129 retval->paramtype = var->vartype;
130 retval->paramtypmod = var->vartypmod;
131 retval->paramcollid = var->varcollid;
132 retval->location = var->location;
133
134 return retval;
135 }
136
137 /*
138 * Select a PARAM_EXEC number to identify the given PlaceHolderVar as a
139 * parameter for the current subquery. (It might already have one.)
140 * Record the need for the PHV in the proper upper-level root->plan_params.
141 *
142 * This is just like assign_param_for_var, except for PlaceHolderVars.
143 */
144 static int
assign_param_for_placeholdervar(PlannerInfo * root,PlaceHolderVar * phv)145 assign_param_for_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
146 {
147 ListCell *ppl;
148 PlannerParamItem *pitem;
149 Index levelsup;
150
151 /* Find the query level the PHV belongs to */
152 for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--)
153 root = root->parent_root;
154
155 /* If there's already a matching PlannerParamItem there, just use it */
156 foreach(ppl, root->plan_params)
157 {
158 pitem = (PlannerParamItem *) lfirst(ppl);
159 if (IsA(pitem->item, PlaceHolderVar))
160 {
161 PlaceHolderVar *pphv = (PlaceHolderVar *) pitem->item;
162
163 /* We assume comparing the PHIDs is sufficient */
164 if (pphv->phid == phv->phid)
165 return pitem->paramId;
166 }
167 }
168
169 /* Nope, so make a new one */
170 phv = copyObject(phv);
171 IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
172 Assert(phv->phlevelsup == 0);
173
174 pitem = makeNode(PlannerParamItem);
175 pitem->item = (Node *) phv;
176 pitem->paramId = root->glob->nParamExec++;
177
178 root->plan_params = lappend(root->plan_params, pitem);
179
180 return pitem->paramId;
181 }
182
183 /*
184 * Generate a Param node to replace the given PlaceHolderVar,
185 * which is expected to have phlevelsup > 0 (ie, it is not local).
186 * Record the need for the PHV in the proper upper-level root->plan_params.
187 *
188 * This is just like replace_outer_var, except for PlaceHolderVars.
189 */
190 Param *
replace_outer_placeholdervar(PlannerInfo * root,PlaceHolderVar * phv)191 replace_outer_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
192 {
193 Param *retval;
194 int i;
195
196 Assert(phv->phlevelsup > 0 && phv->phlevelsup < root->query_level);
197
198 /* Find the PHV in the appropriate plan_params, or add it if not present */
199 i = assign_param_for_placeholdervar(root, phv);
200
201 retval = makeNode(Param);
202 retval->paramkind = PARAM_EXEC;
203 retval->paramid = i;
204 retval->paramtype = exprType((Node *) phv->phexpr);
205 retval->paramtypmod = exprTypmod((Node *) phv->phexpr);
206 retval->paramcollid = exprCollation((Node *) phv->phexpr);
207 retval->location = -1;
208
209 return retval;
210 }
211
212 /*
213 * Generate a Param node to replace the given Aggref
214 * which is expected to have agglevelsup > 0 (ie, it is not local).
215 * Record the need for the Aggref in the proper upper-level root->plan_params.
216 */
217 Param *
replace_outer_agg(PlannerInfo * root,Aggref * agg)218 replace_outer_agg(PlannerInfo *root, Aggref *agg)
219 {
220 Param *retval;
221 PlannerParamItem *pitem;
222 Index levelsup;
223
224 Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level);
225
226 /* Find the query level the Aggref belongs to */
227 for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
228 root = root->parent_root;
229
230 /*
231 * It does not seem worthwhile to try to de-duplicate references to outer
232 * aggs. Just make a new slot every time.
233 */
234 agg = copyObject(agg);
235 IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
236 Assert(agg->agglevelsup == 0);
237
238 pitem = makeNode(PlannerParamItem);
239 pitem->item = (Node *) agg;
240 pitem->paramId = root->glob->nParamExec++;
241
242 root->plan_params = lappend(root->plan_params, pitem);
243
244 retval = makeNode(Param);
245 retval->paramkind = PARAM_EXEC;
246 retval->paramid = pitem->paramId;
247 retval->paramtype = agg->aggtype;
248 retval->paramtypmod = -1;
249 retval->paramcollid = agg->aggcollid;
250 retval->location = agg->location;
251
252 return retval;
253 }
254
255 /*
256 * Generate a Param node to replace the given GroupingFunc expression which is
257 * expected to have agglevelsup > 0 (ie, it is not local).
258 * Record the need for the GroupingFunc in the proper upper-level
259 * root->plan_params.
260 */
261 Param *
replace_outer_grouping(PlannerInfo * root,GroupingFunc * grp)262 replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp)
263 {
264 Param *retval;
265 PlannerParamItem *pitem;
266 Index levelsup;
267 Oid ptype = exprType((Node *) grp);
268
269 Assert(grp->agglevelsup > 0 && grp->agglevelsup < root->query_level);
270
271 /* Find the query level the GroupingFunc belongs to */
272 for (levelsup = grp->agglevelsup; levelsup > 0; levelsup--)
273 root = root->parent_root;
274
275 /*
276 * It does not seem worthwhile to try to de-duplicate references to outer
277 * aggs. Just make a new slot every time.
278 */
279 grp = copyObject(grp);
280 IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0);
281 Assert(grp->agglevelsup == 0);
282
283 pitem = makeNode(PlannerParamItem);
284 pitem->item = (Node *) grp;
285 pitem->paramId = root->glob->nParamExec++;
286
287 root->plan_params = lappend(root->plan_params, pitem);
288
289 retval = makeNode(Param);
290 retval->paramkind = PARAM_EXEC;
291 retval->paramid = pitem->paramId;
292 retval->paramtype = ptype;
293 retval->paramtypmod = -1;
294 retval->paramcollid = InvalidOid;
295 retval->location = grp->location;
296
297 return retval;
298 }
299
300 /*
301 * Generate a Param node to replace the given Var,
302 * which is expected to come from some upper NestLoop plan node.
303 * Record the need for the Var in root->curOuterParams.
304 */
305 Param *
replace_nestloop_param_var(PlannerInfo * root,Var * var)306 replace_nestloop_param_var(PlannerInfo *root, Var *var)
307 {
308 Param *param;
309 NestLoopParam *nlp;
310 ListCell *lc;
311
312 /* Is this Var already listed in root->curOuterParams? */
313 foreach(lc, root->curOuterParams)
314 {
315 nlp = (NestLoopParam *) lfirst(lc);
316 if (equal(var, nlp->paramval))
317 {
318 /* Yes, so just make a Param referencing this NLP's slot */
319 param = makeNode(Param);
320 param->paramkind = PARAM_EXEC;
321 param->paramid = nlp->paramno;
322 param->paramtype = var->vartype;
323 param->paramtypmod = var->vartypmod;
324 param->paramcollid = var->varcollid;
325 param->location = var->location;
326 return param;
327 }
328 }
329
330 /* No, so assign a PARAM_EXEC slot for a new NLP */
331 param = generate_new_exec_param(root,
332 var->vartype,
333 var->vartypmod,
334 var->varcollid);
335 param->location = var->location;
336
337 /* Add it to the list of required NLPs */
338 nlp = makeNode(NestLoopParam);
339 nlp->paramno = param->paramid;
340 nlp->paramval = copyObject(var);
341 root->curOuterParams = lappend(root->curOuterParams, nlp);
342
343 /* And return the replacement Param */
344 return param;
345 }
346
347 /*
348 * Generate a Param node to replace the given PlaceHolderVar,
349 * which is expected to come from some upper NestLoop plan node.
350 * Record the need for the PHV in root->curOuterParams.
351 *
352 * This is just like replace_nestloop_param_var, except for PlaceHolderVars.
353 */
354 Param *
replace_nestloop_param_placeholdervar(PlannerInfo * root,PlaceHolderVar * phv)355 replace_nestloop_param_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
356 {
357 Param *param;
358 NestLoopParam *nlp;
359 ListCell *lc;
360
361 /* Is this PHV already listed in root->curOuterParams? */
362 foreach(lc, root->curOuterParams)
363 {
364 nlp = (NestLoopParam *) lfirst(lc);
365 if (equal(phv, nlp->paramval))
366 {
367 /* Yes, so just make a Param referencing this NLP's slot */
368 param = makeNode(Param);
369 param->paramkind = PARAM_EXEC;
370 param->paramid = nlp->paramno;
371 param->paramtype = exprType((Node *) phv->phexpr);
372 param->paramtypmod = exprTypmod((Node *) phv->phexpr);
373 param->paramcollid = exprCollation((Node *) phv->phexpr);
374 param->location = -1;
375 return param;
376 }
377 }
378
379 /* No, so assign a PARAM_EXEC slot for a new NLP */
380 param = generate_new_exec_param(root,
381 exprType((Node *) phv->phexpr),
382 exprTypmod((Node *) phv->phexpr),
383 exprCollation((Node *) phv->phexpr));
384
385 /* Add it to the list of required NLPs */
386 nlp = makeNode(NestLoopParam);
387 nlp->paramno = param->paramid;
388 nlp->paramval = (Var *) copyObject(phv);
389 root->curOuterParams = lappend(root->curOuterParams, nlp);
390
391 /* And return the replacement Param */
392 return param;
393 }
394
395 /*
396 * process_subquery_nestloop_params
397 * Handle params of a parameterized subquery that need to be fed
398 * from an outer nestloop.
399 *
400 * Currently, that would be *all* params that a subquery in FROM has demanded
401 * from the current query level, since they must be LATERAL references.
402 *
403 * subplan_params is a list of PlannerParamItems that we intend to pass to
404 * a subquery-in-FROM. (This was constructed in root->plan_params while
405 * planning the subquery, but isn't there anymore when this is called.)
406 *
407 * The subplan's references to the outer variables are already represented
408 * as PARAM_EXEC Params, since that conversion was done by the routines above
409 * while planning the subquery. So we need not modify the subplan or the
410 * PlannerParamItems here. What we do need to do is add entries to
411 * root->curOuterParams to signal the parent nestloop plan node that it must
412 * provide these values. This differs from replace_nestloop_param_var in
413 * that the PARAM_EXEC slots to use have already been determined.
414 *
415 * Note that we also use root->curOuterRels as an implicit parameter for
416 * sanity checks.
417 */
418 void
process_subquery_nestloop_params(PlannerInfo * root,List * subplan_params)419 process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
420 {
421 ListCell *lc;
422
423 foreach(lc, subplan_params)
424 {
425 PlannerParamItem *pitem = castNode(PlannerParamItem, lfirst(lc));
426
427 if (IsA(pitem->item, Var))
428 {
429 Var *var = (Var *) pitem->item;
430 NestLoopParam *nlp;
431 ListCell *lc;
432
433 /* If not from a nestloop outer rel, complain */
434 if (!bms_is_member(var->varno, root->curOuterRels))
435 elog(ERROR, "non-LATERAL parameter required by subquery");
436
437 /* Is this param already listed in root->curOuterParams? */
438 foreach(lc, root->curOuterParams)
439 {
440 nlp = (NestLoopParam *) lfirst(lc);
441 if (nlp->paramno == pitem->paramId)
442 {
443 Assert(equal(var, nlp->paramval));
444 /* Present, so nothing to do */
445 break;
446 }
447 }
448 if (lc == NULL)
449 {
450 /* No, so add it */
451 nlp = makeNode(NestLoopParam);
452 nlp->paramno = pitem->paramId;
453 nlp->paramval = copyObject(var);
454 root->curOuterParams = lappend(root->curOuterParams, nlp);
455 }
456 }
457 else if (IsA(pitem->item, PlaceHolderVar))
458 {
459 PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
460 NestLoopParam *nlp;
461 ListCell *lc;
462
463 /* If not from a nestloop outer rel, complain */
464 if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
465 root->curOuterRels))
466 elog(ERROR, "non-LATERAL parameter required by subquery");
467
468 /* Is this param already listed in root->curOuterParams? */
469 foreach(lc, root->curOuterParams)
470 {
471 nlp = (NestLoopParam *) lfirst(lc);
472 if (nlp->paramno == pitem->paramId)
473 {
474 Assert(equal(phv, nlp->paramval));
475 /* Present, so nothing to do */
476 break;
477 }
478 }
479 if (lc == NULL)
480 {
481 /* No, so add it */
482 nlp = makeNode(NestLoopParam);
483 nlp->paramno = pitem->paramId;
484 nlp->paramval = (Var *) copyObject(phv);
485 root->curOuterParams = lappend(root->curOuterParams, nlp);
486 }
487 }
488 else
489 elog(ERROR, "unexpected type of subquery parameter");
490 }
491 }
492
493 /*
494 * Identify any NestLoopParams that should be supplied by a NestLoop plan
495 * node with the specified lefthand rels. Remove them from the active
496 * root->curOuterParams list and return them as the result list.
497 */
498 List *
identify_current_nestloop_params(PlannerInfo * root,Relids leftrelids)499 identify_current_nestloop_params(PlannerInfo *root, Relids leftrelids)
500 {
501 List *result;
502 ListCell *cell;
503 ListCell *prev;
504 ListCell *next;
505
506 result = NIL;
507 prev = NULL;
508 for (cell = list_head(root->curOuterParams); cell; cell = next)
509 {
510 NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
511
512 next = lnext(cell);
513
514 /*
515 * We are looking for Vars and PHVs that can be supplied by the
516 * lefthand rels. The "bms_overlap" test is just an optimization to
517 * allow skipping find_placeholder_info() if the PHV couldn't match.
518 */
519 if (IsA(nlp->paramval, Var) &&
520 bms_is_member(nlp->paramval->varno, leftrelids))
521 {
522 root->curOuterParams = list_delete_cell(root->curOuterParams,
523 cell, prev);
524 result = lappend(result, nlp);
525 }
526 else if (IsA(nlp->paramval, PlaceHolderVar) &&
527 bms_overlap(((PlaceHolderVar *) nlp->paramval)->phrels,
528 leftrelids) &&
529 bms_is_subset(find_placeholder_info(root,
530 (PlaceHolderVar *) nlp->paramval,
531 false)->ph_eval_at,
532 leftrelids))
533 {
534 root->curOuterParams = list_delete_cell(root->curOuterParams,
535 cell, prev);
536 result = lappend(result, nlp);
537 }
538 else
539 prev = cell;
540 }
541 return result;
542 }
543
544 /*
545 * Generate a new Param node that will not conflict with any other.
546 *
547 * This is used to create Params representing subplan outputs or
548 * NestLoop parameters.
549 *
550 * We don't need to build a PlannerParamItem for such a Param, but we do
551 * need to record the PARAM_EXEC slot number as being allocated.
552 */
553 Param *
generate_new_exec_param(PlannerInfo * root,Oid paramtype,int32 paramtypmod,Oid paramcollation)554 generate_new_exec_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
555 Oid paramcollation)
556 {
557 Param *retval;
558
559 retval = makeNode(Param);
560 retval->paramkind = PARAM_EXEC;
561 retval->paramid = root->glob->nParamExec++;
562 retval->paramtype = paramtype;
563 retval->paramtypmod = paramtypmod;
564 retval->paramcollid = paramcollation;
565 retval->location = -1;
566
567 return retval;
568 }
569
570 /*
571 * Assign a (nonnegative) PARAM_EXEC ID for a special parameter (one that
572 * is not actually used to carry a value at runtime). Such parameters are
573 * used for special runtime signaling purposes, such as connecting a
574 * recursive union node to its worktable scan node or forcing plan
575 * re-evaluation within the EvalPlanQual mechanism. No actual Param node
576 * exists with this ID, however.
577 */
578 int
assign_special_exec_param(PlannerInfo * root)579 assign_special_exec_param(PlannerInfo *root)
580 {
581 return root->glob->nParamExec++;
582 }
583