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-2023, 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 41316 : 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 82988 : for (levelsup = var->varlevelsup; levelsup > 0; levelsup--)
74 41672 : root = root->parent_root;
75 :
76 : /* If there's already a matching PlannerParamItem there, just use it */
77 61454 : foreach(ppl, root->plan_params)
78 : {
79 26606 : pitem = (PlannerParamItem *) lfirst(ppl);
80 26606 : if (IsA(pitem->item, Var))
81 : {
82 26606 : 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 26606 : if (pvar->varno == var->varno &&
90 24572 : pvar->varattno == var->varattno &&
91 6468 : pvar->vartype == var->vartype &&
92 6468 : pvar->vartypmod == var->vartypmod &&
93 6468 : pvar->varcollid == var->varcollid)
94 6468 : return pitem->paramId;
95 : }
96 : }
97 :
98 : /* Nope, so make a new one */
99 34848 : var = copyObject(var);
100 34848 : var->varlevelsup = 0;
101 :
102 34848 : pitem = makeNode(PlannerParamItem);
103 34848 : pitem->item = (Node *) var;
104 34848 : pitem->paramId = list_length(root->glob->paramExecTypes);
105 34848 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
106 : var->vartype);
107 :
108 34848 : root->plan_params = lappend(root->plan_params, pitem);
109 :
110 34848 : 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 *
119 41316 : 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 41316 : i = assign_param_for_var(root, var);
128 :
129 41316 : retval = makeNode(Param);
130 41316 : retval->paramkind = PARAM_EXEC;
131 41316 : retval->paramid = i;
132 41316 : retval->paramtype = var->vartype;
133 41316 : retval->paramtypmod = var->vartypmod;
134 41316 : retval->paramcollid = var->varcollid;
135 41316 : retval->location = var->location;
136 :
137 41316 : 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
148 42 : 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 84 : for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--)
156 42 : root = root->parent_root;
157 :
158 : /* If there's already a matching PlannerParamItem there, just use it */
159 102 : foreach(ppl, root->plan_params)
160 : {
161 60 : pitem = (PlannerParamItem *) lfirst(ppl);
162 60 : if (IsA(pitem->item, PlaceHolderVar))
163 : {
164 0 : PlaceHolderVar *pphv = (PlaceHolderVar *) pitem->item;
165 :
166 : /* We assume comparing the PHIDs is sufficient */
167 0 : if (pphv->phid == phv->phid)
168 0 : return pitem->paramId;
169 : }
170 : }
171 :
172 : /* Nope, so make a new one */
173 42 : phv = copyObject(phv);
174 42 : IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
175 : Assert(phv->phlevelsup == 0);
176 :
177 42 : pitem = makeNode(PlannerParamItem);
178 42 : pitem->item = (Node *) phv;
179 42 : pitem->paramId = list_length(root->glob->paramExecTypes);
180 42 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
181 42 : exprType((Node *) phv->phexpr));
182 :
183 42 : root->plan_params = lappend(root->plan_params, pitem);
184 :
185 42 : 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 *
196 42 : 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 42 : i = assign_param_for_placeholdervar(root, phv);
205 :
206 42 : retval = makeNode(Param);
207 42 : retval->paramkind = PARAM_EXEC;
208 42 : retval->paramid = i;
209 42 : retval->paramtype = exprType((Node *) phv->phexpr);
210 42 : retval->paramtypmod = exprTypmod((Node *) phv->phexpr);
211 42 : retval->paramcollid = exprCollation((Node *) phv->phexpr);
212 42 : retval->location = -1;
213 :
214 42 : 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 *
223 52 : 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 104 : for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
233 52 : 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 52 : agg = copyObject(agg);
240 52 : IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
241 : Assert(agg->agglevelsup == 0);
242 :
243 52 : pitem = makeNode(PlannerParamItem);
244 52 : pitem->item = (Node *) agg;
245 52 : pitem->paramId = list_length(root->glob->paramExecTypes);
246 52 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
247 : agg->aggtype);
248 :
249 52 : root->plan_params = lappend(root->plan_params, pitem);
250 :
251 52 : retval = makeNode(Param);
252 52 : retval->paramkind = PARAM_EXEC;
253 52 : retval->paramid = pitem->paramId;
254 52 : retval->paramtype = agg->aggtype;
255 52 : retval->paramtypmod = -1;
256 52 : retval->paramcollid = agg->aggcollid;
257 52 : retval->location = agg->location;
258 :
259 52 : 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 *
269 64 : replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp)
270 : {
271 : Param *retval;
272 : PlannerParamItem *pitem;
273 : Index levelsup;
274 64 : 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 136 : for (levelsup = grp->agglevelsup; levelsup > 0; levelsup--)
280 72 : 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 64 : grp = copyObject(grp);
287 64 : IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0);
288 : Assert(grp->agglevelsup == 0);
289 :
290 64 : pitem = makeNode(PlannerParamItem);
291 64 : pitem->item = (Node *) grp;
292 64 : pitem->paramId = list_length(root->glob->paramExecTypes);
293 64 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
294 : ptype);
295 :
296 64 : root->plan_params = lappend(root->plan_params, pitem);
297 :
298 64 : retval = makeNode(Param);
299 64 : retval->paramkind = PARAM_EXEC;
300 64 : retval->paramid = pitem->paramId;
301 64 : retval->paramtype = ptype;
302 64 : retval->paramtypmod = -1;
303 64 : retval->paramcollid = InvalidOid;
304 64 : retval->location = grp->location;
305 :
306 64 : 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 *
315 77664 : 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 83300 : foreach(lc, root->curOuterParams)
323 : {
324 41164 : nlp = (NestLoopParam *) lfirst(lc);
325 41164 : if (equal(var, nlp->paramval))
326 : {
327 : /* Yes, so just make a Param referencing this NLP's slot */
328 35528 : param = makeNode(Param);
329 35528 : param->paramkind = PARAM_EXEC;
330 35528 : param->paramid = nlp->paramno;
331 35528 : param->paramtype = var->vartype;
332 35528 : param->paramtypmod = var->vartypmod;
333 35528 : param->paramcollid = var->varcollid;
334 35528 : param->location = var->location;
335 35528 : return param;
336 : }
337 : }
338 :
339 : /* No, so assign a PARAM_EXEC slot for a new NLP */
340 42136 : param = generate_new_exec_param(root,
341 : var->vartype,
342 : var->vartypmod,
343 : var->varcollid);
344 42136 : param->location = var->location;
345 :
346 : /* Add it to the list of required NLPs */
347 42136 : nlp = makeNode(NestLoopParam);
348 42136 : nlp->paramno = param->paramid;
349 42136 : nlp->paramval = copyObject(var);
350 42136 : root->curOuterParams = lappend(root->curOuterParams, nlp);
351 :
352 : /* And return the replacement Param */
353 42136 : 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 *
364 234 : 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 312 : foreach(lc, root->curOuterParams)
372 : {
373 180 : nlp = (NestLoopParam *) lfirst(lc);
374 180 : if (equal(phv, nlp->paramval))
375 : {
376 : /* Yes, so just make a Param referencing this NLP's slot */
377 102 : param = makeNode(Param);
378 102 : param->paramkind = PARAM_EXEC;
379 102 : param->paramid = nlp->paramno;
380 102 : param->paramtype = exprType((Node *) phv->phexpr);
381 102 : param->paramtypmod = exprTypmod((Node *) phv->phexpr);
382 102 : param->paramcollid = exprCollation((Node *) phv->phexpr);
383 102 : param->location = -1;
384 102 : return param;
385 : }
386 : }
387 :
388 : /* No, so assign a PARAM_EXEC slot for a new NLP */
389 132 : param = generate_new_exec_param(root,
390 132 : exprType((Node *) phv->phexpr),
391 132 : exprTypmod((Node *) phv->phexpr),
392 132 : exprCollation((Node *) phv->phexpr));
393 :
394 : /* Add it to the list of required NLPs */
395 132 : nlp = makeNode(NestLoopParam);
396 132 : nlp->paramno = param->paramid;
397 132 : nlp->paramval = (Var *) copyObject(phv);
398 132 : root->curOuterParams = lappend(root->curOuterParams, nlp);
399 :
400 : /* And return the replacement Param */
401 132 : 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
428 406 : process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
429 : {
430 : ListCell *lc;
431 :
432 908 : foreach(lc, subplan_params)
433 : {
434 502 : PlannerParamItem *pitem = lfirst_node(PlannerParamItem, lc);
435 :
436 502 : if (IsA(pitem->item, Var))
437 : {
438 472 : Var *var = (Var *) pitem->item;
439 : NestLoopParam *nlp;
440 : ListCell *lc2;
441 :
442 : /* If not from a nestloop outer rel, complain */
443 472 : if (!bms_is_member(var->varno, root->curOuterRels))
444 0 : elog(ERROR, "non-LATERAL parameter required by subquery");
445 :
446 : /* Is this param already listed in root->curOuterParams? */
447 680 : foreach(lc2, root->curOuterParams)
448 : {
449 208 : nlp = (NestLoopParam *) lfirst(lc2);
450 208 : if (nlp->paramno == pitem->paramId)
451 : {
452 : Assert(equal(var, nlp->paramval));
453 : /* Present, so nothing to do */
454 0 : break;
455 : }
456 : }
457 472 : if (lc2 == NULL)
458 : {
459 : /* No, so add it */
460 472 : nlp = makeNode(NestLoopParam);
461 472 : nlp->paramno = pitem->paramId;
462 472 : nlp->paramval = copyObject(var);
463 472 : root->curOuterParams = lappend(root->curOuterParams, nlp);
464 : }
465 : }
466 30 : else if (IsA(pitem->item, PlaceHolderVar))
467 : {
468 30 : PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
469 : NestLoopParam *nlp;
470 : ListCell *lc2;
471 :
472 : /* If not from a nestloop outer rel, complain */
473 30 : if (!bms_is_subset(find_placeholder_info(root, phv)->ph_eval_at,
474 30 : root->curOuterRels))
475 0 : elog(ERROR, "non-LATERAL parameter required by subquery");
476 :
477 : /* Is this param already listed in root->curOuterParams? */
478 90 : foreach(lc2, root->curOuterParams)
479 : {
480 60 : nlp = (NestLoopParam *) lfirst(lc2);
481 60 : if (nlp->paramno == pitem->paramId)
482 : {
483 : Assert(equal(phv, nlp->paramval));
484 : /* Present, so nothing to do */
485 0 : break;
486 : }
487 : }
488 30 : if (lc2 == NULL)
489 : {
490 : /* No, so add it */
491 30 : nlp = makeNode(NestLoopParam);
492 30 : nlp->paramno = pitem->paramId;
493 30 : nlp->paramval = (Var *) copyObject(phv);
494 30 : root->curOuterParams = lappend(root->curOuterParams, nlp);
495 : }
496 : }
497 : else
498 0 : elog(ERROR, "unexpected type of subquery parameter");
499 : }
500 406 : }
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 : * XXX Here we also hack up the returned Vars and PHVs so that they do not
508 : * contain nullingrel sets exceeding what is available from the outer side.
509 : * This is needed if we have applied outer join identity 3,
510 : * (A leftjoin B on (Pab)) leftjoin C on (Pb*c)
511 : * = A leftjoin (B leftjoin C on (Pbc)) on (Pab)
512 : * and C contains lateral references to B. It's still safe to apply the
513 : * identity, but the parser will have created those references in the form
514 : * "b*" (i.e., with varnullingrels listing the A/B join), while what we will
515 : * have available from the nestloop's outer side is just "b". We deal with
516 : * that here by stripping the nullingrels down to what is available from the
517 : * outer side according to leftrelids.
518 : *
519 : * That fixes matters for the case of forward application of identity 3.
520 : * If the identity was applied in the reverse direction, we will have
521 : * parameter Vars containing too few nullingrel bits rather than too many.
522 : * Currently, that causes no problems because setrefs.c applies only a
523 : * subset check to nullingrels in NestLoopParams, but we'd have to work
524 : * harder if we ever want to tighten that check. This is all pretty annoying
525 : * because it greatly weakens setrefs.c's cross-check, but the alternative
526 : * seems to be to generate multiple versions of each laterally-parameterized
527 : * subquery, which'd be unduly expensive.
528 : */
529 : List *
530 74628 : identify_current_nestloop_params(PlannerInfo *root, Relids leftrelids)
531 : {
532 : List *result;
533 : ListCell *cell;
534 :
535 74628 : result = NIL;
536 118232 : foreach(cell, root->curOuterParams)
537 : {
538 43604 : NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
539 :
540 : /*
541 : * We are looking for Vars and PHVs that can be supplied by the
542 : * lefthand rels. When we find one, it's okay to modify it in-place
543 : * because all the routines above make a fresh copy to put into
544 : * curOuterParams.
545 : */
546 87046 : if (IsA(nlp->paramval, Var) &&
547 43442 : bms_is_member(nlp->paramval->varno, leftrelids))
548 42608 : {
549 42608 : Var *var = (Var *) nlp->paramval;
550 :
551 42608 : root->curOuterParams = foreach_delete_current(root->curOuterParams,
552 : cell);
553 42608 : var->varnullingrels = bms_intersect(var->varnullingrels,
554 : leftrelids);
555 42608 : result = lappend(result, nlp);
556 : }
557 1158 : else if (IsA(nlp->paramval, PlaceHolderVar) &&
558 324 : bms_is_subset(find_placeholder_info(root,
559 162 : (PlaceHolderVar *) nlp->paramval)->ph_eval_at,
560 : leftrelids))
561 : {
562 162 : PlaceHolderVar *phv = (PlaceHolderVar *) nlp->paramval;
563 :
564 162 : root->curOuterParams = foreach_delete_current(root->curOuterParams,
565 : cell);
566 162 : phv->phnullingrels = bms_intersect(phv->phnullingrels,
567 : leftrelids);
568 162 : result = lappend(result, nlp);
569 : }
570 : }
571 74628 : return result;
572 : }
573 :
574 : /*
575 : * Generate a new Param node that will not conflict with any other.
576 : *
577 : * This is used to create Params representing subplan outputs or
578 : * NestLoop parameters.
579 : *
580 : * We don't need to build a PlannerParamItem for such a Param, but we do
581 : * need to make sure we record the type in paramExecTypes (otherwise,
582 : * there won't be a slot allocated for it).
583 : */
584 : Param *
585 53874 : generate_new_exec_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
586 : Oid paramcollation)
587 : {
588 : Param *retval;
589 :
590 53874 : retval = makeNode(Param);
591 53874 : retval->paramkind = PARAM_EXEC;
592 53874 : retval->paramid = list_length(root->glob->paramExecTypes);
593 53874 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
594 : paramtype);
595 53874 : retval->paramtype = paramtype;
596 53874 : retval->paramtypmod = paramtypmod;
597 53874 : retval->paramcollid = paramcollation;
598 53874 : retval->location = -1;
599 :
600 53874 : return retval;
601 : }
602 :
603 : /*
604 : * Assign a (nonnegative) PARAM_EXEC ID for a special parameter (one that
605 : * is not actually used to carry a value at runtime). Such parameters are
606 : * used for special runtime signaling purposes, such as connecting a
607 : * recursive union node to its worktable scan node or forcing plan
608 : * re-evaluation within the EvalPlanQual mechanism. No actual Param node
609 : * exists with this ID, however.
610 : */
611 : int
612 85660 : assign_special_exec_param(PlannerInfo *root)
613 : {
614 85660 : int paramId = list_length(root->glob->paramExecTypes);
615 :
616 85660 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
617 : InvalidOid);
618 85660 : return paramId;
619 : }
|