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-2019, 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 */
65static int
66assign_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 the location.
87 */
88 if (pvar->varno == var->varno &&
89 pvar->varattno == var->varattno &&
90 pvar->vartype == var->vartype &&
91 pvar->vartypmod == var->vartypmod &&
92 pvar->varcollid == var->varcollid &&
93 pvar->varnoold == var->varnoold &&
94 pvar->varoattno == var->varoattno)
95 return pitem->paramId;
96 }
97 }
98
99 /* Nope, so make a new one */
100 var = copyObject(var);
101 var->varlevelsup = 0;
102
103 pitem = makeNode(PlannerParamItem);
104 pitem->item = (Node *) var;
105 pitem->paramId = list_length(root->glob->paramExecTypes);
106 root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
107 var->vartype);
108
109 root->plan_params = lappend(root->plan_params, pitem);
110
111 return pitem->paramId;
112}
113
114/*
115 * Generate a Param node to replace the given Var,
116 * which is expected to have varlevelsup > 0 (ie, it is not local).
117 * Record the need for the Var in the proper upper-level root->plan_params.
118 */
119Param *
120replace_outer_var(PlannerInfo *root, Var *var)
121{
122 Param *retval;
123 int i;
124
125 Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);
126
127 /* Find the Var in the appropriate plan_params, or add it if not present */
128 i = assign_param_for_var(root, var);
129
130 retval = makeNode(Param);
131 retval->paramkind = PARAM_EXEC;
132 retval->paramid = i;
133 retval->paramtype = var->vartype;
134 retval->paramtypmod = var->vartypmod;
135 retval->paramcollid = var->varcollid;
136 retval->location = var->location;
137
138 return retval;
139}
140
141/*
142 * Select a PARAM_EXEC number to identify the given PlaceHolderVar as a
143 * parameter for the current subquery. (It might already have one.)
144 * Record the need for the PHV in the proper upper-level root->plan_params.
145 *
146 * This is just like assign_param_for_var, except for PlaceHolderVars.
147 */
148static int
149assign_param_for_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
150{
151 ListCell *ppl;
152 PlannerParamItem *pitem;
153 Index levelsup;
154
155 /* Find the query level the PHV belongs to */
156 for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--)
157 root = root->parent_root;
158
159 /* If there's already a matching PlannerParamItem there, just use it */
160 foreach(ppl, root->plan_params)
161 {
162 pitem = (PlannerParamItem *) lfirst(ppl);
163 if (IsA(pitem->item, PlaceHolderVar))
164 {
165 PlaceHolderVar *pphv = (PlaceHolderVar *) pitem->item;
166
167 /* We assume comparing the PHIDs is sufficient */
168 if (pphv->phid == phv->phid)
169 return pitem->paramId;
170 }
171 }
172
173 /* Nope, so make a new one */
174 phv = copyObject(phv);
175 IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
176 Assert(phv->phlevelsup == 0);
177
178 pitem = makeNode(PlannerParamItem);
179 pitem->item = (Node *) phv;
180 pitem->paramId = list_length(root->glob->paramExecTypes);
181 root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
182 exprType((Node *) phv->phexpr));
183
184 root->plan_params = lappend(root->plan_params, pitem);
185
186 return pitem->paramId;
187}
188
189/*
190 * Generate a Param node to replace the given PlaceHolderVar,
191 * which is expected to have phlevelsup > 0 (ie, it is not local).
192 * Record the need for the PHV in the proper upper-level root->plan_params.
193 *
194 * This is just like replace_outer_var, except for PlaceHolderVars.
195 */
196Param *
197replace_outer_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
198{
199 Param *retval;
200 int i;
201
202 Assert(phv->phlevelsup > 0 && phv->phlevelsup < root->query_level);
203
204 /* Find the PHV in the appropriate plan_params, or add it if not present */
205 i = assign_param_for_placeholdervar(root, phv);
206
207 retval = makeNode(Param);
208 retval->paramkind = PARAM_EXEC;
209 retval->paramid = i;
210 retval->paramtype = exprType((Node *) phv->phexpr);
211 retval->paramtypmod = exprTypmod((Node *) phv->phexpr);
212 retval->paramcollid = exprCollation((Node *) phv->phexpr);
213 retval->location = -1;
214
215 return retval;
216}
217
218/*
219 * Generate a Param node to replace the given Aggref
220 * which is expected to have agglevelsup > 0 (ie, it is not local).
221 * Record the need for the Aggref in the proper upper-level root->plan_params.
222 */
223Param *
224replace_outer_agg(PlannerInfo *root, Aggref *agg)
225{
226 Param *retval;
227 PlannerParamItem *pitem;
228 Index levelsup;
229
230 Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level);
231
232 /* Find the query level the Aggref belongs to */
233 for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
234 root = root->parent_root;
235
236 /*
237 * It does not seem worthwhile to try to de-duplicate references to outer
238 * aggs. Just make a new slot every time.
239 */
240 agg = copyObject(agg);
241 IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
242 Assert(agg->agglevelsup == 0);
243
244 pitem = makeNode(PlannerParamItem);
245 pitem->item = (Node *) agg;
246 pitem->paramId = list_length(root->glob->paramExecTypes);
247 root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
248 agg->aggtype);
249
250 root->plan_params = lappend(root->plan_params, pitem);
251
252 retval = makeNode(Param);
253 retval->paramkind = PARAM_EXEC;
254 retval->paramid = pitem->paramId;
255 retval->paramtype = agg->aggtype;
256 retval->paramtypmod = -1;
257 retval->paramcollid = agg->aggcollid;
258 retval->location = agg->location;
259
260 return retval;
261}
262
263/*
264 * Generate a Param node to replace the given GroupingFunc expression which is
265 * expected to have agglevelsup > 0 (ie, it is not local).
266 * Record the need for the GroupingFunc in the proper upper-level
267 * root->plan_params.
268 */
269Param *
270replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp)
271{
272 Param *retval;
273 PlannerParamItem *pitem;
274 Index levelsup;
275 Oid ptype = exprType((Node *) grp);
276
277 Assert(grp->agglevelsup > 0 && grp->agglevelsup < root->query_level);
278
279 /* Find the query level the GroupingFunc belongs to */
280 for (levelsup = grp->agglevelsup; levelsup > 0; levelsup--)
281 root = root->parent_root;
282
283 /*
284 * It does not seem worthwhile to try to de-duplicate references to outer
285 * aggs. Just make a new slot every time.
286 */
287 grp = copyObject(grp);
288 IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0);
289 Assert(grp->agglevelsup == 0);
290
291 pitem = makeNode(PlannerParamItem);
292 pitem->item = (Node *) grp;
293 pitem->paramId = list_length(root->glob->paramExecTypes);
294 root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
295 ptype);
296
297 root->plan_params = lappend(root->plan_params, pitem);
298
299 retval = makeNode(Param);
300 retval->paramkind = PARAM_EXEC;
301 retval->paramid = pitem->paramId;
302 retval->paramtype = ptype;
303 retval->paramtypmod = -1;
304 retval->paramcollid = InvalidOid;
305 retval->location = grp->location;
306
307 return retval;
308}
309
310/*
311 * Generate a Param node to replace the given Var,
312 * which is expected to come from some upper NestLoop plan node.
313 * Record the need for the Var in root->curOuterParams.
314 */
315Param *
316replace_nestloop_param_var(PlannerInfo *root, Var *var)
317{
318 Param *param;
319 NestLoopParam *nlp;
320 ListCell *lc;
321
322 /* Is this Var already listed in root->curOuterParams? */
323 foreach(lc, root->curOuterParams)
324 {
325 nlp = (NestLoopParam *) lfirst(lc);
326 if (equal(var, nlp->paramval))
327 {
328 /* Yes, so just make a Param referencing this NLP's slot */
329 param = makeNode(Param);
330 param->paramkind = PARAM_EXEC;
331 param->paramid = nlp->paramno;
332 param->paramtype = var->vartype;
333 param->paramtypmod = var->vartypmod;
334 param->paramcollid = var->varcollid;
335 param->location = var->location;
336 return param;
337 }
338 }
339
340 /* No, so assign a PARAM_EXEC slot for a new NLP */
341 param = generate_new_exec_param(root,
342 var->vartype,
343 var->vartypmod,
344 var->varcollid);
345 param->location = var->location;
346
347 /* Add it to the list of required NLPs */
348 nlp = makeNode(NestLoopParam);
349 nlp->paramno = param->paramid;
350 nlp->paramval = copyObject(var);
351 root->curOuterParams = lappend(root->curOuterParams, nlp);
352
353 /* And return the replacement Param */
354 return param;
355}
356
357/*
358 * Generate a Param node to replace the given PlaceHolderVar,
359 * which is expected to come from some upper NestLoop plan node.
360 * Record the need for the PHV in root->curOuterParams.
361 *
362 * This is just like replace_nestloop_param_var, except for PlaceHolderVars.
363 */
364Param *
365replace_nestloop_param_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
366{
367 Param *param;
368 NestLoopParam *nlp;
369 ListCell *lc;
370
371 /* Is this PHV already listed in root->curOuterParams? */
372 foreach(lc, root->curOuterParams)
373 {
374 nlp = (NestLoopParam *) lfirst(lc);
375 if (equal(phv, nlp->paramval))
376 {
377 /* Yes, so just make a Param referencing this NLP's slot */
378 param = makeNode(Param);
379 param->paramkind = PARAM_EXEC;
380 param->paramid = nlp->paramno;
381 param->paramtype = exprType((Node *) phv->phexpr);
382 param->paramtypmod = exprTypmod((Node *) phv->phexpr);
383 param->paramcollid = exprCollation((Node *) phv->phexpr);
384 param->location = -1;
385 return param;
386 }
387 }
388
389 /* No, so assign a PARAM_EXEC slot for a new NLP */
390 param = generate_new_exec_param(root,
391 exprType((Node *) phv->phexpr),
392 exprTypmod((Node *) phv->phexpr),
393 exprCollation((Node *) phv->phexpr));
394
395 /* Add it to the list of required NLPs */
396 nlp = makeNode(NestLoopParam);
397 nlp->paramno = param->paramid;
398 nlp->paramval = (Var *) copyObject(phv);
399 root->curOuterParams = lappend(root->curOuterParams, nlp);
400
401 /* And return the replacement Param */
402 return param;
403}
404
405/*
406 * process_subquery_nestloop_params
407 * Handle params of a parameterized subquery that need to be fed
408 * from an outer nestloop.
409 *
410 * Currently, that would be *all* params that a subquery in FROM has demanded
411 * from the current query level, since they must be LATERAL references.
412 *
413 * subplan_params is a list of PlannerParamItems that we intend to pass to
414 * a subquery-in-FROM. (This was constructed in root->plan_params while
415 * planning the subquery, but isn't there anymore when this is called.)
416 *
417 * The subplan's references to the outer variables are already represented
418 * as PARAM_EXEC Params, since that conversion was done by the routines above
419 * while planning the subquery. So we need not modify the subplan or the
420 * PlannerParamItems here. What we do need to do is add entries to
421 * root->curOuterParams to signal the parent nestloop plan node that it must
422 * provide these values. This differs from replace_nestloop_param_var in
423 * that the PARAM_EXEC slots to use have already been determined.
424 *
425 * Note that we also use root->curOuterRels as an implicit parameter for
426 * sanity checks.
427 */
428void
429process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
430{
431 ListCell *lc;
432
433 foreach(lc, subplan_params)
434 {
435 PlannerParamItem *pitem = castNode(PlannerParamItem, lfirst(lc));
436
437 if (IsA(pitem->item, Var))
438 {
439 Var *var = (Var *) pitem->item;
440 NestLoopParam *nlp;
441 ListCell *lc;
442
443 /* If not from a nestloop outer rel, complain */
444 if (!bms_is_member(var->varno, root->curOuterRels))
445 elog(ERROR, "non-LATERAL parameter required by subquery");
446
447 /* Is this param already listed in root->curOuterParams? */
448 foreach(lc, root->curOuterParams)
449 {
450 nlp = (NestLoopParam *) lfirst(lc);
451 if (nlp->paramno == pitem->paramId)
452 {
453 Assert(equal(var, nlp->paramval));
454 /* Present, so nothing to do */
455 break;
456 }
457 }
458 if (lc == NULL)
459 {
460 /* No, so add it */
461 nlp = makeNode(NestLoopParam);
462 nlp->paramno = pitem->paramId;
463 nlp->paramval = copyObject(var);
464 root->curOuterParams = lappend(root->curOuterParams, nlp);
465 }
466 }
467 else if (IsA(pitem->item, PlaceHolderVar))
468 {
469 PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
470 NestLoopParam *nlp;
471 ListCell *lc;
472
473 /* If not from a nestloop outer rel, complain */
474 if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
475 root->curOuterRels))
476 elog(ERROR, "non-LATERAL parameter required by subquery");
477
478 /* Is this param already listed in root->curOuterParams? */
479 foreach(lc, root->curOuterParams)
480 {
481 nlp = (NestLoopParam *) lfirst(lc);
482 if (nlp->paramno == pitem->paramId)
483 {
484 Assert(equal(phv, nlp->paramval));
485 /* Present, so nothing to do */
486 break;
487 }
488 }
489 if (lc == NULL)
490 {
491 /* No, so add it */
492 nlp = makeNode(NestLoopParam);
493 nlp->paramno = pitem->paramId;
494 nlp->paramval = (Var *) copyObject(phv);
495 root->curOuterParams = lappend(root->curOuterParams, nlp);
496 }
497 }
498 else
499 elog(ERROR, "unexpected type of subquery parameter");
500 }
501}
502
503/*
504 * Identify any NestLoopParams that should be supplied by a NestLoop plan
505 * node with the specified lefthand rels. Remove them from the active
506 * root->curOuterParams list and return them as the result list.
507 */
508List *
509identify_current_nestloop_params(PlannerInfo *root, Relids leftrelids)
510{
511 List *result;
512 ListCell *cell;
513 ListCell *prev;
514 ListCell *next;
515
516 result = NIL;
517 prev = NULL;
518 for (cell = list_head(root->curOuterParams); cell; cell = next)
519 {
520 NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
521
522 next = lnext(cell);
523
524 /*
525 * We are looking for Vars and PHVs that can be supplied by the
526 * lefthand rels. The "bms_overlap" test is just an optimization to
527 * allow skipping find_placeholder_info() if the PHV couldn't match.
528 */
529 if (IsA(nlp->paramval, Var) &&
530 bms_is_member(nlp->paramval->varno, leftrelids))
531 {
532 root->curOuterParams = list_delete_cell(root->curOuterParams,
533 cell, prev);
534 result = lappend(result, nlp);
535 }
536 else if (IsA(nlp->paramval, PlaceHolderVar) &&
537 bms_overlap(((PlaceHolderVar *) nlp->paramval)->phrels,
538 leftrelids) &&
539 bms_is_subset(find_placeholder_info(root,
540 (PlaceHolderVar *) nlp->paramval,
541 false)->ph_eval_at,
542 leftrelids))
543 {
544 root->curOuterParams = list_delete_cell(root->curOuterParams,
545 cell, prev);
546 result = lappend(result, nlp);
547 }
548 else
549 prev = cell;
550 }
551 return result;
552}
553
554/*
555 * Generate a new Param node that will not conflict with any other.
556 *
557 * This is used to create Params representing subplan outputs or
558 * NestLoop parameters.
559 *
560 * We don't need to build a PlannerParamItem for such a Param, but we do
561 * need to make sure we record the type in paramExecTypes (otherwise,
562 * there won't be a slot allocated for it).
563 */
564Param *
565generate_new_exec_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
566 Oid paramcollation)
567{
568 Param *retval;
569
570 retval = makeNode(Param);
571 retval->paramkind = PARAM_EXEC;
572 retval->paramid = list_length(root->glob->paramExecTypes);
573 root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
574 paramtype);
575 retval->paramtype = paramtype;
576 retval->paramtypmod = paramtypmod;
577 retval->paramcollid = paramcollation;
578 retval->location = -1;
579
580 return retval;
581}
582
583/*
584 * Assign a (nonnegative) PARAM_EXEC ID for a special parameter (one that
585 * is not actually used to carry a value at runtime). Such parameters are
586 * used for special runtime signaling purposes, such as connecting a
587 * recursive union node to its worktable scan node or forcing plan
588 * re-evaluation within the EvalPlanQual mechanism. No actual Param node
589 * exists with this ID, however.
590 */
591int
592assign_special_exec_param(PlannerInfo *root)
593{
594 int paramId = list_length(root->glob->paramExecTypes);
595
596 root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
597 InvalidOid);
598 return paramId;
599}
600