Age Owner Branch data TLA 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
2430 tgl@sss.pgh.pa.us 66 :CBC 26646 : 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 [ + + ]: 53578 : for (levelsup = var->varlevelsup; levelsup > 0; levelsup--)
74 : 26932 : root = root->parent_root;
75 : :
76 : : /* If there's already a matching PlannerParamItem there, just use it */
77 [ + + + + : 39150 : foreach(ppl, root->plan_params)
+ + ]
78 : : {
79 : 16552 : pitem = (PlannerParamItem *) lfirst(ppl);
80 [ + - ]: 16552 : if (IsA(pitem->item, Var))
81 : : {
82 : 16552 : 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 [ + + ]: 16552 : if (pvar->varno == var->varno &&
90 [ + + ]: 15458 : pvar->varattno == var->varattno &&
91 [ + - ]: 4057 : pvar->vartype == var->vartype &&
92 [ + - ]: 4057 : pvar->vartypmod == var->vartypmod &&
589 93 [ + - ]: 4057 : pvar->varcollid == var->varcollid &&
233 dean.a.rasheed@gmail 94 [ + + + - ]: 8105 : pvar->varreturningtype == var->varreturningtype &&
589 tgl@sss.pgh.pa.us 95 : 4048 : bms_equal(pvar->varnullingrels, var->varnullingrels))
2430 96 : 4048 : return pitem->paramId;
97 : : }
98 : : }
99 : :
100 : : /* Nope, so make a new one */
101 : 22598 : var = copyObject(var);
102 : 22598 : var->varlevelsup = 0;
103 : :
104 : 22598 : pitem = makeNode(PlannerParamItem);
105 : 22598 : pitem->item = (Node *) var;
106 : 22598 : pitem->paramId = list_length(root->glob->paramExecTypes);
107 : 22598 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
108 : : var->vartype);
109 : :
110 : 22598 : root->plan_params = lappend(root->plan_params, pitem);
111 : :
112 : 22598 : 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 : 26646 : replace_outer_var(PlannerInfo *root, Var *var)
122 : : {
123 : : Param *retval;
124 : : int i;
125 : :
126 [ + - - + ]: 26646 : 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 : 26646 : i = assign_param_for_var(root, var);
130 : :
131 : 26646 : retval = makeNode(Param);
132 : 26646 : retval->paramkind = PARAM_EXEC;
133 : 26646 : retval->paramid = i;
134 : 26646 : retval->paramtype = var->vartype;
135 : 26646 : retval->paramtypmod = var->vartypmod;
136 : 26646 : retval->paramcollid = var->varcollid;
137 : 26646 : retval->location = var->location;
138 : :
139 : 26646 : 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 : 30 : 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 [ + + ]: 60 : for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--)
158 : 30 : root = root->parent_root;
159 : :
160 : : /* If there's already a matching PlannerParamItem there, just use it */
161 [ + + + + : 66 : foreach(ppl, root->plan_params)
+ + ]
162 : : {
163 : 36 : pitem = (PlannerParamItem *) lfirst(ppl);
164 [ - + ]: 36 : if (IsA(pitem->item, PlaceHolderVar))
165 : : {
2430 tgl@sss.pgh.pa.us 166 :UBC 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 */
2430 tgl@sss.pgh.pa.us 175 :CBC 30 : phv = copyObject(phv);
176 : 30 : IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
177 [ - + ]: 30 : Assert(phv->phlevelsup == 0);
178 : :
179 : 30 : pitem = makeNode(PlannerParamItem);
180 : 30 : pitem->item = (Node *) phv;
181 : 30 : pitem->paramId = list_length(root->glob->paramExecTypes);
182 : 30 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
183 : 30 : exprType((Node *) phv->phexpr));
184 : :
185 : 30 : root->plan_params = lappend(root->plan_params, pitem);
186 : :
187 : 30 : 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 : 30 : replace_outer_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
199 : : {
200 : : Param *retval;
201 : : int i;
202 : :
203 [ + - - + ]: 30 : 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 : 30 : i = assign_param_for_placeholdervar(root, phv);
207 : :
208 : 30 : retval = makeNode(Param);
209 : 30 : retval->paramkind = PARAM_EXEC;
210 : 30 : retval->paramid = i;
211 : 30 : retval->paramtype = exprType((Node *) phv->phexpr);
212 : 30 : retval->paramtypmod = exprTypmod((Node *) phv->phexpr);
213 : 30 : retval->paramcollid = exprCollation((Node *) phv->phexpr);
214 : 30 : retval->location = -1;
215 : :
216 : 30 : 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 : 26 : replace_outer_agg(PlannerInfo *root, Aggref *agg)
226 : : {
227 : : Param *retval;
228 : : PlannerParamItem *pitem;
229 : : Index levelsup;
230 : :
231 [ + - - + ]: 26 : Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level);
232 : :
233 : : /* Find the query level the Aggref belongs to */
234 [ + + ]: 52 : for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
235 : 26 : 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 : 26 : agg = copyObject(agg);
242 : 26 : IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
243 [ - + ]: 26 : Assert(agg->agglevelsup == 0);
244 : :
245 : 26 : pitem = makeNode(PlannerParamItem);
246 : 26 : pitem->item = (Node *) agg;
247 : 26 : pitem->paramId = list_length(root->glob->paramExecTypes);
248 : 26 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
249 : : agg->aggtype);
250 : :
251 : 26 : root->plan_params = lappend(root->plan_params, pitem);
252 : :
253 : 26 : retval = makeNode(Param);
254 : 26 : retval->paramkind = PARAM_EXEC;
255 : 26 : retval->paramid = pitem->paramId;
256 : 26 : retval->paramtype = agg->aggtype;
257 : 26 : retval->paramtypmod = -1;
258 : 26 : retval->paramcollid = agg->aggcollid;
259 : 26 : retval->location = agg->location;
260 : :
261 : 26 : 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 : 32 : replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp)
272 : : {
273 : : Param *retval;
274 : : PlannerParamItem *pitem;
275 : : Index levelsup;
276 : 32 : Oid ptype = exprType((Node *) grp);
277 : :
278 [ + - - + ]: 32 : Assert(grp->agglevelsup > 0 && grp->agglevelsup < root->query_level);
279 : :
280 : : /* Find the query level the GroupingFunc belongs to */
281 [ + + ]: 68 : for (levelsup = grp->agglevelsup; levelsup > 0; levelsup--)
282 : 36 : 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 : 32 : grp = copyObject(grp);
289 : 32 : IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0);
290 [ - + ]: 32 : Assert(grp->agglevelsup == 0);
291 : :
292 : 32 : pitem = makeNode(PlannerParamItem);
293 : 32 : pitem->item = (Node *) grp;
294 : 32 : pitem->paramId = list_length(root->glob->paramExecTypes);
295 : 32 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
296 : : ptype);
297 : :
298 : 32 : root->plan_params = lappend(root->plan_params, pitem);
299 : :
300 : 32 : retval = makeNode(Param);
301 : 32 : retval->paramkind = PARAM_EXEC;
302 : 32 : retval->paramid = pitem->paramId;
303 : 32 : retval->paramtype = ptype;
304 : 32 : retval->paramtypmod = -1;
305 : 32 : retval->paramcollid = InvalidOid;
306 : 32 : retval->location = grp->location;
307 : :
308 : 32 : 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 *
538 dean.a.rasheed@gmail 318 : 3 : replace_outer_merge_support(PlannerInfo *root, MergeSupportFunc *msf)
319 : : {
320 : : Param *retval;
321 : : PlannerParamItem *pitem;
322 : 3 : Oid ptype = exprType((Node *) msf);
323 : :
324 [ - + ]: 3 : 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 : 3 : root = root->parent_root;
333 [ - + ]: 3 : if (root == NULL)
538 dean.a.rasheed@gmail 334 [ # # ]:UBC 0 : elog(ERROR, "MergeSupportFunc found outside MERGE");
538 dean.a.rasheed@gmail 335 [ - + ]:CBC 3 : } 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 : 3 : msf = copyObject(msf);
342 : :
343 : 3 : pitem = makeNode(PlannerParamItem);
344 : 3 : pitem->item = (Node *) msf;
345 : 3 : pitem->paramId = list_length(root->glob->paramExecTypes);
346 : 3 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
347 : : ptype);
348 : :
349 : 3 : root->plan_params = lappend(root->plan_params, pitem);
350 : :
351 : 3 : retval = makeNode(Param);
352 : 3 : retval->paramkind = PARAM_EXEC;
353 : 3 : retval->paramid = pitem->paramId;
354 : 3 : retval->paramtype = ptype;
355 : 3 : retval->paramtypmod = -1;
356 : 3 : retval->paramcollid = InvalidOid;
357 : 3 : retval->location = msf->location;
358 : :
359 : 3 : 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 *
233 368 : 9 : replace_outer_returning(PlannerInfo *root, ReturningExpr *rexpr)
369 : : {
370 : : Param *retval;
371 : : PlannerParamItem *pitem;
372 : : Index levelsup;
373 : 9 : Oid ptype = exprType((Node *) rexpr->retexpr);
374 : :
375 [ + - - + ]: 9 : Assert(rexpr->retlevelsup > 0 && rexpr->retlevelsup < root->query_level);
376 : :
377 : : /* Find the query level the ReturningExpr belongs to */
378 [ + + ]: 21 : for (levelsup = rexpr->retlevelsup; levelsup > 0; levelsup--)
379 : 12 : 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 : 9 : rexpr = copyObject(rexpr);
386 : 9 : IncrementVarSublevelsUp((Node *) rexpr, -((int) rexpr->retlevelsup), 0);
387 [ - + ]: 9 : Assert(rexpr->retlevelsup == 0);
388 : :
389 : 9 : pitem = makeNode(PlannerParamItem);
390 : 9 : pitem->item = (Node *) rexpr;
391 : 9 : pitem->paramId = list_length(root->glob->paramExecTypes);
392 : 9 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
393 : : ptype);
394 : :
395 : 9 : root->plan_params = lappend(root->plan_params, pitem);
396 : :
397 : 9 : retval = makeNode(Param);
398 : 9 : retval->paramkind = PARAM_EXEC;
399 : 9 : retval->paramid = pitem->paramId;
400 : 9 : retval->paramtype = ptype;
401 : 9 : retval->paramtypmod = exprTypmod((Node *) rexpr->retexpr);
402 : 9 : retval->paramcollid = exprCollation((Node *) rexpr->retexpr);
403 : 9 : retval->location = exprLocation((Node *) rexpr->retexpr);
404 : :
405 : 9 : 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 *
2430 tgl@sss.pgh.pa.us 414 : 45628 : 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 [ + + + + : 50659 : foreach(lc, root->curOuterParams)
+ + ]
422 : : {
423 : 26118 : nlp = (NestLoopParam *) lfirst(lc);
424 [ + + ]: 26118 : if (equal(var, nlp->paramval))
425 : : {
426 : : /* Yes, so just make a Param referencing this NLP's slot */
427 : 21087 : param = makeNode(Param);
428 : 21087 : param->paramkind = PARAM_EXEC;
429 : 21087 : param->paramid = nlp->paramno;
430 : 21087 : param->paramtype = var->vartype;
431 : 21087 : param->paramtypmod = var->vartypmod;
432 : 21087 : param->paramcollid = var->varcollid;
433 : 21087 : param->location = var->location;
434 : 21087 : return param;
435 : : }
436 : : }
437 : :
438 : : /* No, so assign a PARAM_EXEC slot for a new NLP */
439 : 24541 : param = generate_new_exec_param(root,
440 : : var->vartype,
441 : : var->vartypmod,
442 : : var->varcollid);
443 : 24541 : param->location = var->location;
444 : :
445 : : /* Add it to the list of required NLPs */
446 : 24541 : nlp = makeNode(NestLoopParam);
447 : 24541 : nlp->paramno = param->paramid;
448 : 24541 : nlp->paramval = copyObject(var);
449 : 24541 : root->curOuterParams = lappend(root->curOuterParams, nlp);
450 : :
451 : : /* And return the replacement Param */
452 : 24541 : 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 : 162 : 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 [ + + + + : 213 : foreach(lc, root->curOuterParams)
+ + ]
471 : : {
472 : 111 : nlp = (NestLoopParam *) lfirst(lc);
473 [ + + ]: 111 : if (equal(phv, nlp->paramval))
474 : : {
475 : : /* Yes, so just make a Param referencing this NLP's slot */
476 : 60 : param = makeNode(Param);
477 : 60 : param->paramkind = PARAM_EXEC;
478 : 60 : param->paramid = nlp->paramno;
479 : 60 : param->paramtype = exprType((Node *) phv->phexpr);
480 : 60 : param->paramtypmod = exprTypmod((Node *) phv->phexpr);
481 : 60 : param->paramcollid = exprCollation((Node *) phv->phexpr);
482 : 60 : param->location = -1;
483 : 60 : return param;
484 : : }
485 : : }
486 : :
487 : : /* No, so assign a PARAM_EXEC slot for a new NLP */
488 : 102 : param = generate_new_exec_param(root,
489 : 102 : exprType((Node *) phv->phexpr),
490 : 102 : exprTypmod((Node *) phv->phexpr),
491 : 102 : exprCollation((Node *) phv->phexpr));
492 : :
493 : : /* Add it to the list of required NLPs */
494 : 102 : nlp = makeNode(NestLoopParam);
495 : 102 : nlp->paramno = param->paramid;
496 : 102 : nlp->paramval = (Var *) copyObject(phv);
497 : 102 : root->curOuterParams = lappend(root->curOuterParams, nlp);
498 : :
499 : : /* And return the replacement Param */
500 : 102 : 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 : 275 : process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
528 : : {
529 : : ListCell *lc;
530 : :
531 [ + + + + : 604 : foreach(lc, subplan_params)
+ + ]
532 : : {
1510 peter@eisentraut.org 533 : 329 : PlannerParamItem *pitem = lfirst_node(PlannerParamItem, lc);
534 : :
2430 tgl@sss.pgh.pa.us 535 [ + + ]: 329 : if (IsA(pitem->item, Var))
536 : : {
537 : 305 : Var *var = (Var *) pitem->item;
538 : : NestLoopParam *nlp;
539 : : ListCell *lc2;
540 : :
541 : : /* If not from a nestloop outer rel, complain */
542 [ - + ]: 305 : if (!bms_is_member(var->varno, root->curOuterRels))
2430 tgl@sss.pgh.pa.us 543 [ # # ]:UBC 0 : elog(ERROR, "non-LATERAL parameter required by subquery");
544 : :
545 : : /* Is this param already listed in root->curOuterParams? */
1067 drowley@postgresql.o 546 [ + + + + :CBC 410 : foreach(lc2, root->curOuterParams)
+ + ]
547 : : {
548 : 105 : nlp = (NestLoopParam *) lfirst(lc2);
2430 tgl@sss.pgh.pa.us 549 [ - + ]: 105 : if (nlp->paramno == pitem->paramId)
550 : : {
809 tgl@sss.pgh.pa.us 551 [ # # ]:UBC 0 : Assert(equal(var, nlp->paramval));
552 : : /* Present, so nothing to do */
2430 553 : 0 : break;
554 : : }
555 : : }
1067 drowley@postgresql.o 556 [ + - ]:CBC 305 : if (lc2 == NULL)
557 : : {
558 : : /* No, so add it */
2430 tgl@sss.pgh.pa.us 559 : 305 : nlp = makeNode(NestLoopParam);
560 : 305 : nlp->paramno = pitem->paramId;
809 561 : 305 : nlp->paramval = copyObject(var);
2430 562 : 305 : root->curOuterParams = lappend(root->curOuterParams, nlp);
563 : : }
564 : : }
565 [ + - ]: 24 : else if (IsA(pitem->item, PlaceHolderVar))
566 : : {
567 : 24 : PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
568 : : NestLoopParam *nlp;
569 : : ListCell *lc2;
570 : :
571 : : /* If not from a nestloop outer rel, complain */
1116 572 [ - + ]: 24 : if (!bms_is_subset(find_placeholder_info(root, phv)->ph_eval_at,
2430 573 : 24 : root->curOuterRels))
2430 tgl@sss.pgh.pa.us 574 [ # # ]:UBC 0 : elog(ERROR, "non-LATERAL parameter required by subquery");
575 : :
576 : : /* Is this param already listed in root->curOuterParams? */
1067 drowley@postgresql.o 577 [ + + + + :CBC 63 : foreach(lc2, root->curOuterParams)
+ + ]
578 : : {
579 : 39 : nlp = (NestLoopParam *) lfirst(lc2);
2430 tgl@sss.pgh.pa.us 580 [ - + ]: 39 : if (nlp->paramno == pitem->paramId)
581 : : {
809 tgl@sss.pgh.pa.us 582 [ # # ]:UBC 0 : Assert(equal(phv, nlp->paramval));
583 : : /* Present, so nothing to do */
2430 584 : 0 : break;
585 : : }
586 : : }
1067 drowley@postgresql.o 587 [ + - ]:CBC 24 : if (lc2 == NULL)
588 : : {
589 : : /* No, so add it */
2430 tgl@sss.pgh.pa.us 590 : 24 : nlp = makeNode(NestLoopParam);
591 : 24 : nlp->paramno = pitem->paramId;
809 592 : 24 : nlp->paramval = (Var *) copyObject(phv);
2430 593 : 24 : root->curOuterParams = lappend(root->curOuterParams, nlp);
594 : : }
595 : : }
596 : : else
2430 tgl@sss.pgh.pa.us 597 [ # # ]:UBC 0 : elog(ERROR, "unexpected type of subquery parameter");
598 : : }
2430 tgl@sss.pgh.pa.us 599 :CBC 275 : }
600 : :
601 : : /*
602 : : * Identify any NestLoopParams that should be supplied by a NestLoop
603 : : * plan node with the specified lefthand rels and required-outer rels.
604 : : * Remove them from the active root->curOuterParams list and return
605 : : * them as the result list.
606 : : *
607 : : * Vars and PHVs appearing in the result list must have nullingrel sets
608 : : * that could validly appear in the lefthand rel's output. Ordinarily that
609 : : * would be true already, but if we have applied outer join identity 3,
610 : : * there could be more or fewer nullingrel bits in the nodes appearing in
611 : : * curOuterParams than are in the nominal leftrelids. We deal with that by
612 : : * forcing their nullingrel sets to include exactly the outer-join relids
613 : : * that appear in leftrelids and can null the respective Var or PHV.
614 : : * This fix is a bit ad-hoc and intellectually unsatisfactory, because it's
615 : : * essentially jumping to the conclusion that we've placed evaluation of
616 : : * the nestloop parameters correctly, and thus it defeats the intent of the
617 : : * subsequent nullingrel cross-checks in setrefs.c. But the alternative
618 : : * seems to be to generate multiple versions of each laterally-parameterized
619 : : * subquery, which'd be unduly expensive.
620 : : */
621 : : List *
69 622 : 45951 : identify_current_nestloop_params(PlannerInfo *root,
623 : : Relids leftrelids,
624 : : Relids outerrelids)
625 : : {
626 : : List *result;
627 : : Relids allleftrelids;
628 : : ListCell *cell;
629 : :
630 : : /*
631 : : * We'll be able to evaluate a PHV in the lefthand path if it uses the
632 : : * lefthand rels plus any available required-outer rels. But don't do so
633 : : * if it uses *only* required-outer rels; in that case it should be
634 : : * evaluated higher in the tree. For Vars, no such hair-splitting is
635 : : * necessary since they depend on only one relid.
636 : : */
78 637 [ + + ]: 45951 : if (outerrelids)
638 : 526 : allleftrelids = bms_union(leftrelids, outerrelids);
639 : : else
640 : 45425 : allleftrelids = leftrelids;
641 : :
2430 642 : 45951 : result = NIL;
2245 643 [ + + + + : 71517 : foreach(cell, root->curOuterParams)
+ + ]
644 : : {
2430 645 : 25566 : NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
646 : :
647 : : /*
648 : : * We are looking for Vars and PHVs that can be supplied by the
649 : : * lefthand rels. When we find one, it's okay to modify it in-place
650 : : * because all the routines above make a fresh copy to put into
651 : : * curOuterParams.
652 : : */
653 [ + + + + ]: 51000 : if (IsA(nlp->paramval, Var) &&
654 : 25434 : bms_is_member(nlp->paramval->varno, leftrelids))
655 : 24846 : {
809 656 : 24846 : Var *var = (Var *) nlp->paramval;
69 657 : 24846 : RelOptInfo *rel = root->simple_rel_array[var->varno];
658 : :
2245 659 : 24846 : root->curOuterParams = foreach_delete_current(root->curOuterParams,
660 : : cell);
69 661 : 24846 : var->varnullingrels = bms_intersect(rel->nulling_relids,
662 : : leftrelids);
2430 663 : 24846 : result = lappend(result, nlp);
664 : : }
78 665 [ + + ]: 720 : else if (IsA(nlp->paramval, PlaceHolderVar))
666 : : {
809 667 : 132 : PlaceHolderVar *phv = (PlaceHolderVar *) nlp->paramval;
69 668 : 132 : PlaceHolderInfo *phinfo = find_placeholder_info(root, phv);
669 : 132 : Relids eval_at = phinfo->ph_eval_at;
670 : :
78 671 [ + - + + ]: 264 : if (bms_is_subset(eval_at, allleftrelids) &&
672 : 132 : bms_overlap(eval_at, leftrelids))
673 : : {
674 : 126 : root->curOuterParams = foreach_delete_current(root->curOuterParams,
675 : : cell);
676 : :
677 : : /*
678 : : * Deal with an edge case: if the PHV was pulled up out of a
679 : : * subquery and it contains a subquery that was originally
680 : : * pushed down from this query level, then that will still be
681 : : * represented as a SubLink, because SS_process_sublinks won't
682 : : * recurse into outer PHVs, so it didn't get transformed
683 : : * during expression preprocessing in the subquery. We need a
684 : : * version of the PHV that has a SubPlan, which we can get
685 : : * from the current query level's placeholder_list. This is
686 : : * quite grotty of course, but dealing with it earlier in the
687 : : * handling of subplan params would be just as grotty, and it
688 : : * might end up being a waste of cycles if we don't decide to
689 : : * treat the PHV as a NestLoopParam. (Perhaps that whole
690 : : * mechanism should be redesigned someday, but today is not
691 : : * that day.)
692 : : */
69 693 [ + + ]: 126 : if (root->parse->hasSubLinks)
694 : : {
695 : 12 : phv = copyObject(phinfo->ph_var);
696 : :
697 : : /*
698 : : * The ph_var will have empty nullingrels, but that
699 : : * doesn't matter since we're about to overwrite
700 : : * phv->phnullingrels. Other fields should be OK already.
701 : : */
702 : 12 : nlp->paramval = (Var *) phv;
703 : : }
704 : :
705 : 126 : phv->phnullingrels =
706 : 126 : bms_intersect(get_placeholder_nulling_relids(root, phinfo),
707 : : leftrelids);
708 : :
78 709 : 126 : result = lappend(result, nlp);
710 : : }
711 : : }
712 : : }
2430 713 : 45951 : return result;
714 : : }
715 : :
716 : : /*
717 : : * Generate a new Param node that will not conflict with any other.
718 : : *
719 : : * This is used to create Params representing subplan outputs or
720 : : * NestLoop parameters.
721 : : *
722 : : * We don't need to build a PlannerParamItem for such a Param, but we do
723 : : * need to make sure we record the type in paramExecTypes (otherwise,
724 : : * there won't be a slot allocated for it).
725 : : */
726 : : Param *
727 : 31784 : generate_new_exec_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
728 : : Oid paramcollation)
729 : : {
730 : : Param *retval;
731 : :
732 : 31784 : retval = makeNode(Param);
733 : 31784 : retval->paramkind = PARAM_EXEC;
734 : 31784 : retval->paramid = list_length(root->glob->paramExecTypes);
735 : 31784 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
736 : : paramtype);
737 : 31784 : retval->paramtype = paramtype;
738 : 31784 : retval->paramtypmod = paramtypmod;
739 : 31784 : retval->paramcollid = paramcollation;
740 : 31784 : retval->location = -1;
741 : :
742 : 31784 : return retval;
743 : : }
744 : :
745 : : /*
746 : : * Assign a (nonnegative) PARAM_EXEC ID for a special parameter (one that
747 : : * is not actually used to carry a value at runtime). Such parameters are
748 : : * used for special runtime signaling purposes, such as connecting a
749 : : * recursive union node to its worktable scan node or forcing plan
750 : : * re-evaluation within the EvalPlanQual mechanism. No actual Param node
751 : : * exists with this ID, however.
752 : : */
753 : : int
754 : 49549 : assign_special_exec_param(PlannerInfo *root)
755 : : {
756 : 49549 : int paramId = list_length(root->glob->paramExecTypes);
757 : :
758 : 49549 : root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
759 : : InvalidOid);
760 : 49549 : return paramId;
761 : : }
|