Coverage Report

Created: 2025-10-09 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/optimizer/util/paramassign.c
Line
Count
Source
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
assign_param_for_var(PlannerInfo *root, Var *var)
67
0
{
68
0
  ListCell   *ppl;
69
0
  PlannerParamItem *pitem;
70
0
  Index   levelsup;
71
72
  /* Find the query level the Var belongs to */
73
0
  for (levelsup = var->varlevelsup; levelsup > 0; levelsup--)
74
0
    root = root->parent_root;
75
76
  /* If there's already a matching PlannerParamItem there, just use it */
77
0
  foreach(ppl, root->plan_params)
78
0
  {
79
0
    pitem = (PlannerParamItem *) lfirst(ppl);
80
0
    if (IsA(pitem->item, Var))
81
0
    {
82
0
      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
0
      if (pvar->varno == var->varno &&
90
0
        pvar->varattno == var->varattno &&
91
0
        pvar->vartype == var->vartype &&
92
0
        pvar->vartypmod == var->vartypmod &&
93
0
        pvar->varcollid == var->varcollid &&
94
0
        pvar->varreturningtype == var->varreturningtype &&
95
0
        bms_equal(pvar->varnullingrels, var->varnullingrels))
96
0
        return pitem->paramId;
97
0
    }
98
0
  }
99
100
  /* Nope, so make a new one */
101
0
  var = copyObject(var);
102
0
  var->varlevelsup = 0;
103
104
0
  pitem = makeNode(PlannerParamItem);
105
0
  pitem->item = (Node *) var;
106
0
  pitem->paramId = list_length(root->glob->paramExecTypes);
107
0
  root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
108
0
                       var->vartype);
109
110
0
  root->plan_params = lappend(root->plan_params, pitem);
111
112
0
  return pitem->paramId;
113
0
}
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
replace_outer_var(PlannerInfo *root, Var *var)
122
0
{
123
0
  Param    *retval;
124
0
  int     i;
125
126
0
  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
0
  i = assign_param_for_var(root, var);
130
131
0
  retval = makeNode(Param);
132
0
  retval->paramkind = PARAM_EXEC;
133
0
  retval->paramid = i;
134
0
  retval->paramtype = var->vartype;
135
0
  retval->paramtypmod = var->vartypmod;
136
0
  retval->paramcollid = var->varcollid;
137
0
  retval->location = var->location;
138
139
0
  return retval;
140
0
}
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
assign_param_for_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
151
0
{
152
0
  ListCell   *ppl;
153
0
  PlannerParamItem *pitem;
154
0
  Index   levelsup;
155
156
  /* Find the query level the PHV belongs to */
157
0
  for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--)
158
0
    root = root->parent_root;
159
160
  /* If there's already a matching PlannerParamItem there, just use it */
161
0
  foreach(ppl, root->plan_params)
162
0
  {
163
0
    pitem = (PlannerParamItem *) lfirst(ppl);
164
0
    if (IsA(pitem->item, PlaceHolderVar))
165
0
    {
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
0
    }
172
0
  }
173
174
  /* Nope, so make a new one */
175
0
  phv = copyObject(phv);
176
0
  IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
177
0
  Assert(phv->phlevelsup == 0);
178
179
0
  pitem = makeNode(PlannerParamItem);
180
0
  pitem->item = (Node *) phv;
181
0
  pitem->paramId = list_length(root->glob->paramExecTypes);
182
0
  root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
183
0
                       exprType((Node *) phv->phexpr));
184
185
0
  root->plan_params = lappend(root->plan_params, pitem);
186
187
0
  return pitem->paramId;
188
0
}
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
replace_outer_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
199
0
{
200
0
  Param    *retval;
201
0
  int     i;
202
203
0
  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
0
  i = assign_param_for_placeholdervar(root, phv);
207
208
0
  retval = makeNode(Param);
209
0
  retval->paramkind = PARAM_EXEC;
210
0
  retval->paramid = i;
211
0
  retval->paramtype = exprType((Node *) phv->phexpr);
212
0
  retval->paramtypmod = exprTypmod((Node *) phv->phexpr);
213
0
  retval->paramcollid = exprCollation((Node *) phv->phexpr);
214
0
  retval->location = -1;
215
216
0
  return retval;
217
0
}
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
replace_outer_agg(PlannerInfo *root, Aggref *agg)
226
0
{
227
0
  Param    *retval;
228
0
  PlannerParamItem *pitem;
229
0
  Index   levelsup;
230
231
0
  Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level);
232
233
  /* Find the query level the Aggref belongs to */
234
0
  for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
235
0
    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
0
  agg = copyObject(agg);
242
0
  IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
243
0
  Assert(agg->agglevelsup == 0);
244
245
0
  pitem = makeNode(PlannerParamItem);
246
0
  pitem->item = (Node *) agg;
247
0
  pitem->paramId = list_length(root->glob->paramExecTypes);
248
0
  root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
249
0
                       agg->aggtype);
250
251
0
  root->plan_params = lappend(root->plan_params, pitem);
252
253
0
  retval = makeNode(Param);
254
0
  retval->paramkind = PARAM_EXEC;
255
0
  retval->paramid = pitem->paramId;
256
0
  retval->paramtype = agg->aggtype;
257
0
  retval->paramtypmod = -1;
258
0
  retval->paramcollid = agg->aggcollid;
259
0
  retval->location = agg->location;
260
261
0
  return retval;
262
0
}
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
replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp)
272
0
{
273
0
  Param    *retval;
274
0
  PlannerParamItem *pitem;
275
0
  Index   levelsup;
276
0
  Oid     ptype = exprType((Node *) grp);
277
278
0
  Assert(grp->agglevelsup > 0 && grp->agglevelsup < root->query_level);
279
280
  /* Find the query level the GroupingFunc belongs to */
281
0
  for (levelsup = grp->agglevelsup; levelsup > 0; levelsup--)
282
0
    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
0
  grp = copyObject(grp);
289
0
  IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0);
290
0
  Assert(grp->agglevelsup == 0);
291
292
0
  pitem = makeNode(PlannerParamItem);
293
0
  pitem->item = (Node *) grp;
294
0
  pitem->paramId = list_length(root->glob->paramExecTypes);
295
0
  root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
296
0
                       ptype);
297
298
0
  root->plan_params = lappend(root->plan_params, pitem);
299
300
0
  retval = makeNode(Param);
301
0
  retval->paramkind = PARAM_EXEC;
302
0
  retval->paramid = pitem->paramId;
303
0
  retval->paramtype = ptype;
304
0
  retval->paramtypmod = -1;
305
0
  retval->paramcollid = InvalidOid;
306
0
  retval->location = grp->location;
307
308
0
  return retval;
309
0
}
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
replace_outer_merge_support(PlannerInfo *root, MergeSupportFunc *msf)
319
0
{
320
0
  Param    *retval;
321
0
  PlannerParamItem *pitem;
322
0
  Oid     ptype = exprType((Node *) msf);
323
324
0
  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
0
  do
331
0
  {
332
0
    root = root->parent_root;
333
0
    if (root == NULL)
334
0
      elog(ERROR, "MergeSupportFunc found outside MERGE");
335
0
  } 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
0
  msf = copyObject(msf);
342
343
0
  pitem = makeNode(PlannerParamItem);
344
0
  pitem->item = (Node *) msf;
345
0
  pitem->paramId = list_length(root->glob->paramExecTypes);
346
0
  root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
347
0
                       ptype);
348
349
0
  root->plan_params = lappend(root->plan_params, pitem);
350
351
0
  retval = makeNode(Param);
352
0
  retval->paramkind = PARAM_EXEC;
353
0
  retval->paramid = pitem->paramId;
354
0
  retval->paramtype = ptype;
355
0
  retval->paramtypmod = -1;
356
0
  retval->paramcollid = InvalidOid;
357
0
  retval->location = msf->location;
358
359
0
  return retval;
360
0
}
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
replace_outer_returning(PlannerInfo *root, ReturningExpr *rexpr)
369
0
{
370
0
  Param    *retval;
371
0
  PlannerParamItem *pitem;
372
0
  Index   levelsup;
373
0
  Oid     ptype = exprType((Node *) rexpr->retexpr);
374
375
0
  Assert(rexpr->retlevelsup > 0 && rexpr->retlevelsup < root->query_level);
376
377
  /* Find the query level the ReturningExpr belongs to */
378
0
  for (levelsup = rexpr->retlevelsup; levelsup > 0; levelsup--)
379
0
    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
0
  rexpr = copyObject(rexpr);
386
0
  IncrementVarSublevelsUp((Node *) rexpr, -((int) rexpr->retlevelsup), 0);
387
0
  Assert(rexpr->retlevelsup == 0);
388
389
0
  pitem = makeNode(PlannerParamItem);
390
0
  pitem->item = (Node *) rexpr;
391
0
  pitem->paramId = list_length(root->glob->paramExecTypes);
392
0
  root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
393
0
                       ptype);
394
395
0
  root->plan_params = lappend(root->plan_params, pitem);
396
397
0
  retval = makeNode(Param);
398
0
  retval->paramkind = PARAM_EXEC;
399
0
  retval->paramid = pitem->paramId;
400
0
  retval->paramtype = ptype;
401
0
  retval->paramtypmod = exprTypmod((Node *) rexpr->retexpr);
402
0
  retval->paramcollid = exprCollation((Node *) rexpr->retexpr);
403
0
  retval->location = exprLocation((Node *) rexpr->retexpr);
404
405
0
  return retval;
406
0
}
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
replace_nestloop_param_var(PlannerInfo *root, Var *var)
415
0
{
416
0
  Param    *param;
417
0
  NestLoopParam *nlp;
418
0
  ListCell   *lc;
419
420
  /* Is this Var already listed in root->curOuterParams? */
421
0
  foreach(lc, root->curOuterParams)
422
0
  {
423
0
    nlp = (NestLoopParam *) lfirst(lc);
424
0
    if (equal(var, nlp->paramval))
425
0
    {
426
      /* Yes, so just make a Param referencing this NLP's slot */
427
0
      param = makeNode(Param);
428
0
      param->paramkind = PARAM_EXEC;
429
0
      param->paramid = nlp->paramno;
430
0
      param->paramtype = var->vartype;
431
0
      param->paramtypmod = var->vartypmod;
432
0
      param->paramcollid = var->varcollid;
433
0
      param->location = var->location;
434
0
      return param;
435
0
    }
436
0
  }
437
438
  /* No, so assign a PARAM_EXEC slot for a new NLP */
439
0
  param = generate_new_exec_param(root,
440
0
                  var->vartype,
441
0
                  var->vartypmod,
442
0
                  var->varcollid);
443
0
  param->location = var->location;
444
445
  /* Add it to the list of required NLPs */
446
0
  nlp = makeNode(NestLoopParam);
447
0
  nlp->paramno = param->paramid;
448
0
  nlp->paramval = copyObject(var);
449
0
  root->curOuterParams = lappend(root->curOuterParams, nlp);
450
451
  /* And return the replacement Param */
452
0
  return param;
453
0
}
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
replace_nestloop_param_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
464
0
{
465
0
  Param    *param;
466
0
  NestLoopParam *nlp;
467
0
  ListCell   *lc;
468
469
  /* Is this PHV already listed in root->curOuterParams? */
470
0
  foreach(lc, root->curOuterParams)
471
0
  {
472
0
    nlp = (NestLoopParam *) lfirst(lc);
473
0
    if (equal(phv, nlp->paramval))
474
0
    {
475
      /* Yes, so just make a Param referencing this NLP's slot */
476
0
      param = makeNode(Param);
477
0
      param->paramkind = PARAM_EXEC;
478
0
      param->paramid = nlp->paramno;
479
0
      param->paramtype = exprType((Node *) phv->phexpr);
480
0
      param->paramtypmod = exprTypmod((Node *) phv->phexpr);
481
0
      param->paramcollid = exprCollation((Node *) phv->phexpr);
482
0
      param->location = -1;
483
0
      return param;
484
0
    }
485
0
  }
486
487
  /* No, so assign a PARAM_EXEC slot for a new NLP */
488
0
  param = generate_new_exec_param(root,
489
0
                  exprType((Node *) phv->phexpr),
490
0
                  exprTypmod((Node *) phv->phexpr),
491
0
                  exprCollation((Node *) phv->phexpr));
492
493
  /* Add it to the list of required NLPs */
494
0
  nlp = makeNode(NestLoopParam);
495
0
  nlp->paramno = param->paramid;
496
0
  nlp->paramval = (Var *) copyObject(phv);
497
0
  root->curOuterParams = lappend(root->curOuterParams, nlp);
498
499
  /* And return the replacement Param */
500
0
  return param;
501
0
}
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
process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
528
0
{
529
0
  ListCell   *lc;
530
531
0
  foreach(lc, subplan_params)
532
0
  {
533
0
    PlannerParamItem *pitem = lfirst_node(PlannerParamItem, lc);
534
535
0
    if (IsA(pitem->item, Var))
536
0
    {
537
0
      Var      *var = (Var *) pitem->item;
538
0
      NestLoopParam *nlp;
539
0
      ListCell   *lc2;
540
541
      /* If not from a nestloop outer rel, complain */
542
0
      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
0
      foreach(lc2, root->curOuterParams)
547
0
      {
548
0
        nlp = (NestLoopParam *) lfirst(lc2);
549
0
        if (nlp->paramno == pitem->paramId)
550
0
        {
551
0
          Assert(equal(var, nlp->paramval));
552
          /* Present, so nothing to do */
553
0
          break;
554
0
        }
555
0
      }
556
0
      if (lc2 == NULL)
557
0
      {
558
        /* No, so add it */
559
0
        nlp = makeNode(NestLoopParam);
560
0
        nlp->paramno = pitem->paramId;
561
0
        nlp->paramval = copyObject(var);
562
0
        root->curOuterParams = lappend(root->curOuterParams, nlp);
563
0
      }
564
0
    }
565
0
    else if (IsA(pitem->item, PlaceHolderVar))
566
0
    {
567
0
      PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
568
0
      NestLoopParam *nlp;
569
0
      ListCell   *lc2;
570
571
      /* If not from a nestloop outer rel, complain */
572
0
      if (!bms_is_subset(find_placeholder_info(root, phv)->ph_eval_at,
573
0
                 root->curOuterRels))
574
0
        elog(ERROR, "non-LATERAL parameter required by subquery");
575
576
      /* Is this param already listed in root->curOuterParams? */
577
0
      foreach(lc2, root->curOuterParams)
578
0
      {
579
0
        nlp = (NestLoopParam *) lfirst(lc2);
580
0
        if (nlp->paramno == pitem->paramId)
581
0
        {
582
0
          Assert(equal(phv, nlp->paramval));
583
          /* Present, so nothing to do */
584
0
          break;
585
0
        }
586
0
      }
587
0
      if (lc2 == NULL)
588
0
      {
589
        /* No, so add it */
590
0
        nlp = makeNode(NestLoopParam);
591
0
        nlp->paramno = pitem->paramId;
592
0
        nlp->paramval = (Var *) copyObject(phv);
593
0
        root->curOuterParams = lappend(root->curOuterParams, nlp);
594
0
      }
595
0
    }
596
0
    else
597
0
      elog(ERROR, "unexpected type of subquery parameter");
598
0
  }
599
0
}
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 *
622
identify_current_nestloop_params(PlannerInfo *root,
623
                 Relids leftrelids,
624
                 Relids outerrelids)
625
0
{
626
0
  List     *result;
627
0
  Relids    allleftrelids;
628
0
  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
   */
637
0
  if (outerrelids)
638
0
    allleftrelids = bms_union(leftrelids, outerrelids);
639
0
  else
640
0
    allleftrelids = leftrelids;
641
642
0
  result = NIL;
643
0
  foreach(cell, root->curOuterParams)
644
0
  {
645
0
    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
0
    if (IsA(nlp->paramval, Var) &&
654
0
      bms_is_member(nlp->paramval->varno, leftrelids))
655
0
    {
656
0
      Var      *var = (Var *) nlp->paramval;
657
0
      RelOptInfo *rel = root->simple_rel_array[var->varno];
658
659
0
      root->curOuterParams = foreach_delete_current(root->curOuterParams,
660
0
                              cell);
661
0
      var->varnullingrels = bms_intersect(rel->nulling_relids,
662
0
                        leftrelids);
663
0
      result = lappend(result, nlp);
664
0
    }
665
0
    else if (IsA(nlp->paramval, PlaceHolderVar))
666
0
    {
667
0
      PlaceHolderVar *phv = (PlaceHolderVar *) nlp->paramval;
668
0
      PlaceHolderInfo *phinfo = find_placeholder_info(root, phv);
669
0
      Relids    eval_at = phinfo->ph_eval_at;
670
671
0
      if (bms_is_subset(eval_at, allleftrelids) &&
672
0
        bms_overlap(eval_at, leftrelids))
673
0
      {
674
0
        root->curOuterParams = foreach_delete_current(root->curOuterParams,
675
0
                                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
         */
693
0
        if (root->parse->hasSubLinks)
694
0
        {
695
0
          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
0
          nlp->paramval = (Var *) phv;
703
0
        }
704
705
0
        phv->phnullingrels =
706
0
          bms_intersect(get_placeholder_nulling_relids(root, phinfo),
707
0
                  leftrelids);
708
709
0
        result = lappend(result, nlp);
710
0
      }
711
0
    }
712
0
  }
713
0
  return result;
714
0
}
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
generate_new_exec_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
728
            Oid paramcollation)
729
0
{
730
0
  Param    *retval;
731
732
0
  retval = makeNode(Param);
733
0
  retval->paramkind = PARAM_EXEC;
734
0
  retval->paramid = list_length(root->glob->paramExecTypes);
735
0
  root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
736
0
                       paramtype);
737
0
  retval->paramtype = paramtype;
738
0
  retval->paramtypmod = paramtypmod;
739
0
  retval->paramcollid = paramcollation;
740
0
  retval->location = -1;
741
742
0
  return retval;
743
0
}
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
assign_special_exec_param(PlannerInfo *root)
755
0
{
756
0
  int     paramId = list_length(root->glob->paramExecTypes);
757
758
0
  root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
759
0
                       InvalidOid);
760
0
  return paramId;
761
0
}