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