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