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