LCOV - code coverage report
Current view: top level - src/backend/optimizer/util - paramassign.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 96.4 % 252 243
Test Date: 2026-03-03 13:15:30 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit

            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-2026, 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        28947 : 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        58186 :     for (levelsup = var->varlevelsup; levelsup > 0; levelsup--)
      74        29239 :         root = root->parent_root;
      75              : 
      76              :     /* If there's already a matching PlannerParamItem there, just use it */
      77        41977 :     foreach(ppl, root->plan_params)
      78              :     {
      79        17273 :         pitem = (PlannerParamItem *) lfirst(ppl);
      80        17273 :         if (IsA(pitem->item, Var))
      81              :         {
      82        17273 :             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        17273 :             if (pvar->varno == var->varno &&
      90        16034 :                 pvar->varattno == var->varattno &&
      91         4252 :                 pvar->vartype == var->vartype &&
      92         4252 :                 pvar->vartypmod == var->vartypmod &&
      93         4252 :                 pvar->varcollid == var->varcollid &&
      94         8495 :                 pvar->varreturningtype == var->varreturningtype &&
      95         4243 :                 bms_equal(pvar->varnullingrels, var->varnullingrels))
      96         4243 :                 return pitem->paramId;
      97              :         }
      98              :     }
      99              : 
     100              :     /* Nope, so make a new one */
     101        24704 :     var = copyObject(var);
     102        24704 :     var->varlevelsup = 0;
     103              : 
     104        24704 :     pitem = makeNode(PlannerParamItem);
     105        24704 :     pitem->item = (Node *) var;
     106        24704 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     107        24704 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     108              :                                              var->vartype);
     109              : 
     110        24704 :     root->plan_params = lappend(root->plan_params, pitem);
     111              : 
     112        24704 :     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        28947 : replace_outer_var(PlannerInfo *root, Var *var)
     122              : {
     123              :     Param      *retval;
     124              :     int         i;
     125              : 
     126              :     Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);
     127              : 
     128              :     /* Find the Var in the appropriate plan_params, or add it if not present */
     129        28947 :     i = assign_param_for_var(root, var);
     130              : 
     131        28947 :     retval = makeNode(Param);
     132        28947 :     retval->paramkind = PARAM_EXEC;
     133        28947 :     retval->paramid = i;
     134        28947 :     retval->paramtype = var->vartype;
     135        28947 :     retval->paramtypmod = var->vartypmod;
     136        28947 :     retval->paramcollid = var->varcollid;
     137        28947 :     retval->location = var->location;
     138              : 
     139        28947 :     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              :         {
     166            0 :             PlaceHolderVar *pphv = (PlaceHolderVar *) pitem->item;
     167              : 
     168              :             /* We assume comparing the PHIDs is sufficient */
     169            0 :             if (pphv->phid == phv->phid)
     170            0 :                 return pitem->paramId;
     171              :         }
     172              :     }
     173              : 
     174              :     /* Nope, so make a new one */
     175           30 :     phv = copyObject(phv);
     176           30 :     IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
     177              :     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              :     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           29 : replace_outer_agg(PlannerInfo *root, Aggref *agg)
     226              : {
     227              :     Param      *retval;
     228              :     PlannerParamItem *pitem;
     229              :     Index       levelsup;
     230              : 
     231              :     Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level);
     232              : 
     233              :     /* Find the query level the Aggref belongs to */
     234           58 :     for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
     235           29 :         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           29 :     agg = copyObject(agg);
     242           29 :     IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
     243              :     Assert(agg->agglevelsup == 0);
     244              : 
     245           29 :     pitem = makeNode(PlannerParamItem);
     246           29 :     pitem->item = (Node *) agg;
     247           29 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     248           29 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     249              :                                              agg->aggtype);
     250              : 
     251           29 :     root->plan_params = lappend(root->plan_params, pitem);
     252              : 
     253           29 :     retval = makeNode(Param);
     254           29 :     retval->paramkind = PARAM_EXEC;
     255           29 :     retval->paramid = pitem->paramId;
     256           29 :     retval->paramtype = agg->aggtype;
     257           29 :     retval->paramtypmod = -1;
     258           29 :     retval->paramcollid = agg->aggcollid;
     259           29 :     retval->location = agg->location;
     260              : 
     261           29 :     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              :     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              :     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 *
     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              :     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)
     334            0 :             elog(ERROR, "MergeSupportFunc found outside MERGE");
     335            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 *
     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              :     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              :     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 *
     414        58577 : 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        75879 :     foreach(lc, root->curOuterParams)
     422              :     {
     423        43303 :         nlp = (NestLoopParam *) lfirst(lc);
     424        43303 :         if (equal(var, nlp->paramval))
     425              :         {
     426              :             /* Yes, so just make a Param referencing this NLP's slot */
     427        26001 :             param = makeNode(Param);
     428        26001 :             param->paramkind = PARAM_EXEC;
     429        26001 :             param->paramid = nlp->paramno;
     430        26001 :             param->paramtype = var->vartype;
     431        26001 :             param->paramtypmod = var->vartypmod;
     432        26001 :             param->paramcollid = var->varcollid;
     433        26001 :             param->location = var->location;
     434        26001 :             return param;
     435              :         }
     436              :     }
     437              : 
     438              :     /* No, so assign a PARAM_EXEC slot for a new NLP */
     439        32576 :     param = generate_new_exec_param(root,
     440              :                                     var->vartype,
     441              :                                     var->vartypmod,
     442              :                                     var->varcollid);
     443        32576 :     param->location = var->location;
     444              : 
     445              :     /* Add it to the list of required NLPs */
     446        32576 :     nlp = makeNode(NestLoopParam);
     447        32576 :     nlp->paramno = param->paramid;
     448        32576 :     nlp->paramval = copyObject(var);
     449        32576 :     root->curOuterParams = lappend(root->curOuterParams, nlp);
     450              : 
     451              :     /* And return the replacement Param */
     452        32576 :     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          659 : process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
     528              : {
     529              :     ListCell   *lc;
     530              : 
     531         1583 :     foreach(lc, subplan_params)
     532              :     {
     533          924 :         PlannerParamItem *pitem = lfirst_node(PlannerParamItem, lc);
     534              : 
     535          924 :         if (IsA(pitem->item, Var))
     536              :         {
     537          900 :             Var        *var = (Var *) pitem->item;
     538              :             NestLoopParam *nlp;
     539              :             ListCell   *lc2;
     540              : 
     541              :             /* If not from a nestloop outer rel, complain */
     542          900 :             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         1222 :             foreach(lc2, root->curOuterParams)
     547              :             {
     548          322 :                 nlp = (NestLoopParam *) lfirst(lc2);
     549          322 :                 if (nlp->paramno == pitem->paramId)
     550              :                 {
     551              :                     Assert(equal(var, nlp->paramval));
     552              :                     /* Present, so nothing to do */
     553            0 :                     break;
     554              :                 }
     555              :             }
     556          900 :             if (lc2 == NULL)
     557              :             {
     558              :                 /* No, so add it */
     559          900 :                 nlp = makeNode(NestLoopParam);
     560          900 :                 nlp->paramno = pitem->paramId;
     561          900 :                 nlp->paramval = copyObject(var);
     562          900 :                 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 */
     572           24 :             if (!bms_is_subset(find_placeholder_info(root, phv)->ph_eval_at,
     573           24 :                                root->curOuterRels))
     574            0 :                 elog(ERROR, "non-LATERAL parameter required by subquery");
     575              : 
     576              :             /* Is this param already listed in root->curOuterParams? */
     577           63 :             foreach(lc2, root->curOuterParams)
     578              :             {
     579           39 :                 nlp = (NestLoopParam *) lfirst(lc2);
     580           39 :                 if (nlp->paramno == pitem->paramId)
     581              :                 {
     582              :                     Assert(equal(phv, nlp->paramval));
     583              :                     /* Present, so nothing to do */
     584            0 :                     break;
     585              :                 }
     586              :             }
     587           24 :             if (lc2 == NULL)
     588              :             {
     589              :                 /* No, so add it */
     590           24 :                 nlp = makeNode(NestLoopParam);
     591           24 :                 nlp->paramno = pitem->paramId;
     592           24 :                 nlp->paramval = (Var *) copyObject(phv);
     593           24 :                 root->curOuterParams = lappend(root->curOuterParams, nlp);
     594              :             }
     595              :         }
     596              :         else
     597            0 :             elog(ERROR, "unexpected type of subquery parameter");
     598              :     }
     599          659 : }
     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        53919 : 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              :      */
     637        53919 :     if (outerrelids)
     638          553 :         allleftrelids = bms_union(leftrelids, outerrelids);
     639              :     else
     640        53366 :         allleftrelids = leftrelids;
     641              : 
     642        53919 :     result = NIL;
     643        88148 :     foreach(cell, root->curOuterParams)
     644              :     {
     645        34229 :         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        68326 :         if (IsA(nlp->paramval, Var) &&
     654        34097 :             bms_is_member(nlp->paramval->varno, leftrelids))
     655        33476 :         {
     656        33476 :             Var        *var = (Var *) nlp->paramval;
     657        33476 :             RelOptInfo *rel = root->simple_rel_array[var->varno];
     658              : 
     659        33476 :             root->curOuterParams = foreach_delete_current(root->curOuterParams,
     660              :                                                           cell);
     661        33476 :             var->varnullingrels = bms_intersect(rel->nulling_relids,
     662              :                                                 leftrelids);
     663        33476 :             result = lappend(result, nlp);
     664              :         }
     665          753 :         else if (IsA(nlp->paramval, PlaceHolderVar))
     666              :         {
     667          132 :             PlaceHolderVar *phv = (PlaceHolderVar *) nlp->paramval;
     668          132 :             PlaceHolderInfo *phinfo = find_placeholder_info(root, phv);
     669          132 :             Relids      eval_at = phinfo->ph_eval_at;
     670              : 
     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              :                  */
     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              : 
     709          126 :                 result = lappend(result, nlp);
     710              :             }
     711              :         }
     712              :     }
     713        53919 :     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        40488 : generate_new_exec_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
     728              :                         Oid paramcollation)
     729              : {
     730              :     Param      *retval;
     731              : 
     732        40488 :     retval = makeNode(Param);
     733        40488 :     retval->paramkind = PARAM_EXEC;
     734        40488 :     retval->paramid = list_length(root->glob->paramExecTypes);
     735        40488 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     736              :                                              paramtype);
     737        40488 :     retval->paramtype = paramtype;
     738        40488 :     retval->paramtypmod = paramtypmod;
     739        40488 :     retval->paramcollid = paramcollation;
     740        40488 :     retval->location = -1;
     741              : 
     742        40488 :     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        56636 : assign_special_exec_param(PlannerInfo *root)
     755              : {
     756        56636 :     int         paramId = list_length(root->glob->paramExecTypes);
     757              : 
     758        56636 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     759              :                                              InvalidOid);
     760        56636 :     return paramId;
     761              : }
        

Generated by: LCOV version 2.0-1