LCOV - code coverage report
Current view: top level - src/backend/optimizer/util - paramassign.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 233 242 96.3 %
Date: 2025-01-18 04:15:08 Functions: 14 14 100.0 %
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-2025, PostgreSQL Global Development Group
      44             :  * Portions Copyright (c) 1994, Regents of the University of California
      45             :  *
      46             :  * IDENTIFICATION
      47             :  *    src/backend/optimizer/util/paramassign.c
      48             :  *
      49             :  *-------------------------------------------------------------------------
      50             :  */
      51             : #include "postgres.h"
      52             : 
      53             : #include "nodes/nodeFuncs.h"
      54             : #include "nodes/plannodes.h"
      55             : #include "optimizer/paramassign.h"
      56             : #include "optimizer/placeholder.h"
      57             : #include "rewrite/rewriteManip.h"
      58             : 
      59             : 
      60             : /*
      61             :  * Select a PARAM_EXEC number to identify the given Var as a parameter for
      62             :  * the current subquery.  (It might already have one.)
      63             :  * Record the need for the Var in the proper upper-level root->plan_params.
      64             :  */
      65             : static int
      66       48430 : 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       97314 :     for (levelsup = var->varlevelsup; levelsup > 0; levelsup--)
      74       48884 :         root = root->parent_root;
      75             : 
      76             :     /* If there's already a matching PlannerParamItem there, just use it */
      77       71500 :     foreach(ppl, root->plan_params)
      78             :     {
      79       30468 :         pitem = (PlannerParamItem *) lfirst(ppl);
      80       30468 :         if (IsA(pitem->item, Var))
      81             :         {
      82       30468 :             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       30468 :             if (pvar->varno == var->varno &&
      90       28392 :                 pvar->varattno == var->varattno &&
      91        7416 :                 pvar->vartype == var->vartype &&
      92        7416 :                 pvar->vartypmod == var->vartypmod &&
      93        7416 :                 pvar->varcollid == var->varcollid &&
      94       14814 :                 pvar->varreturningtype == var->varreturningtype &&
      95        7398 :                 bms_equal(pvar->varnullingrels, var->varnullingrels))
      96        7398 :                 return pitem->paramId;
      97             :         }
      98             :     }
      99             : 
     100             :     /* Nope, so make a new one */
     101       41032 :     var = copyObject(var);
     102       41032 :     var->varlevelsup = 0;
     103             : 
     104       41032 :     pitem = makeNode(PlannerParamItem);
     105       41032 :     pitem->item = (Node *) var;
     106       41032 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     107       41032 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     108             :                                              var->vartype);
     109             : 
     110       41032 :     root->plan_params = lappend(root->plan_params, pitem);
     111             : 
     112       41032 :     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       48430 : 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       48430 :     i = assign_param_for_var(root, var);
     130             : 
     131       48430 :     retval = makeNode(Param);
     132       48430 :     retval->paramkind = PARAM_EXEC;
     133       48430 :     retval->paramid = i;
     134       48430 :     retval->paramtype = var->vartype;
     135       48430 :     retval->paramtypmod = var->vartypmod;
     136       48430 :     retval->paramcollid = var->varcollid;
     137       48430 :     retval->location = var->location;
     138             : 
     139       48430 :     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          42 : 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          84 :     for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--)
     158          42 :         root = root->parent_root;
     159             : 
     160             :     /* If there's already a matching PlannerParamItem there, just use it */
     161         102 :     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          42 :     phv = copyObject(phv);
     176          42 :     IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
     177             :     Assert(phv->phlevelsup == 0);
     178             : 
     179          42 :     pitem = makeNode(PlannerParamItem);
     180          42 :     pitem->item = (Node *) phv;
     181          42 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     182          42 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     183          42 :                                              exprType((Node *) phv->phexpr));
     184             : 
     185          42 :     root->plan_params = lappend(root->plan_params, pitem);
     186             : 
     187          42 :     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          42 : 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          42 :     i = assign_param_for_placeholdervar(root, phv);
     207             : 
     208          42 :     retval = makeNode(Param);
     209          42 :     retval->paramkind = PARAM_EXEC;
     210          42 :     retval->paramid = i;
     211          42 :     retval->paramtype = exprType((Node *) phv->phexpr);
     212          42 :     retval->paramtypmod = exprTypmod((Node *) phv->phexpr);
     213          42 :     retval->paramcollid = exprCollation((Node *) phv->phexpr);
     214          42 :     retval->location = -1;
     215             : 
     216          42 :     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          52 : 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         104 :     for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
     235          52 :         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          52 :     agg = copyObject(agg);
     242          52 :     IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
     243             :     Assert(agg->agglevelsup == 0);
     244             : 
     245          52 :     pitem = makeNode(PlannerParamItem);
     246          52 :     pitem->item = (Node *) agg;
     247          52 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     248          52 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     249             :                                              agg->aggtype);
     250             : 
     251          52 :     root->plan_params = lappend(root->plan_params, pitem);
     252             : 
     253          52 :     retval = makeNode(Param);
     254          52 :     retval->paramkind = PARAM_EXEC;
     255          52 :     retval->paramid = pitem->paramId;
     256          52 :     retval->paramtype = agg->aggtype;
     257          52 :     retval->paramtypmod = -1;
     258          52 :     retval->paramcollid = agg->aggcollid;
     259          52 :     retval->location = agg->location;
     260             : 
     261          52 :     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          64 : replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp)
     272             : {
     273             :     Param      *retval;
     274             :     PlannerParamItem *pitem;
     275             :     Index       levelsup;
     276          64 :     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         136 :     for (levelsup = grp->agglevelsup; levelsup > 0; levelsup--)
     282          72 :         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          64 :     grp = copyObject(grp);
     289          64 :     IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0);
     290             :     Assert(grp->agglevelsup == 0);
     291             : 
     292          64 :     pitem = makeNode(PlannerParamItem);
     293          64 :     pitem->item = (Node *) grp;
     294          64 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     295          64 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     296             :                                              ptype);
     297             : 
     298          64 :     root->plan_params = lappend(root->plan_params, pitem);
     299             : 
     300          64 :     retval = makeNode(Param);
     301          64 :     retval->paramkind = PARAM_EXEC;
     302          64 :     retval->paramid = pitem->paramId;
     303          64 :     retval->paramtype = ptype;
     304          64 :     retval->paramtypmod = -1;
     305          64 :     retval->paramcollid = InvalidOid;
     306          64 :     retval->location = grp->location;
     307             : 
     308          64 :     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           6 : replace_outer_merge_support(PlannerInfo *root, MergeSupportFunc *msf)
     319             : {
     320             :     Param      *retval;
     321             :     PlannerParamItem *pitem;
     322           6 :     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           6 :         root = root->parent_root;
     333           6 :         if (root == NULL)
     334           0 :             elog(ERROR, "MergeSupportFunc found outside MERGE");
     335           6 :     } 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           6 :     msf = copyObject(msf);
     342             : 
     343           6 :     pitem = makeNode(PlannerParamItem);
     344           6 :     pitem->item = (Node *) msf;
     345           6 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     346           6 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     347             :                                              ptype);
     348             : 
     349           6 :     root->plan_params = lappend(root->plan_params, pitem);
     350             : 
     351           6 :     retval = makeNode(Param);
     352           6 :     retval->paramkind = PARAM_EXEC;
     353           6 :     retval->paramid = pitem->paramId;
     354           6 :     retval->paramtype = ptype;
     355           6 :     retval->paramtypmod = -1;
     356           6 :     retval->paramcollid = InvalidOid;
     357           6 :     retval->location = msf->location;
     358             : 
     359           6 :     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          18 : replace_outer_returning(PlannerInfo *root, ReturningExpr *rexpr)
     369             : {
     370             :     Param      *retval;
     371             :     PlannerParamItem *pitem;
     372             :     Index       levelsup;
     373          18 :     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          42 :     for (levelsup = rexpr->retlevelsup; levelsup > 0; levelsup--)
     379          24 :         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          18 :     rexpr = copyObject(rexpr);
     386          18 :     IncrementVarSublevelsUp((Node *) rexpr, -((int) rexpr->retlevelsup), 0);
     387             :     Assert(rexpr->retlevelsup == 0);
     388             : 
     389          18 :     pitem = makeNode(PlannerParamItem);
     390          18 :     pitem->item = (Node *) rexpr;
     391          18 :     pitem->paramId = list_length(root->glob->paramExecTypes);
     392          18 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     393             :                                              ptype);
     394             : 
     395          18 :     root->plan_params = lappend(root->plan_params, pitem);
     396             : 
     397          18 :     retval = makeNode(Param);
     398          18 :     retval->paramkind = PARAM_EXEC;
     399          18 :     retval->paramid = pitem->paramId;
     400          18 :     retval->paramtype = ptype;
     401          18 :     retval->paramtypmod = exprTypmod((Node *) rexpr->retexpr);
     402          18 :     retval->paramcollid = exprCollation((Node *) rexpr->retexpr);
     403          18 :     retval->location = exprLocation((Node *) rexpr->retexpr);
     404             : 
     405          18 :     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       83440 : 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       92154 :     foreach(lc, root->curOuterParams)
     422             :     {
     423       46684 :         nlp = (NestLoopParam *) lfirst(lc);
     424       46684 :         if (equal(var, nlp->paramval))
     425             :         {
     426             :             /* Yes, so just make a Param referencing this NLP's slot */
     427       37970 :             param = makeNode(Param);
     428       37970 :             param->paramkind = PARAM_EXEC;
     429       37970 :             param->paramid = nlp->paramno;
     430       37970 :             param->paramtype = var->vartype;
     431       37970 :             param->paramtypmod = var->vartypmod;
     432       37970 :             param->paramcollid = var->varcollid;
     433       37970 :             param->location = var->location;
     434       37970 :             return param;
     435             :         }
     436             :     }
     437             : 
     438             :     /* No, so assign a PARAM_EXEC slot for a new NLP */
     439       45470 :     param = generate_new_exec_param(root,
     440             :                                     var->vartype,
     441             :                                     var->vartypmod,
     442             :                                     var->varcollid);
     443       45470 :     param->location = var->location;
     444             : 
     445             :     /* Add it to the list of required NLPs */
     446       45470 :     nlp = makeNode(NestLoopParam);
     447       45470 :     nlp->paramno = param->paramid;
     448       45470 :     nlp->paramval = copyObject(var);
     449       45470 :     root->curOuterParams = lappend(root->curOuterParams, nlp);
     450             : 
     451             :     /* And return the replacement Param */
     452       45470 :     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         258 : 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         336 :     foreach(lc, root->curOuterParams)
     471             :     {
     472         180 :         nlp = (NestLoopParam *) lfirst(lc);
     473         180 :         if (equal(phv, nlp->paramval))
     474             :         {
     475             :             /* Yes, so just make a Param referencing this NLP's slot */
     476         102 :             param = makeNode(Param);
     477         102 :             param->paramkind = PARAM_EXEC;
     478         102 :             param->paramid = nlp->paramno;
     479         102 :             param->paramtype = exprType((Node *) phv->phexpr);
     480         102 :             param->paramtypmod = exprTypmod((Node *) phv->phexpr);
     481         102 :             param->paramcollid = exprCollation((Node *) phv->phexpr);
     482         102 :             param->location = -1;
     483         102 :             return param;
     484             :         }
     485             :     }
     486             : 
     487             :     /* No, so assign a PARAM_EXEC slot for a new NLP */
     488         156 :     param = generate_new_exec_param(root,
     489         156 :                                     exprType((Node *) phv->phexpr),
     490         156 :                                     exprTypmod((Node *) phv->phexpr),
     491         156 :                                     exprCollation((Node *) phv->phexpr));
     492             : 
     493             :     /* Add it to the list of required NLPs */
     494         156 :     nlp = makeNode(NestLoopParam);
     495         156 :     nlp->paramno = param->paramid;
     496         156 :     nlp->paramval = (Var *) copyObject(phv);
     497         156 :     root->curOuterParams = lappend(root->curOuterParams, nlp);
     498             : 
     499             :     /* And return the replacement Param */
     500         156 :     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         478 : process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
     528             : {
     529             :     ListCell   *lc;
     530             : 
     531        1076 :     foreach(lc, subplan_params)
     532             :     {
     533         598 :         PlannerParamItem *pitem = lfirst_node(PlannerParamItem, lc);
     534             : 
     535         598 :         if (IsA(pitem->item, Var))
     536             :         {
     537         568 :             Var        *var = (Var *) pitem->item;
     538             :             NestLoopParam *nlp;
     539             :             ListCell   *lc2;
     540             : 
     541             :             /* If not from a nestloop outer rel, complain */
     542         568 :             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         772 :             foreach(lc2, root->curOuterParams)
     547             :             {
     548         204 :                 nlp = (NestLoopParam *) lfirst(lc2);
     549         204 :                 if (nlp->paramno == pitem->paramId)
     550             :                 {
     551             :                     Assert(equal(var, nlp->paramval));
     552             :                     /* Present, so nothing to do */
     553           0 :                     break;
     554             :                 }
     555             :             }
     556         568 :             if (lc2 == NULL)
     557             :             {
     558             :                 /* No, so add it */
     559         568 :                 nlp = makeNode(NestLoopParam);
     560         568 :                 nlp->paramno = pitem->paramId;
     561         568 :                 nlp->paramval = copyObject(var);
     562         568 :                 root->curOuterParams = lappend(root->curOuterParams, nlp);
     563             :             }
     564             :         }
     565          30 :         else if (IsA(pitem->item, PlaceHolderVar))
     566             :         {
     567          30 :             PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
     568             :             NestLoopParam *nlp;
     569             :             ListCell   *lc2;
     570             : 
     571             :             /* If not from a nestloop outer rel, complain */
     572          30 :             if (!bms_is_subset(find_placeholder_info(root, phv)->ph_eval_at,
     573          30 :                                root->curOuterRels))
     574           0 :                 elog(ERROR, "non-LATERAL parameter required by subquery");
     575             : 
     576             :             /* Is this param already listed in root->curOuterParams? */
     577          90 :             foreach(lc2, root->curOuterParams)
     578             :             {
     579          60 :                 nlp = (NestLoopParam *) lfirst(lc2);
     580          60 :                 if (nlp->paramno == pitem->paramId)
     581             :                 {
     582             :                     Assert(equal(phv, nlp->paramval));
     583             :                     /* Present, so nothing to do */
     584           0 :                     break;
     585             :                 }
     586             :             }
     587          30 :             if (lc2 == NULL)
     588             :             {
     589             :                 /* No, so add it */
     590          30 :                 nlp = makeNode(NestLoopParam);
     591          30 :                 nlp->paramno = pitem->paramId;
     592          30 :                 nlp->paramval = (Var *) copyObject(phv);
     593          30 :                 root->curOuterParams = lappend(root->curOuterParams, nlp);
     594             :             }
     595             :         }
     596             :         else
     597           0 :             elog(ERROR, "unexpected type of subquery parameter");
     598             :     }
     599         478 : }
     600             : 
     601             : /*
     602             :  * Identify any NestLoopParams that should be supplied by a NestLoop plan
     603             :  * node with the specified lefthand rels.  Remove them from the active
     604             :  * root->curOuterParams list and return them as the result list.
     605             :  *
     606             :  * XXX Here we also hack up the returned Vars and PHVs so that they do not
     607             :  * contain nullingrel sets exceeding what is available from the outer side.
     608             :  * This is needed if we have applied outer join identity 3,
     609             :  *      (A leftjoin B on (Pab)) leftjoin C on (Pb*c)
     610             :  *      = A leftjoin (B leftjoin C on (Pbc)) on (Pab)
     611             :  * and C contains lateral references to B.  It's still safe to apply the
     612             :  * identity, but the parser will have created those references in the form
     613             :  * "b*" (i.e., with varnullingrels listing the A/B join), while what we will
     614             :  * have available from the nestloop's outer side is just "b".  We deal with
     615             :  * that here by stripping the nullingrels down to what is available from the
     616             :  * outer side according to leftrelids.
     617             :  *
     618             :  * That fixes matters for the case of forward application of identity 3.
     619             :  * If the identity was applied in the reverse direction, we will have
     620             :  * parameter Vars containing too few nullingrel bits rather than too many.
     621             :  * Currently, that causes no problems because setrefs.c applies only a
     622             :  * subset check to nullingrels in NestLoopParams, but we'd have to work
     623             :  * harder if we ever want to tighten that check.  This is all pretty annoying
     624             :  * because it greatly weakens setrefs.c's cross-check, but the alternative
     625             :  * seems to be to generate multiple versions of each laterally-parameterized
     626             :  * subquery, which'd be unduly expensive.
     627             :  */
     628             : List *
     629       83802 : identify_current_nestloop_params(PlannerInfo *root, Relids leftrelids)
     630             : {
     631             :     List       *result;
     632             :     ListCell   *cell;
     633             : 
     634       83802 :     result = NIL;
     635      130982 :     foreach(cell, root->curOuterParams)
     636             :     {
     637       47180 :         NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
     638             : 
     639             :         /*
     640             :          * We are looking for Vars and PHVs that can be supplied by the
     641             :          * lefthand rels.  When we find one, it's okay to modify it in-place
     642             :          * because all the routines above make a fresh copy to put into
     643             :          * curOuterParams.
     644             :          */
     645       94174 :         if (IsA(nlp->paramval, Var) &&
     646       46994 :             bms_is_member(nlp->paramval->varno, leftrelids))
     647       46038 :         {
     648       46038 :             Var        *var = (Var *) nlp->paramval;
     649             : 
     650       46038 :             root->curOuterParams = foreach_delete_current(root->curOuterParams,
     651             :                                                           cell);
     652       46038 :             var->varnullingrels = bms_intersect(var->varnullingrels,
     653             :                                                 leftrelids);
     654       46038 :             result = lappend(result, nlp);
     655             :         }
     656        1328 :         else if (IsA(nlp->paramval, PlaceHolderVar) &&
     657         372 :                  bms_is_subset(find_placeholder_info(root,
     658         186 :                                                      (PlaceHolderVar *) nlp->paramval)->ph_eval_at,
     659             :                                leftrelids))
     660             :         {
     661         186 :             PlaceHolderVar *phv = (PlaceHolderVar *) nlp->paramval;
     662             : 
     663         186 :             root->curOuterParams = foreach_delete_current(root->curOuterParams,
     664             :                                                           cell);
     665         186 :             phv->phnullingrels = bms_intersect(phv->phnullingrels,
     666             :                                                leftrelids);
     667         186 :             result = lappend(result, nlp);
     668             :         }
     669             :     }
     670       83802 :     return result;
     671             : }
     672             : 
     673             : /*
     674             :  * Generate a new Param node that will not conflict with any other.
     675             :  *
     676             :  * This is used to create Params representing subplan outputs or
     677             :  * NestLoop parameters.
     678             :  *
     679             :  * We don't need to build a PlannerParamItem for such a Param, but we do
     680             :  * need to make sure we record the type in paramExecTypes (otherwise,
     681             :  * there won't be a slot allocated for it).
     682             :  */
     683             : Param *
     684       58204 : generate_new_exec_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
     685             :                         Oid paramcollation)
     686             : {
     687             :     Param      *retval;
     688             : 
     689       58204 :     retval = makeNode(Param);
     690       58204 :     retval->paramkind = PARAM_EXEC;
     691       58204 :     retval->paramid = list_length(root->glob->paramExecTypes);
     692       58204 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     693             :                                              paramtype);
     694       58204 :     retval->paramtype = paramtype;
     695       58204 :     retval->paramtypmod = paramtypmod;
     696       58204 :     retval->paramcollid = paramcollation;
     697       58204 :     retval->location = -1;
     698             : 
     699       58204 :     return retval;
     700             : }
     701             : 
     702             : /*
     703             :  * Assign a (nonnegative) PARAM_EXEC ID for a special parameter (one that
     704             :  * is not actually used to carry a value at runtime).  Such parameters are
     705             :  * used for special runtime signaling purposes, such as connecting a
     706             :  * recursive union node to its worktable scan node or forcing plan
     707             :  * re-evaluation within the EvalPlanQual mechanism.  No actual Param node
     708             :  * exists with this ID, however.
     709             :  */
     710             : int
     711      103398 : assign_special_exec_param(PlannerInfo *root)
     712             : {
     713      103398 :     int         paramId = list_length(root->glob->paramExecTypes);
     714             : 
     715      103398 :     root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes,
     716             :                                              InvalidOid);
     717      103398 :     return paramId;
     718             : }

Generated by: LCOV version 1.14