Line data Source code
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-2025, 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
66 48430 : 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 97314 : for (levelsup = var->varlevelsup; levelsup > 0; levelsup--)
74 48884 : root = root->parent_root;
75 :
76 : /* If there's already a matching PlannerParamItem there, just use it */
77 71500 : foreach(ppl, root->plan_params)
78 : {
79 30468 : pitem = (PlannerParamItem *) lfirst(ppl);
80 30468 : if (IsA(pitem->item, Var))
81 : {
82 30468 : 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 30468 : if (pvar->varno == var->varno &&
90 28392 : pvar->varattno == var->varattno &&
91 7416 : pvar->vartype == var->vartype &&
92 7416 : pvar->vartypmod == var->vartypmod &&
93 7416 : pvar->varcollid == var->varcollid &&
94 14814 : pvar->varreturningtype == var->varreturningtype &&
95 7398 : bms_equal(pvar->varnullingrels, var->varnullingrels))
96 7398 : return pitem->paramId;
97 : }
98 : }
99 :
100 : /* Nope, so make a new one */
101 41032 : var = copyObject(var);
102 41032 : var->varlevelsup = 0;
103 :
104 41032 : pitem = makeNode(PlannerParamItem);
105 41032 : pitem->item = (Node *) var;
106 41032 : pitem->paramId = list_length(root->glob->paramExecTypes);
107 41032 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
108 : var->vartype);
109 :
110 41032 : root->plan_params = lappend(root->plan_params, pitem);
111 :
112 41032 : return pitem->paramId;
113 : }
114 :
115 : /*
116 : * Generate a Param node to replace the given Var,
117 : * which is expected to have varlevelsup > 0 (ie, it is not local).
118 : * Record the need for the Var in the proper upper-level root->plan_params.
119 : */
120 : Param *
121 48430 : replace_outer_var(PlannerInfo *root, Var *var)
122 : {
123 : Param *retval;
124 : int i;
125 :
126 : Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);
127 :
128 : /* Find the Var in the appropriate plan_params, or add it if not present */
129 48430 : i = assign_param_for_var(root, var);
130 :
131 48430 : retval = makeNode(Param);
132 48430 : retval->paramkind = PARAM_EXEC;
133 48430 : retval->paramid = i;
134 48430 : retval->paramtype = var->vartype;
135 48430 : retval->paramtypmod = var->vartypmod;
136 48430 : retval->paramcollid = var->varcollid;
137 48430 : retval->location = var->location;
138 :
139 48430 : return retval;
140 : }
141 :
142 : /*
143 : * Select a PARAM_EXEC number to identify the given PlaceHolderVar as a
144 : * parameter for the current subquery. (It might already have one.)
145 : * Record the need for the PHV in the proper upper-level root->plan_params.
146 : *
147 : * This is just like assign_param_for_var, except for PlaceHolderVars.
148 : */
149 : static int
150 42 : assign_param_for_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
151 : {
152 : ListCell *ppl;
153 : PlannerParamItem *pitem;
154 : Index levelsup;
155 :
156 : /* Find the query level the PHV belongs to */
157 84 : for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--)
158 42 : root = root->parent_root;
159 :
160 : /* If there's already a matching PlannerParamItem there, just use it */
161 102 : foreach(ppl, root->plan_params)
162 : {
163 60 : pitem = (PlannerParamItem *) lfirst(ppl);
164 60 : if (IsA(pitem->item, PlaceHolderVar))
165 : {
166 0 : PlaceHolderVar *pphv = (PlaceHolderVar *) pitem->item;
167 :
168 : /* We assume comparing the PHIDs is sufficient */
169 0 : if (pphv->phid == phv->phid)
170 0 : return pitem->paramId;
171 : }
172 : }
173 :
174 : /* Nope, so make a new one */
175 42 : phv = copyObject(phv);
176 42 : IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
177 : Assert(phv->phlevelsup == 0);
178 :
179 42 : pitem = makeNode(PlannerParamItem);
180 42 : pitem->item = (Node *) phv;
181 42 : pitem->paramId = list_length(root->glob->paramExecTypes);
182 42 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
183 42 : exprType((Node *) phv->phexpr));
184 :
185 42 : root->plan_params = lappend(root->plan_params, pitem);
186 :
187 42 : return pitem->paramId;
188 : }
189 :
190 : /*
191 : * Generate a Param node to replace the given PlaceHolderVar,
192 : * which is expected to have phlevelsup > 0 (ie, it is not local).
193 : * Record the need for the PHV in the proper upper-level root->plan_params.
194 : *
195 : * This is just like replace_outer_var, except for PlaceHolderVars.
196 : */
197 : Param *
198 42 : replace_outer_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
199 : {
200 : Param *retval;
201 : int i;
202 :
203 : Assert(phv->phlevelsup > 0 && phv->phlevelsup < root->query_level);
204 :
205 : /* Find the PHV in the appropriate plan_params, or add it if not present */
206 42 : i = assign_param_for_placeholdervar(root, phv);
207 :
208 42 : retval = makeNode(Param);
209 42 : retval->paramkind = PARAM_EXEC;
210 42 : retval->paramid = i;
211 42 : retval->paramtype = exprType((Node *) phv->phexpr);
212 42 : retval->paramtypmod = exprTypmod((Node *) phv->phexpr);
213 42 : retval->paramcollid = exprCollation((Node *) phv->phexpr);
214 42 : retval->location = -1;
215 :
216 42 : return retval;
217 : }
218 :
219 : /*
220 : * Generate a Param node to replace the given Aggref
221 : * which is expected to have agglevelsup > 0 (ie, it is not local).
222 : * Record the need for the Aggref in the proper upper-level root->plan_params.
223 : */
224 : Param *
225 52 : replace_outer_agg(PlannerInfo *root, Aggref *agg)
226 : {
227 : Param *retval;
228 : PlannerParamItem *pitem;
229 : Index levelsup;
230 :
231 : Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level);
232 :
233 : /* Find the query level the Aggref belongs to */
234 104 : for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
235 52 : root = root->parent_root;
236 :
237 : /*
238 : * It does not seem worthwhile to try to de-duplicate references to outer
239 : * aggs. Just make a new slot every time.
240 : */
241 52 : agg = copyObject(agg);
242 52 : IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
243 : Assert(agg->agglevelsup == 0);
244 :
245 52 : pitem = makeNode(PlannerParamItem);
246 52 : pitem->item = (Node *) agg;
247 52 : pitem->paramId = list_length(root->glob->paramExecTypes);
248 52 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
249 : agg->aggtype);
250 :
251 52 : root->plan_params = lappend(root->plan_params, pitem);
252 :
253 52 : retval = makeNode(Param);
254 52 : retval->paramkind = PARAM_EXEC;
255 52 : retval->paramid = pitem->paramId;
256 52 : retval->paramtype = agg->aggtype;
257 52 : retval->paramtypmod = -1;
258 52 : retval->paramcollid = agg->aggcollid;
259 52 : retval->location = agg->location;
260 :
261 52 : return retval;
262 : }
263 :
264 : /*
265 : * Generate a Param node to replace the given GroupingFunc expression which is
266 : * expected to have agglevelsup > 0 (ie, it is not local).
267 : * Record the need for the GroupingFunc in the proper upper-level
268 : * root->plan_params.
269 : */
270 : Param *
271 64 : replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp)
272 : {
273 : Param *retval;
274 : PlannerParamItem *pitem;
275 : Index levelsup;
276 64 : Oid ptype = exprType((Node *) grp);
277 :
278 : Assert(grp->agglevelsup > 0 && grp->agglevelsup < root->query_level);
279 :
280 : /* Find the query level the GroupingFunc belongs to */
281 136 : for (levelsup = grp->agglevelsup; levelsup > 0; levelsup--)
282 72 : root = root->parent_root;
283 :
284 : /*
285 : * It does not seem worthwhile to try to de-duplicate references to outer
286 : * aggs. Just make a new slot every time.
287 : */
288 64 : grp = copyObject(grp);
289 64 : IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0);
290 : Assert(grp->agglevelsup == 0);
291 :
292 64 : pitem = makeNode(PlannerParamItem);
293 64 : pitem->item = (Node *) grp;
294 64 : pitem->paramId = list_length(root->glob->paramExecTypes);
295 64 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
296 : ptype);
297 :
298 64 : root->plan_params = lappend(root->plan_params, pitem);
299 :
300 64 : retval = makeNode(Param);
301 64 : retval->paramkind = PARAM_EXEC;
302 64 : retval->paramid = pitem->paramId;
303 64 : retval->paramtype = ptype;
304 64 : retval->paramtypmod = -1;
305 64 : retval->paramcollid = InvalidOid;
306 64 : retval->location = grp->location;
307 :
308 64 : return retval;
309 : }
310 :
311 : /*
312 : * Generate a Param node to replace the given MergeSupportFunc expression
313 : * which is expected to be in the RETURNING list of an upper-level MERGE
314 : * query. Record the need for the MergeSupportFunc in the proper upper-level
315 : * root->plan_params.
316 : */
317 : Param *
318 6 : replace_outer_merge_support(PlannerInfo *root, MergeSupportFunc *msf)
319 : {
320 : Param *retval;
321 : PlannerParamItem *pitem;
322 6 : Oid ptype = exprType((Node *) msf);
323 :
324 : Assert(root->parse->commandType != CMD_MERGE);
325 :
326 : /*
327 : * The parser should have ensured that the MergeSupportFunc is in the
328 : * RETURNING list of an upper-level MERGE query, so find that query.
329 : */
330 : do
331 : {
332 6 : root = root->parent_root;
333 6 : if (root == NULL)
334 0 : elog(ERROR, "MergeSupportFunc found outside MERGE");
335 6 : } while (root->parse->commandType != CMD_MERGE);
336 :
337 : /*
338 : * It does not seem worthwhile to try to de-duplicate references to outer
339 : * MergeSupportFunc expressions. Just make a new slot every time.
340 : */
341 6 : msf = copyObject(msf);
342 :
343 6 : pitem = makeNode(PlannerParamItem);
344 6 : pitem->item = (Node *) msf;
345 6 : pitem->paramId = list_length(root->glob->paramExecTypes);
346 6 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
347 : ptype);
348 :
349 6 : root->plan_params = lappend(root->plan_params, pitem);
350 :
351 6 : retval = makeNode(Param);
352 6 : retval->paramkind = PARAM_EXEC;
353 6 : retval->paramid = pitem->paramId;
354 6 : retval->paramtype = ptype;
355 6 : retval->paramtypmod = -1;
356 6 : retval->paramcollid = InvalidOid;
357 6 : retval->location = msf->location;
358 :
359 6 : return retval;
360 : }
361 :
362 : /*
363 : * Generate a Param node to replace the given ReturningExpr expression which
364 : * is expected to have retlevelsup > 0 (ie, it is not local). Record the need
365 : * for the ReturningExpr in the proper upper-level root->plan_params.
366 : */
367 : Param *
368 18 : replace_outer_returning(PlannerInfo *root, ReturningExpr *rexpr)
369 : {
370 : Param *retval;
371 : PlannerParamItem *pitem;
372 : Index levelsup;
373 18 : Oid ptype = exprType((Node *) rexpr->retexpr);
374 :
375 : Assert(rexpr->retlevelsup > 0 && rexpr->retlevelsup < root->query_level);
376 :
377 : /* Find the query level the ReturningExpr belongs to */
378 42 : for (levelsup = rexpr->retlevelsup; levelsup > 0; levelsup--)
379 24 : root = root->parent_root;
380 :
381 : /*
382 : * It does not seem worthwhile to try to de-duplicate references to outer
383 : * ReturningExprs. Just make a new slot every time.
384 : */
385 18 : rexpr = copyObject(rexpr);
386 18 : IncrementVarSublevelsUp((Node *) rexpr, -((int) rexpr->retlevelsup), 0);
387 : Assert(rexpr->retlevelsup == 0);
388 :
389 18 : pitem = makeNode(PlannerParamItem);
390 18 : pitem->item = (Node *) rexpr;
391 18 : pitem->paramId = list_length(root->glob->paramExecTypes);
392 18 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
393 : ptype);
394 :
395 18 : root->plan_params = lappend(root->plan_params, pitem);
396 :
397 18 : retval = makeNode(Param);
398 18 : retval->paramkind = PARAM_EXEC;
399 18 : retval->paramid = pitem->paramId;
400 18 : retval->paramtype = ptype;
401 18 : retval->paramtypmod = exprTypmod((Node *) rexpr->retexpr);
402 18 : retval->paramcollid = exprCollation((Node *) rexpr->retexpr);
403 18 : retval->location = exprLocation((Node *) rexpr->retexpr);
404 :
405 18 : return retval;
406 : }
407 :
408 : /*
409 : * Generate a Param node to replace the given Var,
410 : * which is expected to come from some upper NestLoop plan node.
411 : * Record the need for the Var in root->curOuterParams.
412 : */
413 : Param *
414 83440 : replace_nestloop_param_var(PlannerInfo *root, Var *var)
415 : {
416 : Param *param;
417 : NestLoopParam *nlp;
418 : ListCell *lc;
419 :
420 : /* Is this Var already listed in root->curOuterParams? */
421 92154 : foreach(lc, root->curOuterParams)
422 : {
423 46684 : nlp = (NestLoopParam *) lfirst(lc);
424 46684 : if (equal(var, nlp->paramval))
425 : {
426 : /* Yes, so just make a Param referencing this NLP's slot */
427 37970 : param = makeNode(Param);
428 37970 : param->paramkind = PARAM_EXEC;
429 37970 : param->paramid = nlp->paramno;
430 37970 : param->paramtype = var->vartype;
431 37970 : param->paramtypmod = var->vartypmod;
432 37970 : param->paramcollid = var->varcollid;
433 37970 : param->location = var->location;
434 37970 : return param;
435 : }
436 : }
437 :
438 : /* No, so assign a PARAM_EXEC slot for a new NLP */
439 45470 : param = generate_new_exec_param(root,
440 : var->vartype,
441 : var->vartypmod,
442 : var->varcollid);
443 45470 : param->location = var->location;
444 :
445 : /* Add it to the list of required NLPs */
446 45470 : nlp = makeNode(NestLoopParam);
447 45470 : nlp->paramno = param->paramid;
448 45470 : nlp->paramval = copyObject(var);
449 45470 : root->curOuterParams = lappend(root->curOuterParams, nlp);
450 :
451 : /* And return the replacement Param */
452 45470 : return param;
453 : }
454 :
455 : /*
456 : * Generate a Param node to replace the given PlaceHolderVar,
457 : * which is expected to come from some upper NestLoop plan node.
458 : * Record the need for the PHV in root->curOuterParams.
459 : *
460 : * This is just like replace_nestloop_param_var, except for PlaceHolderVars.
461 : */
462 : Param *
463 258 : replace_nestloop_param_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
464 : {
465 : Param *param;
466 : NestLoopParam *nlp;
467 : ListCell *lc;
468 :
469 : /* Is this PHV already listed in root->curOuterParams? */
470 336 : foreach(lc, root->curOuterParams)
471 : {
472 180 : nlp = (NestLoopParam *) lfirst(lc);
473 180 : if (equal(phv, nlp->paramval))
474 : {
475 : /* Yes, so just make a Param referencing this NLP's slot */
476 102 : param = makeNode(Param);
477 102 : param->paramkind = PARAM_EXEC;
478 102 : param->paramid = nlp->paramno;
479 102 : param->paramtype = exprType((Node *) phv->phexpr);
480 102 : param->paramtypmod = exprTypmod((Node *) phv->phexpr);
481 102 : param->paramcollid = exprCollation((Node *) phv->phexpr);
482 102 : param->location = -1;
483 102 : return param;
484 : }
485 : }
486 :
487 : /* No, so assign a PARAM_EXEC slot for a new NLP */
488 156 : param = generate_new_exec_param(root,
489 156 : exprType((Node *) phv->phexpr),
490 156 : exprTypmod((Node *) phv->phexpr),
491 156 : exprCollation((Node *) phv->phexpr));
492 :
493 : /* Add it to the list of required NLPs */
494 156 : nlp = makeNode(NestLoopParam);
495 156 : nlp->paramno = param->paramid;
496 156 : nlp->paramval = (Var *) copyObject(phv);
497 156 : root->curOuterParams = lappend(root->curOuterParams, nlp);
498 :
499 : /* And return the replacement Param */
500 156 : return param;
501 : }
502 :
503 : /*
504 : * process_subquery_nestloop_params
505 : * Handle params of a parameterized subquery that need to be fed
506 : * from an outer nestloop.
507 : *
508 : * Currently, that would be *all* params that a subquery in FROM has demanded
509 : * from the current query level, since they must be LATERAL references.
510 : *
511 : * subplan_params is a list of PlannerParamItems that we intend to pass to
512 : * a subquery-in-FROM. (This was constructed in root->plan_params while
513 : * planning the subquery, but isn't there anymore when this is called.)
514 : *
515 : * The subplan's references to the outer variables are already represented
516 : * as PARAM_EXEC Params, since that conversion was done by the routines above
517 : * while planning the subquery. So we need not modify the subplan or the
518 : * PlannerParamItems here. What we do need to do is add entries to
519 : * root->curOuterParams to signal the parent nestloop plan node that it must
520 : * provide these values. This differs from replace_nestloop_param_var in
521 : * that the PARAM_EXEC slots to use have already been determined.
522 : *
523 : * Note that we also use root->curOuterRels as an implicit parameter for
524 : * sanity checks.
525 : */
526 : void
527 478 : process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
528 : {
529 : ListCell *lc;
530 :
531 1076 : foreach(lc, subplan_params)
532 : {
533 598 : PlannerParamItem *pitem = lfirst_node(PlannerParamItem, lc);
534 :
535 598 : if (IsA(pitem->item, Var))
536 : {
537 568 : Var *var = (Var *) pitem->item;
538 : NestLoopParam *nlp;
539 : ListCell *lc2;
540 :
541 : /* If not from a nestloop outer rel, complain */
542 568 : if (!bms_is_member(var->varno, root->curOuterRels))
543 0 : elog(ERROR, "non-LATERAL parameter required by subquery");
544 :
545 : /* Is this param already listed in root->curOuterParams? */
546 772 : foreach(lc2, root->curOuterParams)
547 : {
548 204 : nlp = (NestLoopParam *) lfirst(lc2);
549 204 : if (nlp->paramno == pitem->paramId)
550 : {
551 : Assert(equal(var, nlp->paramval));
552 : /* Present, so nothing to do */
553 0 : break;
554 : }
555 : }
556 568 : if (lc2 == NULL)
557 : {
558 : /* No, so add it */
559 568 : nlp = makeNode(NestLoopParam);
560 568 : nlp->paramno = pitem->paramId;
561 568 : nlp->paramval = copyObject(var);
562 568 : root->curOuterParams = lappend(root->curOuterParams, nlp);
563 : }
564 : }
565 30 : else if (IsA(pitem->item, PlaceHolderVar))
566 : {
567 30 : PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
568 : NestLoopParam *nlp;
569 : ListCell *lc2;
570 :
571 : /* If not from a nestloop outer rel, complain */
572 30 : if (!bms_is_subset(find_placeholder_info(root, phv)->ph_eval_at,
573 30 : root->curOuterRels))
574 0 : elog(ERROR, "non-LATERAL parameter required by subquery");
575 :
576 : /* Is this param already listed in root->curOuterParams? */
577 90 : foreach(lc2, root->curOuterParams)
578 : {
579 60 : nlp = (NestLoopParam *) lfirst(lc2);
580 60 : if (nlp->paramno == pitem->paramId)
581 : {
582 : Assert(equal(phv, nlp->paramval));
583 : /* Present, so nothing to do */
584 0 : break;
585 : }
586 : }
587 30 : if (lc2 == NULL)
588 : {
589 : /* No, so add it */
590 30 : nlp = makeNode(NestLoopParam);
591 30 : nlp->paramno = pitem->paramId;
592 30 : nlp->paramval = (Var *) copyObject(phv);
593 30 : root->curOuterParams = lappend(root->curOuterParams, nlp);
594 : }
595 : }
596 : else
597 0 : elog(ERROR, "unexpected type of subquery parameter");
598 : }
599 478 : }
600 :
601 : /*
602 : * Identify any NestLoopParams that should be supplied by a NestLoop plan
603 : * node with the specified lefthand rels. Remove them from the active
604 : * root->curOuterParams list and return them as the result list.
605 : *
606 : * XXX Here we also hack up the returned Vars and PHVs so that they do not
607 : * contain nullingrel sets exceeding what is available from the outer side.
608 : * This is needed if we have applied outer join identity 3,
609 : * (A leftjoin B on (Pab)) leftjoin C on (Pb*c)
610 : * = A leftjoin (B leftjoin C on (Pbc)) on (Pab)
611 : * and C contains lateral references to B. It's still safe to apply the
612 : * identity, but the parser will have created those references in the form
613 : * "b*" (i.e., with varnullingrels listing the A/B join), while what we will
614 : * have available from the nestloop's outer side is just "b". We deal with
615 : * that here by stripping the nullingrels down to what is available from the
616 : * outer side according to leftrelids.
617 : *
618 : * That fixes matters for the case of forward application of identity 3.
619 : * If the identity was applied in the reverse direction, we will have
620 : * parameter Vars containing too few nullingrel bits rather than too many.
621 : * Currently, that causes no problems because setrefs.c applies only a
622 : * subset check to nullingrels in NestLoopParams, but we'd have to work
623 : * harder if we ever want to tighten that check. This is all pretty annoying
624 : * because it greatly weakens setrefs.c's cross-check, but the alternative
625 : * seems to be to generate multiple versions of each laterally-parameterized
626 : * subquery, which'd be unduly expensive.
627 : */
628 : List *
629 83802 : identify_current_nestloop_params(PlannerInfo *root, Relids leftrelids)
630 : {
631 : List *result;
632 : ListCell *cell;
633 :
634 83802 : result = NIL;
635 130982 : foreach(cell, root->curOuterParams)
636 : {
637 47180 : NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
638 :
639 : /*
640 : * We are looking for Vars and PHVs that can be supplied by the
641 : * lefthand rels. When we find one, it's okay to modify it in-place
642 : * because all the routines above make a fresh copy to put into
643 : * curOuterParams.
644 : */
645 94174 : if (IsA(nlp->paramval, Var) &&
646 46994 : bms_is_member(nlp->paramval->varno, leftrelids))
647 46038 : {
648 46038 : Var *var = (Var *) nlp->paramval;
649 :
650 46038 : root->curOuterParams = foreach_delete_current(root->curOuterParams,
651 : cell);
652 46038 : var->varnullingrels = bms_intersect(var->varnullingrels,
653 : leftrelids);
654 46038 : result = lappend(result, nlp);
655 : }
656 1328 : else if (IsA(nlp->paramval, PlaceHolderVar) &&
657 372 : bms_is_subset(find_placeholder_info(root,
658 186 : (PlaceHolderVar *) nlp->paramval)->ph_eval_at,
659 : leftrelids))
660 : {
661 186 : PlaceHolderVar *phv = (PlaceHolderVar *) nlp->paramval;
662 :
663 186 : root->curOuterParams = foreach_delete_current(root->curOuterParams,
664 : cell);
665 186 : phv->phnullingrels = bms_intersect(phv->phnullingrels,
666 : leftrelids);
667 186 : result = lappend(result, nlp);
668 : }
669 : }
670 83802 : return result;
671 : }
672 :
673 : /*
674 : * Generate a new Param node that will not conflict with any other.
675 : *
676 : * This is used to create Params representing subplan outputs or
677 : * NestLoop parameters.
678 : *
679 : * We don't need to build a PlannerParamItem for such a Param, but we do
680 : * need to make sure we record the type in paramExecTypes (otherwise,
681 : * there won't be a slot allocated for it).
682 : */
683 : Param *
684 58204 : generate_new_exec_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
685 : Oid paramcollation)
686 : {
687 : Param *retval;
688 :
689 58204 : retval = makeNode(Param);
690 58204 : retval->paramkind = PARAM_EXEC;
691 58204 : retval->paramid = list_length(root->glob->paramExecTypes);
692 58204 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
693 : paramtype);
694 58204 : retval->paramtype = paramtype;
695 58204 : retval->paramtypmod = paramtypmod;
696 58204 : retval->paramcollid = paramcollation;
697 58204 : retval->location = -1;
698 :
699 58204 : return retval;
700 : }
701 :
702 : /*
703 : * Assign a (nonnegative) PARAM_EXEC ID for a special parameter (one that
704 : * is not actually used to carry a value at runtime). Such parameters are
705 : * used for special runtime signaling purposes, such as connecting a
706 : * recursive union node to its worktable scan node or forcing plan
707 : * re-evaluation within the EvalPlanQual mechanism. No actual Param node
708 : * exists with this ID, however.
709 : */
710 : int
711 103398 : assign_special_exec_param(PlannerInfo *root)
712 : {
713 103398 : int paramId = list_length(root->glob->paramExecTypes);
714 :
715 103398 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
716 : InvalidOid);
717 103398 : return paramId;
718 : }
|