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-04-07 14:16: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        43667 : 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        87734 :     for (levelsup = var->varlevelsup; levelsup > 0; levelsup--)
      74        44067 :         root = root->parent_root;
      75              : 
      76              :     /* If there's already a matching PlannerParamItem there, just use it */
      77        64535 :     foreach(ppl, root->plan_params)
      78              :     {
      79        27550 :         pitem = (PlannerParamItem *) lfirst(ppl);
      80        27550 :         if (IsA(pitem->item, Var))
      81              :         {
      82        27550 :             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        27550 :             if (pvar->varno == var->varno &&
      90        25897 :                 pvar->varattno == var->varattno &&
      91         6697 :                 pvar->vartype == var->vartype &&
      92         6697 :                 pvar->vartypmod == var->vartypmod &&
      93         6697 :                 pvar->varcollid == var->varcollid &&
      94        13379 :                 pvar->varreturningtype == var->varreturningtype &&
      95         6682 :                 bms_equal(pvar->varnullingrels, var->varnullingrels))
      96         6682 :                 return pitem->paramId;
      97              :         }
      98              :     }
      99              : 
     100              :     /* Nope, so make a new one */
     101        36985 :     var = copyObject(var);
     102        36985 :     var->varlevelsup = 0;
     103              : 
     104        36985 :     pitem = makeNode(PlannerParamItem);
     105        36985 :     pitem->item = (Node *) var;
     106        36985 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     107        36985 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     108              :                                              var->vartype);
     109              : 
     110        36985 :     root->plan_params = lappend(root->plan_params, pitem);
     111              : 
     112        36985 :     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        43667 : 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        43667 :     i = assign_param_for_var(root, var);
     130              : 
     131        43667 :     retval = makeNode(Param);
     132        43667 :     retval->paramkind = PARAM_EXEC;
     133        43667 :     retval->paramid = i;
     134        43667 :     retval->paramtype = var->vartype;
     135        43667 :     retval->paramtypmod = var->vartypmod;
     136        43667 :     retval->paramcollid = var->varcollid;
     137        43667 :     retval->location = var->location;
     138              : 
     139        43667 :     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           50 : 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          100 :     for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--)
     158           50 :         root = root->parent_root;
     159              : 
     160              :     /* If there's already a matching PlannerParamItem there, just use it */
     161          110 :     foreach(ppl, root->plan_params)
     162              :     {
     163           60 :         pitem = (PlannerParamItem *) lfirst(ppl);
     164           60 :         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           50 :     phv = copyObject(phv);
     176           50 :     IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
     177              :     Assert(phv->phlevelsup == 0);
     178              : 
     179           50 :     pitem = makeNode(PlannerParamItem);
     180           50 :     pitem->item = (Node *) phv;
     181           50 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     182           50 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     183           50 :                                              exprType((Node *) phv->phexpr));
     184              : 
     185           50 :     root->plan_params = lappend(root->plan_params, pitem);
     186              : 
     187           50 :     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           50 : 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           50 :     i = assign_param_for_placeholdervar(root, phv);
     207              : 
     208           50 :     retval = makeNode(Param);
     209           50 :     retval->paramkind = PARAM_EXEC;
     210           50 :     retval->paramid = i;
     211           50 :     retval->paramtype = exprType((Node *) phv->phexpr);
     212           50 :     retval->paramtypmod = exprTypmod((Node *) phv->phexpr);
     213           50 :     retval->paramcollid = exprCollation((Node *) phv->phexpr);
     214           50 :     retval->location = -1;
     215              : 
     216           50 :     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           57 : 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          114 :     for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
     235           57 :         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           57 :     agg = copyObject(agg);
     242           57 :     IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
     243              :     Assert(agg->agglevelsup == 0);
     244              : 
     245           57 :     pitem = makeNode(PlannerParamItem);
     246           57 :     pitem->item = (Node *) agg;
     247           57 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     248           57 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     249              :                                              agg->aggtype);
     250              : 
     251           57 :     root->plan_params = lappend(root->plan_params, pitem);
     252              : 
     253           57 :     retval = makeNode(Param);
     254           57 :     retval->paramkind = PARAM_EXEC;
     255           57 :     retval->paramid = pitem->paramId;
     256           57 :     retval->paramtype = agg->aggtype;
     257           57 :     retval->paramtypmod = -1;
     258           57 :     retval->paramcollid = agg->aggcollid;
     259           57 :     retval->location = agg->location;
     260              : 
     261           57 :     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           57 : replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp)
     272              : {
     273              :     Param      *retval;
     274              :     PlannerParamItem *pitem;
     275              :     Index       levelsup;
     276           57 :     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          120 :     for (levelsup = grp->agglevelsup; levelsup > 0; levelsup--)
     282           63 :         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           57 :     grp = copyObject(grp);
     289           57 :     IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0);
     290              :     Assert(grp->agglevelsup == 0);
     291              : 
     292           57 :     pitem = makeNode(PlannerParamItem);
     293           57 :     pitem->item = (Node *) grp;
     294           57 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     295           57 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     296              :                                              ptype);
     297              : 
     298           57 :     root->plan_params = lappend(root->plan_params, pitem);
     299              : 
     300           57 :     retval = makeNode(Param);
     301           57 :     retval->paramkind = PARAM_EXEC;
     302           57 :     retval->paramid = pitem->paramId;
     303           57 :     retval->paramtype = ptype;
     304           57 :     retval->paramtypmod = -1;
     305           57 :     retval->paramcollid = InvalidOid;
     306           57 :     retval->location = grp->location;
     307              : 
     308           57 :     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            5 : replace_outer_merge_support(PlannerInfo *root, MergeSupportFunc *msf)
     319              : {
     320              :     Param      *retval;
     321              :     PlannerParamItem *pitem;
     322            5 :     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            5 :         root = root->parent_root;
     333            5 :         if (root == NULL)
     334            0 :             elog(ERROR, "MergeSupportFunc found outside MERGE");
     335            5 :     } 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            5 :     msf = copyObject(msf);
     342              : 
     343            5 :     pitem = makeNode(PlannerParamItem);
     344            5 :     pitem->item = (Node *) msf;
     345            5 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     346            5 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     347              :                                              ptype);
     348              : 
     349            5 :     root->plan_params = lappend(root->plan_params, pitem);
     350              : 
     351            5 :     retval = makeNode(Param);
     352            5 :     retval->paramkind = PARAM_EXEC;
     353            5 :     retval->paramid = pitem->paramId;
     354            5 :     retval->paramtype = ptype;
     355            5 :     retval->paramtypmod = -1;
     356            5 :     retval->paramcollid = InvalidOid;
     357            5 :     retval->location = msf->location;
     358              : 
     359            5 :     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           15 : replace_outer_returning(PlannerInfo *root, ReturningExpr *rexpr)
     369              : {
     370              :     Param      *retval;
     371              :     PlannerParamItem *pitem;
     372              :     Index       levelsup;
     373           15 :     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           35 :     for (levelsup = rexpr->retlevelsup; levelsup > 0; levelsup--)
     379           20 :         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           15 :     rexpr = copyObject(rexpr);
     386           15 :     IncrementVarSublevelsUp((Node *) rexpr, -((int) rexpr->retlevelsup), 0);
     387              :     Assert(rexpr->retlevelsup == 0);
     388              : 
     389           15 :     pitem = makeNode(PlannerParamItem);
     390           15 :     pitem->item = (Node *) rexpr;
     391           15 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     392           15 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     393              :                                              ptype);
     394              : 
     395           15 :     root->plan_params = lappend(root->plan_params, pitem);
     396              : 
     397           15 :     retval = makeNode(Param);
     398           15 :     retval->paramkind = PARAM_EXEC;
     399           15 :     retval->paramid = pitem->paramId;
     400           15 :     retval->paramtype = ptype;
     401           15 :     retval->paramtypmod = exprTypmod((Node *) rexpr->retexpr);
     402           15 :     retval->paramcollid = exprCollation((Node *) rexpr->retexpr);
     403           15 :     retval->location = exprLocation((Node *) rexpr->retexpr);
     404              : 
     405           15 :     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        77864 : 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        96887 :     foreach(lc, root->curOuterParams)
     422              :     {
     423        55094 :         nlp = (NestLoopParam *) lfirst(lc);
     424        55094 :         if (equal(var, nlp->paramval))
     425              :         {
     426              :             /* Yes, so just make a Param referencing this NLP's slot */
     427        36071 :             param = makeNode(Param);
     428        36071 :             param->paramkind = PARAM_EXEC;
     429        36071 :             param->paramid = nlp->paramno;
     430        36071 :             param->paramtype = var->vartype;
     431        36071 :             param->paramtypmod = var->vartypmod;
     432        36071 :             param->paramcollid = var->varcollid;
     433        36071 :             param->location = var->location;
     434        36071 :             return param;
     435              :         }
     436              :     }
     437              : 
     438              :     /* No, so assign a PARAM_EXEC slot for a new NLP */
     439        41793 :     param = generate_new_exec_param(root,
     440              :                                     var->vartype,
     441              :                                     var->vartypmod,
     442              :                                     var->varcollid);
     443        41793 :     param->location = var->location;
     444              : 
     445              :     /* Add it to the list of required NLPs */
     446        41793 :     nlp = makeNode(NestLoopParam);
     447        41793 :     nlp->paramno = param->paramid;
     448        41793 :     nlp->paramval = copyObject(var);
     449        41793 :     root->curOuterParams = lappend(root->curOuterParams, nlp);
     450              : 
     451              :     /* And return the replacement Param */
     452        41793 :     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          270 : 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          355 :     foreach(lc, root->curOuterParams)
     471              :     {
     472          185 :         nlp = (NestLoopParam *) lfirst(lc);
     473          185 :         if (equal(phv, nlp->paramval))
     474              :         {
     475              :             /* Yes, so just make a Param referencing this NLP's slot */
     476          100 :             param = makeNode(Param);
     477          100 :             param->paramkind = PARAM_EXEC;
     478          100 :             param->paramid = nlp->paramno;
     479          100 :             param->paramtype = exprType((Node *) phv->phexpr);
     480          100 :             param->paramtypmod = exprTypmod((Node *) phv->phexpr);
     481          100 :             param->paramcollid = exprCollation((Node *) phv->phexpr);
     482          100 :             param->location = -1;
     483          100 :             return param;
     484              :         }
     485              :     }
     486              : 
     487              :     /* No, so assign a PARAM_EXEC slot for a new NLP */
     488          170 :     param = generate_new_exec_param(root,
     489          170 :                                     exprType((Node *) phv->phexpr),
     490          170 :                                     exprTypmod((Node *) phv->phexpr),
     491          170 :                                     exprCollation((Node *) phv->phexpr));
     492              : 
     493              :     /* Add it to the list of required NLPs */
     494          170 :     nlp = makeNode(NestLoopParam);
     495          170 :     nlp->paramno = param->paramid;
     496          170 :     nlp->paramval = (Var *) copyObject(phv);
     497          170 :     root->curOuterParams = lappend(root->curOuterParams, nlp);
     498              : 
     499              :     /* And return the replacement Param */
     500          170 :     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          877 : process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
     528              : {
     529              :     ListCell   *lc;
     530              : 
     531         2087 :     foreach(lc, subplan_params)
     532              :     {
     533         1210 :         PlannerParamItem *pitem = lfirst_node(PlannerParamItem, lc);
     534              : 
     535         1210 :         if (IsA(pitem->item, Var))
     536              :         {
     537         1170 :             Var        *var = (Var *) pitem->item;
     538              :             NestLoopParam *nlp;
     539              :             ListCell   *lc2;
     540              : 
     541              :             /* If not from a nestloop outer rel, complain */
     542         1170 :             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         1598 :             foreach(lc2, root->curOuterParams)
     547              :             {
     548          428 :                 nlp = (NestLoopParam *) lfirst(lc2);
     549          428 :                 if (nlp->paramno == pitem->paramId)
     550              :                 {
     551              :                     Assert(equal(var, nlp->paramval));
     552              :                     /* Present, so nothing to do */
     553            0 :                     break;
     554              :                 }
     555              :             }
     556         1170 :             if (lc2 == NULL)
     557              :             {
     558              :                 /* No, so add it */
     559         1170 :                 nlp = makeNode(NestLoopParam);
     560         1170 :                 nlp->paramno = pitem->paramId;
     561         1170 :                 nlp->paramval = copyObject(var);
     562         1170 :                 root->curOuterParams = lappend(root->curOuterParams, nlp);
     563              :             }
     564              :         }
     565           40 :         else if (IsA(pitem->item, PlaceHolderVar))
     566              :         {
     567           40 :             PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
     568              :             NestLoopParam *nlp;
     569              :             ListCell   *lc2;
     570              : 
     571              :             /* If not from a nestloop outer rel, complain */
     572           40 :             if (!bms_is_subset(find_placeholder_info(root, phv)->ph_eval_at,
     573           40 :                                root->curOuterRels))
     574            0 :                 elog(ERROR, "non-LATERAL parameter required by subquery");
     575              : 
     576              :             /* Is this param already listed in root->curOuterParams? */
     577          105 :             foreach(lc2, root->curOuterParams)
     578              :             {
     579           65 :                 nlp = (NestLoopParam *) lfirst(lc2);
     580           65 :                 if (nlp->paramno == pitem->paramId)
     581              :                 {
     582              :                     Assert(equal(phv, nlp->paramval));
     583              :                     /* Present, so nothing to do */
     584            0 :                     break;
     585              :                 }
     586              :             }
     587           40 :             if (lc2 == NULL)
     588              :             {
     589              :                 /* No, so add it */
     590           40 :                 nlp = makeNode(NestLoopParam);
     591           40 :                 nlp->paramno = pitem->paramId;
     592           40 :                 nlp->paramval = (Var *) copyObject(phv);
     593           40 :                 root->curOuterParams = lappend(root->curOuterParams, nlp);
     594              :             }
     595              :         }
     596              :         else
     597            0 :             elog(ERROR, "unexpected type of subquery parameter");
     598              :     }
     599          877 : }
     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        76336 : 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        76336 :     if (outerrelids)
     638          797 :         allleftrelids = bms_union(leftrelids, outerrelids);
     639              :     else
     640        75539 :         allleftrelids = leftrelids;
     641              : 
     642        76336 :     result = NIL;
     643       120424 :     foreach(cell, root->curOuterParams)
     644              :     {
     645        44088 :         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        87956 :         if (IsA(nlp->paramval, Var) &&
     654        43868 :             bms_is_member(nlp->paramval->varno, leftrelids))
     655        42963 :         {
     656        42963 :             Var        *var = (Var *) nlp->paramval;
     657        42963 :             RelOptInfo *rel = root->simple_rel_array[var->varno];
     658              : 
     659        42963 :             root->curOuterParams = foreach_delete_current(root->curOuterParams,
     660              :                                                           cell);
     661        42963 :             var->varnullingrels = bms_intersect(rel->nulling_relids,
     662              :                                                 leftrelids);
     663        42963 :             result = lappend(result, nlp);
     664              :         }
     665         1125 :         else if (IsA(nlp->paramval, PlaceHolderVar))
     666              :         {
     667          220 :             PlaceHolderVar *phv = (PlaceHolderVar *) nlp->paramval;
     668          220 :             PlaceHolderInfo *phinfo = find_placeholder_info(root, phv);
     669          220 :             Relids      eval_at = phinfo->ph_eval_at;
     670              : 
     671          440 :             if (bms_is_subset(eval_at, allleftrelids) &&
     672          220 :                 bms_overlap(eval_at, leftrelids))
     673              :             {
     674          210 :                 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          210 :                 if (root->parse->hasSubLinks)
     694              :                 {
     695           20 :                     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           20 :                     nlp->paramval = (Var *) phv;
     703              :                 }
     704              : 
     705          210 :                 phv->phnullingrels =
     706          210 :                     bms_intersect(get_placeholder_nulling_relids(root, phinfo),
     707              :                                   leftrelids);
     708              : 
     709          210 :                 result = lappend(result, nlp);
     710              :             }
     711              :         }
     712              :     }
     713        76336 :     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        52748 : generate_new_exec_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
     728              :                         Oid paramcollation)
     729              : {
     730              :     Param      *retval;
     731              : 
     732        52748 :     retval = makeNode(Param);
     733        52748 :     retval->paramkind = PARAM_EXEC;
     734        52748 :     retval->paramid = list_length(root->glob->paramExecTypes);
     735        52748 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     736              :                                              paramtype);
     737        52748 :     retval->paramtype = paramtype;
     738        52748 :     retval->paramtypmod = paramtypmod;
     739        52748 :     retval->paramcollid = paramcollation;
     740        52748 :     retval->location = -1;
     741              : 
     742        52748 :     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        74602 : assign_special_exec_param(PlannerInfo *root)
     755              : {
     756        74602 :     int         paramId = list_length(root->glob->paramExecTypes);
     757              : 
     758        74602 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     759              :                                              InvalidOid);
     760        74602 :     return paramId;
     761              : }
        

Generated by: LCOV version 2.0-1