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