LCOV - code coverage report
Current view: top level - src/backend/optimizer/util - placeholder.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 92.8 % 167 155
Test Date: 2026-04-30 20:16:35 Functions: 100.0 % 15 15
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * placeholder.c
       4              :  *    PlaceHolderVar and PlaceHolderInfo manipulation routines
       5              :  *
       6              :  *
       7              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8              :  * Portions Copyright (c) 1994, Regents of the University of California
       9              :  *
      10              :  *
      11              :  * IDENTIFICATION
      12              :  *    src/backend/optimizer/util/placeholder.c
      13              :  *
      14              :  *-------------------------------------------------------------------------
      15              :  */
      16              : #include "postgres.h"
      17              : 
      18              : #include "nodes/nodeFuncs.h"
      19              : #include "optimizer/cost.h"
      20              : #include "optimizer/optimizer.h"
      21              : #include "optimizer/pathnode.h"
      22              : #include "optimizer/placeholder.h"
      23              : #include "optimizer/planmain.h"
      24              : #include "utils/lsyscache.h"
      25              : 
      26              : 
      27              : typedef struct contain_placeholder_references_context
      28              : {
      29              :     int         relid;
      30              :     int         sublevels_up;
      31              : } contain_placeholder_references_context;
      32              : 
      33              : /* Local functions */
      34              : static void find_placeholders_recurse(PlannerInfo *root, Node *jtnode);
      35              : static void find_placeholders_in_expr(PlannerInfo *root, Node *expr);
      36              : static bool contain_placeholder_references_walker(Node *node,
      37              :                                                   contain_placeholder_references_context *context);
      38              : static bool contain_noop_phv_walker(Node *node, void *context);
      39              : static Node *strip_noop_phvs_mutator(Node *node, void *context);
      40              : 
      41              : 
      42              : /*
      43              :  * make_placeholder_expr
      44              :  *      Make a PlaceHolderVar for the given expression.
      45              :  *
      46              :  * phrels is the syntactic location (as a set of relids) to attribute
      47              :  * to the expression.
      48              :  *
      49              :  * The caller is responsible for adjusting phlevelsup and phnullingrels
      50              :  * as needed.  Because we do not know here which query level the PHV
      51              :  * will be associated with, it's important that this function touches
      52              :  * only root->glob; messing with other parts of PlannerInfo would be
      53              :  * likely to do the wrong thing.
      54              :  */
      55              : PlaceHolderVar *
      56         2337 : make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels)
      57              : {
      58         2337 :     PlaceHolderVar *phv = makeNode(PlaceHolderVar);
      59              : 
      60         2337 :     phv->phexpr = expr;
      61         2337 :     phv->phrels = phrels;
      62         2337 :     phv->phnullingrels = NULL;   /* caller may change this later */
      63         2337 :     phv->phid = ++(root->glob->lastPHId);
      64         2337 :     phv->phlevelsup = 0;     /* caller may change this later */
      65              : 
      66         2337 :     return phv;
      67              : }
      68              : 
      69              : /*
      70              :  * find_placeholder_info
      71              :  *      Fetch the PlaceHolderInfo for the given PHV
      72              :  *
      73              :  * If the PlaceHolderInfo doesn't exist yet, create it if we haven't yet
      74              :  * frozen the set of PlaceHolderInfos for the query; else throw an error.
      75              :  *
      76              :  * This is separate from make_placeholder_expr because subquery pullup has
      77              :  * to make PlaceHolderVars for expressions that might not be used at all in
      78              :  * the upper query, or might not remain after const-expression simplification.
      79              :  * We build PlaceHolderInfos only for PHVs that are still present in the
      80              :  * simplified query passed to query_planner().
      81              :  *
      82              :  * Note: this should only be called after query_planner() has started.
      83              :  */
      84              : PlaceHolderInfo *
      85         8593 : find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv)
      86              : {
      87              :     PlaceHolderInfo *phinfo;
      88              :     Relids      rels_used;
      89              : 
      90              :     /* if this ever isn't true, we'd need to be able to look in parent lists */
      91              :     Assert(phv->phlevelsup == 0);
      92              : 
      93              :     /* Use placeholder_array to look up existing PlaceHolderInfo quickly */
      94         8593 :     if (phv->phid < root->placeholder_array_size)
      95         7369 :         phinfo = root->placeholder_array[phv->phid];
      96              :     else
      97         1224 :         phinfo = NULL;
      98         8593 :     if (phinfo != NULL)
      99              :     {
     100              :         Assert(phinfo->phid == phv->phid);
     101         6629 :         return phinfo;
     102              :     }
     103              : 
     104              :     /* Not found, so create it */
     105         1964 :     if (root->placeholdersFrozen)
     106            0 :         elog(ERROR, "too late to create a new PlaceHolderInfo");
     107              : 
     108         1964 :     phinfo = makeNode(PlaceHolderInfo);
     109              : 
     110         1964 :     phinfo->phid = phv->phid;
     111         1964 :     phinfo->ph_var = copyObject(phv);
     112              : 
     113              :     /*
     114              :      * By convention, phinfo->ph_var->phnullingrels is always empty, since the
     115              :      * PlaceHolderInfo represents the initially-calculated state of the
     116              :      * PlaceHolderVar.  PlaceHolderVars appearing in the query tree might have
     117              :      * varying values of phnullingrels, reflecting outer joins applied above
     118              :      * the calculation level.
     119              :      */
     120         1964 :     phinfo->ph_var->phnullingrels = NULL;
     121              : 
     122              :     /*
     123              :      * Any referenced rels that are outside the PHV's syntactic scope are
     124              :      * LATERAL references, which should be included in ph_lateral but not in
     125              :      * ph_eval_at.  If no referenced rels are within the syntactic scope,
     126              :      * force evaluation at the syntactic location.
     127              :      */
     128         1964 :     rels_used = pull_varnos(root, (Node *) phv->phexpr);
     129         1964 :     phinfo->ph_lateral = bms_difference(rels_used, phv->phrels);
     130         1964 :     phinfo->ph_eval_at = bms_int_members(rels_used, phv->phrels);
     131              :     /* If no contained vars, force evaluation at syntactic location */
     132         1964 :     if (bms_is_empty(phinfo->ph_eval_at))
     133              :     {
     134          880 :         phinfo->ph_eval_at = bms_copy(phv->phrels);
     135              :         Assert(!bms_is_empty(phinfo->ph_eval_at));
     136              :     }
     137         1964 :     phinfo->ph_needed = NULL;    /* initially it's unused */
     138              :     /* for the moment, estimate width using just the datatype info */
     139         1964 :     phinfo->ph_width = get_typavgwidth(exprType((Node *) phv->phexpr),
     140         1964 :                                        exprTypmod((Node *) phv->phexpr));
     141              : 
     142              :     /*
     143              :      * Add to both placeholder_list and placeholder_array.  Note: because we
     144              :      * store pointers to the PlaceHolderInfos in two data structures, it'd be
     145              :      * unsafe to pass the whole placeholder_list structure through
     146              :      * expression_tree_mutator or the like --- or at least, you'd have to
     147              :      * rebuild the placeholder_array afterwards.
     148              :      */
     149         1964 :     root->placeholder_list = lappend(root->placeholder_list, phinfo);
     150              : 
     151         1964 :     if (phinfo->phid >= root->placeholder_array_size)
     152              :     {
     153              :         /* Must allocate or enlarge placeholder_array */
     154              :         int         new_size;
     155              : 
     156         1224 :         new_size = root->placeholder_array_size ? root->placeholder_array_size * 2 : 8;
     157         1224 :         while (phinfo->phid >= new_size)
     158            0 :             new_size *= 2;
     159         1224 :         if (root->placeholder_array)
     160            0 :             root->placeholder_array =
     161            0 :                 repalloc0_array(root->placeholder_array, PlaceHolderInfo *, root->placeholder_array_size, new_size);
     162              :         else
     163         1224 :             root->placeholder_array =
     164         1224 :                 palloc0_array(PlaceHolderInfo *, new_size);
     165         1224 :         root->placeholder_array_size = new_size;
     166              :     }
     167         1964 :     root->placeholder_array[phinfo->phid] = phinfo;
     168              : 
     169              :     /*
     170              :      * The PHV's contained expression may contain other, lower-level PHVs.  We
     171              :      * now know we need to get those into the PlaceHolderInfo list, too, so we
     172              :      * may as well do that immediately.
     173              :      */
     174         1964 :     find_placeholders_in_expr(root, (Node *) phinfo->ph_var->phexpr);
     175              : 
     176         1964 :     return phinfo;
     177              : }
     178              : 
     179              : /*
     180              :  * find_placeholders_in_jointree
     181              :  *      Search the jointree for PlaceHolderVars, and build PlaceHolderInfos
     182              :  *
     183              :  * We don't need to look at the targetlist because build_base_rel_tlists()
     184              :  * will already have made entries for any PHVs in the tlist.
     185              :  */
     186              : void
     187       253490 : find_placeholders_in_jointree(PlannerInfo *root)
     188              : {
     189              :     /* This must be done before freezing the set of PHIs */
     190              :     Assert(!root->placeholdersFrozen);
     191              : 
     192              :     /* We need do nothing if the query contains no PlaceHolderVars */
     193       253490 :     if (root->glob->lastPHId != 0)
     194              :     {
     195              :         /* Start recursion at top of jointree */
     196              :         Assert(root->parse->jointree != NULL &&
     197              :                IsA(root->parse->jointree, FromExpr));
     198         1498 :         find_placeholders_recurse(root, (Node *) root->parse->jointree);
     199              :     }
     200       253490 : }
     201              : 
     202              : /*
     203              :  * find_placeholders_recurse
     204              :  *    One recursion level of find_placeholders_in_jointree.
     205              :  *
     206              :  * jtnode is the current jointree node to examine.
     207              :  */
     208              : static void
     209         7113 : find_placeholders_recurse(PlannerInfo *root, Node *jtnode)
     210              : {
     211         7113 :     if (jtnode == NULL)
     212            0 :         return;
     213         7113 :     if (IsA(jtnode, RangeTblRef))
     214              :     {
     215              :         /* No quals to deal with here */
     216              :     }
     217         3582 :     else if (IsA(jtnode, FromExpr))
     218              :     {
     219         1781 :         FromExpr   *f = (FromExpr *) jtnode;
     220              :         ListCell   *l;
     221              : 
     222              :         /*
     223              :          * First, recurse to handle child joins.
     224              :          */
     225         3794 :         foreach(l, f->fromlist)
     226              :         {
     227         2013 :             find_placeholders_recurse(root, lfirst(l));
     228              :         }
     229              : 
     230              :         /*
     231              :          * Now process the top-level quals.
     232              :          */
     233         1781 :         find_placeholders_in_expr(root, f->quals);
     234              :     }
     235         1801 :     else if (IsA(jtnode, JoinExpr))
     236              :     {
     237         1801 :         JoinExpr   *j = (JoinExpr *) jtnode;
     238              : 
     239              :         /*
     240              :          * First, recurse to handle child joins.
     241              :          */
     242         1801 :         find_placeholders_recurse(root, j->larg);
     243         1801 :         find_placeholders_recurse(root, j->rarg);
     244              : 
     245              :         /* Process the qual clauses */
     246         1801 :         find_placeholders_in_expr(root, j->quals);
     247              :     }
     248              :     else
     249            0 :         elog(ERROR, "unrecognized node type: %d",
     250              :              (int) nodeTag(jtnode));
     251              : }
     252              : 
     253              : /*
     254              :  * find_placeholders_in_expr
     255              :  *      Find all PlaceHolderVars in the given expression, and create
     256              :  *      PlaceHolderInfo entries for them.
     257              :  */
     258              : static void
     259         5546 : find_placeholders_in_expr(PlannerInfo *root, Node *expr)
     260              : {
     261              :     List       *vars;
     262              :     ListCell   *vl;
     263              : 
     264              :     /*
     265              :      * pull_var_clause does more than we need here, but it'll do and it's
     266              :      * convenient to use.
     267              :      */
     268         5546 :     vars = pull_var_clause(expr,
     269              :                            PVC_RECURSE_AGGREGATES |
     270              :                            PVC_RECURSE_WINDOWFUNCS |
     271              :                            PVC_INCLUDE_PLACEHOLDERS);
     272        11883 :     foreach(vl, vars)
     273              :     {
     274         6337 :         PlaceHolderVar *phv = (PlaceHolderVar *) lfirst(vl);
     275              : 
     276              :         /* Ignore any plain Vars */
     277         6337 :         if (!IsA(phv, PlaceHolderVar))
     278         5533 :             continue;
     279              : 
     280              :         /* Create a PlaceHolderInfo entry if there's not one already */
     281          804 :         (void) find_placeholder_info(root, phv);
     282              :     }
     283         5546 :     list_free(vars);
     284         5546 : }
     285              : 
     286              : /*
     287              :  * fix_placeholder_input_needed_levels
     288              :  *      Adjust the "needed at" levels for placeholder inputs
     289              :  *
     290              :  * This is called after we've finished determining the eval_at levels for
     291              :  * all placeholders.  We need to make sure that all vars and placeholders
     292              :  * needed to evaluate each placeholder will be available at the scan or join
     293              :  * level where the evaluation will be done.  (It might seem that scan-level
     294              :  * evaluations aren't interesting, but that's not so: a LATERAL reference
     295              :  * within a placeholder's expression needs to cause the referenced var or
     296              :  * placeholder to be marked as needed in the scan where it's evaluated.)
     297              :  * Note that this loop can have side-effects on the ph_needed sets of other
     298              :  * PlaceHolderInfos; that's okay because we don't examine ph_needed here, so
     299              :  * there are no ordering issues to worry about.
     300              :  */
     301              : void
     302       253490 : fix_placeholder_input_needed_levels(PlannerInfo *root)
     303              : {
     304              :     ListCell   *lc;
     305              : 
     306       255454 :     foreach(lc, root->placeholder_list)
     307              :     {
     308         1964 :         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     309         1964 :         List       *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
     310              :                                            PVC_RECURSE_AGGREGATES |
     311              :                                            PVC_RECURSE_WINDOWFUNCS |
     312              :                                            PVC_INCLUDE_PLACEHOLDERS);
     313              : 
     314         1964 :         add_vars_to_targetlist(root, vars, phinfo->ph_eval_at);
     315         1964 :         list_free(vars);
     316              :     }
     317       253490 : }
     318              : 
     319              : /*
     320              :  * rebuild_placeholder_attr_needed
     321              :  *    Put back attr_needed bits for Vars/PHVs needed in PlaceHolderVars.
     322              :  *
     323              :  * This is used to rebuild attr_needed/ph_needed sets after removal of a
     324              :  * useless outer join.  It should match what
     325              :  * fix_placeholder_input_needed_levels did, except that we call
     326              :  * add_vars_to_attr_needed not add_vars_to_targetlist.
     327              :  */
     328              : void
     329         9052 : rebuild_placeholder_attr_needed(PlannerInfo *root)
     330              : {
     331              :     ListCell   *lc;
     332              : 
     333         9221 :     foreach(lc, root->placeholder_list)
     334              :     {
     335          169 :         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     336          169 :         List       *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
     337              :                                            PVC_RECURSE_AGGREGATES |
     338              :                                            PVC_RECURSE_WINDOWFUNCS |
     339              :                                            PVC_INCLUDE_PLACEHOLDERS);
     340              : 
     341          169 :         add_vars_to_attr_needed(root, vars, phinfo->ph_eval_at);
     342          169 :         list_free(vars);
     343              :     }
     344         9052 : }
     345              : 
     346              : /*
     347              :  * add_placeholders_to_base_rels
     348              :  *      Add any required PlaceHolderVars to base rels' targetlists.
     349              :  *
     350              :  * If any placeholder can be computed at a base rel and is needed above it,
     351              :  * add it to that rel's targetlist.  This might look like it could be merged
     352              :  * with fix_placeholder_input_needed_levels, but it must be separate because
     353              :  * join removal happens in between, and can change the ph_eval_at sets.  There
     354              :  * is essentially the same logic in add_placeholders_to_joinrel, but we can't
     355              :  * do that part until joinrels are formed.
     356              :  */
     357              : void
     358       253490 : add_placeholders_to_base_rels(PlannerInfo *root)
     359              : {
     360              :     ListCell   *lc;
     361              : 
     362       255449 :     foreach(lc, root->placeholder_list)
     363              :     {
     364         1959 :         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     365         1959 :         Relids      eval_at = phinfo->ph_eval_at;
     366              :         int         varno;
     367              : 
     368         3600 :         if (bms_get_singleton_member(eval_at, &varno) &&
     369         1641 :             bms_nonempty_difference(phinfo->ph_needed, eval_at))
     370              :         {
     371         1576 :             RelOptInfo *rel = find_base_rel(root, varno);
     372              : 
     373              :             /*
     374              :              * As in add_vars_to_targetlist(), a value computed at scan level
     375              :              * has not yet been nulled by any outer join, so its phnullingrels
     376              :              * should be empty.
     377              :              */
     378              :             Assert(phinfo->ph_var->phnullingrels == NULL);
     379              : 
     380              :             /* Copying the PHV might be unnecessary here, but be safe */
     381         1576 :             rel->reltarget->exprs = lappend(rel->reltarget->exprs,
     382         1576 :                                             copyObject(phinfo->ph_var));
     383              :             /* reltarget's cost and width fields will be updated later */
     384              :         }
     385              :     }
     386       253490 : }
     387              : 
     388              : /*
     389              :  * add_placeholders_to_joinrel
     390              :  *      Add any newly-computable PlaceHolderVars to a join rel's targetlist;
     391              :  *      and if computable PHVs contain lateral references, add those
     392              :  *      references to the joinrel's direct_lateral_relids.
     393              :  *
     394              :  * A join rel should emit a PlaceHolderVar if (a) the PHV can be computed
     395              :  * at or below this join level and (b) the PHV is needed above this level.
     396              :  * Our caller build_join_rel() has already added any PHVs that were computed
     397              :  * in either join input rel, so we need add only newly-computable ones to
     398              :  * the targetlist.  However, direct_lateral_relids must be updated for every
     399              :  * PHV computable at or below this join, as explained below.
     400              :  */
     401              : void
     402       179394 : add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
     403              :                             RelOptInfo *outer_rel, RelOptInfo *inner_rel,
     404              :                             SpecialJoinInfo *sjinfo)
     405              : {
     406       179394 :     Relids      relids = joinrel->relids;
     407       179394 :     int64       tuple_width = joinrel->reltarget->width;
     408              :     ListCell   *lc;
     409              : 
     410       183070 :     foreach(lc, root->placeholder_list)
     411              :     {
     412         3676 :         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     413              : 
     414              :         /* Is it computable here? */
     415         3676 :         if (bms_is_subset(phinfo->ph_eval_at, relids))
     416              :         {
     417              :             /* Is it still needed above this joinrel? */
     418         2536 :             if (bms_nonempty_difference(phinfo->ph_needed, relids))
     419              :             {
     420              :                 /*
     421              :                  * Yes, but only add to tlist if it wasn't computed in either
     422              :                  * input; otherwise it should be there already.  Also, we
     423              :                  * charge the cost of evaluating the contained expression if
     424              :                  * the PHV can be computed here but not in either input.  This
     425              :                  * is a bit bogus because we make the decision based on the
     426              :                  * first pair of possible input relations considered for the
     427              :                  * joinrel.  With other pairs, it might be possible to compute
     428              :                  * the PHV in one input or the other, and then we'd be double
     429              :                  * charging the PHV's cost for some join paths.  For now, live
     430              :                  * with that; but we might want to improve it later by
     431              :                  * refiguring the reltarget costs for each pair of inputs.
     432              :                  */
     433         1739 :                 if (!bms_is_subset(phinfo->ph_eval_at, outer_rel->relids) &&
     434         1262 :                     !bms_is_subset(phinfo->ph_eval_at, inner_rel->relids))
     435              :                 {
     436              :                     /* Copying might be unnecessary here, but be safe */
     437          393 :                     PlaceHolderVar *phv = copyObject(phinfo->ph_var);
     438              :                     QualCost    cost;
     439              : 
     440              :                     /*
     441              :                      * It'll start out not nulled by anything.  Joins above
     442              :                      * this one might add to its phnullingrels later, in much
     443              :                      * the same way as for Vars.
     444              :                      */
     445              :                     Assert(phv->phnullingrels == NULL);
     446              : 
     447          393 :                     joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs,
     448              :                                                         phv);
     449          393 :                     cost_qual_eval_node(&cost, (Node *) phv->phexpr, root);
     450          393 :                     joinrel->reltarget->cost.startup += cost.startup;
     451          393 :                     joinrel->reltarget->cost.per_tuple += cost.per_tuple;
     452          393 :                     tuple_width += phinfo->ph_width;
     453              :                 }
     454              :             }
     455              : 
     456              :             /*
     457              :              * Also adjust joinrel's direct_lateral_relids to include the
     458              :              * PHV's source rel(s).  We must do this even if we're not
     459              :              * actually going to emit the PHV, otherwise join_is_legal() will
     460              :              * reject valid join orderings.  (In principle maybe we could
     461              :              * instead remove the joinrel's lateral_relids dependency; but
     462              :              * that's complicated to get right, and cases where we're not
     463              :              * going to emit the PHV are too rare to justify the work.)
     464              :              *
     465              :              * In principle we should only do this if the join doesn't yet
     466              :              * include the PHV's source rel(s).  But our caller
     467              :              * build_join_rel() will clean things up by removing the join's
     468              :              * own relids from its direct_lateral_relids, so we needn't
     469              :              * account for that here.
     470              :              */
     471         2536 :             joinrel->direct_lateral_relids =
     472         2536 :                 bms_add_members(joinrel->direct_lateral_relids,
     473         2536 :                                 phinfo->ph_lateral);
     474              :         }
     475              :     }
     476              : 
     477       179394 :     joinrel->reltarget->width = clamp_width_est(tuple_width);
     478       179394 : }
     479              : 
     480              : /*
     481              :  * contain_placeholder_references_to
     482              :  *      Detect whether any PlaceHolderVars in the given clause contain
     483              :  *      references to the given relid (typically an OJ relid).
     484              :  *
     485              :  * "Contain" means that there's a use of the relid inside the PHV's
     486              :  * contained expression, so that changing the nullability status of
     487              :  * the rel might change what the PHV computes.
     488              :  *
     489              :  * The code here to cope with upper-level PHVs is likely dead, but keep it
     490              :  * anyway just in case.
     491              :  */
     492              : bool
     493        12607 : contain_placeholder_references_to(PlannerInfo *root, Node *clause,
     494              :                                   int relid)
     495              : {
     496              :     contain_placeholder_references_context context;
     497              : 
     498              :     /* We can answer quickly in the common case that there's no PHVs at all */
     499        12607 :     if (root->glob->lastPHId == 0)
     500        12087 :         return false;
     501              :     /* Else run the recursive search */
     502          520 :     context.relid = relid;
     503          520 :     context.sublevels_up = 0;
     504          520 :     return contain_placeholder_references_walker(clause, &context);
     505              : }
     506              : 
     507              : static bool
     508         1763 : contain_placeholder_references_walker(Node *node,
     509              :                                       contain_placeholder_references_context *context)
     510              : {
     511         1763 :     if (node == NULL)
     512          105 :         return false;
     513         1658 :     if (IsA(node, PlaceHolderVar))
     514              :     {
     515           45 :         PlaceHolderVar *phv = (PlaceHolderVar *) node;
     516              : 
     517              :         /* We should just look through PHVs of other query levels */
     518           45 :         if (phv->phlevelsup == context->sublevels_up)
     519              :         {
     520              :             /* If phrels matches, we found what we came for */
     521           45 :             if (bms_is_member(context->relid, phv->phrels))
     522           10 :                 return true;
     523              : 
     524              :             /*
     525              :              * We should not examine phnullingrels: what we are looking for is
     526              :              * references in the contained expression, not OJs that might null
     527              :              * the result afterwards.  Also, we don't need to recurse into the
     528              :              * contained expression, because phrels should adequately
     529              :              * summarize what's in there.  So we're done here.
     530              :              */
     531           35 :             return false;
     532              :         }
     533              :     }
     534         1613 :     else if (IsA(node, Query))
     535              :     {
     536              :         /* Recurse into RTE subquery or not-yet-planned sublink subquery */
     537              :         bool        result;
     538              : 
     539            0 :         context->sublevels_up++;
     540            0 :         result = query_tree_walker((Query *) node,
     541              :                                    contain_placeholder_references_walker,
     542              :                                    context,
     543              :                                    0);
     544            0 :         context->sublevels_up--;
     545            0 :         return result;
     546              :     }
     547         1613 :     return expression_tree_walker(node, contain_placeholder_references_walker,
     548              :                                   context);
     549              : }
     550              : 
     551              : /*
     552              :  * Compute the set of outer-join relids that can null a placeholder.
     553              :  *
     554              :  * This is analogous to RelOptInfo.nulling_relids for Vars, but we compute it
     555              :  * on-the-fly rather than saving it somewhere.  Currently the value is needed
     556              :  * at most once per query, so there's little value in doing otherwise.  If it
     557              :  * ever gains more widespread use, perhaps we should cache the result in
     558              :  * PlaceHolderInfo.
     559              :  */
     560              : Relids
     561          210 : get_placeholder_nulling_relids(PlannerInfo *root, PlaceHolderInfo *phinfo)
     562              : {
     563          210 :     Relids      result = NULL;
     564          210 :     int         relid = -1;
     565              : 
     566              :     /*
     567              :      * Form the union of all potential nulling OJs for each baserel included
     568              :      * in ph_eval_at.
     569              :      */
     570          480 :     while ((relid = bms_next_member(phinfo->ph_eval_at, relid)) > 0)
     571              :     {
     572          270 :         RelOptInfo *rel = root->simple_rel_array[relid];
     573              : 
     574              :         /* ignore the RTE_GROUP RTE */
     575          270 :         if (relid == root->group_rtindex)
     576            0 :             continue;
     577              : 
     578          270 :         if (rel == NULL)        /* must be an outer join */
     579              :         {
     580              :             Assert(bms_is_member(relid, root->outer_join_rels));
     581           10 :             continue;
     582              :         }
     583          260 :         result = bms_add_members(result, rel->nulling_relids);
     584              :     }
     585              : 
     586              :     /* Now remove any OJs already included in ph_eval_at, and we're done. */
     587          210 :     result = bms_del_members(result, phinfo->ph_eval_at);
     588          210 :     return result;
     589              : }
     590              : 
     591              : /*
     592              :  * strip_noop_phvs
     593              :  *    Strip no-op PlaceHolderVar nodes from the given expression tree.
     594              :  *
     595              :  * A PlaceHolderVar that is not marked as nullable (i.e., its phnullingrels
     596              :  * is empty) is effectively a no-op when it appears in a relation-scan-level
     597              :  * expression.  This function strips such PlaceHolderVars, which is useful
     598              :  * for matching expressions to index keys or partition keys in cases where
     599              :  * the expression has been wrapped in PlaceHolderVars during subquery pullup.
     600              :  *
     601              :  * IMPORTANT: the caller must ensure that the expression is a scan-level
     602              :  * expression, so that non-nullable PlaceHolderVars in it are indeed no-ops.
     603              :  *
     604              :  * The removal is performed recursively because PlaceHolderVars can be nested
     605              :  * or interleaved with other node types.  We must peel back all layers to
     606              :  * expose the base expression.
     607              :  *
     608              :  * As a performance optimization, we first use a lightweight walker to check
     609              :  * for the presence of strippable PlaceHolderVars.  The expensive mutator is
     610              :  * invoked only if a candidate is found, avoiding unnecessary memory allocation
     611              :  * and tree copying in the common case where no PlaceHolderVars are present.
     612              :  */
     613              : Node *
     614      3106330 : strip_noop_phvs(Node *node)
     615              : {
     616              :     /* Don't mutate/copy if no target PHVs exist */
     617      3106330 :     if (!contain_noop_phv_walker(node, NULL))
     618      3105370 :         return node;
     619              : 
     620          960 :     return strip_noop_phvs_mutator(node, NULL);
     621              : }
     622              : 
     623              : /*
     624              :  * contain_noop_phv_walker
     625              :  *    Detect if there are any PlaceHolderVars in the tree that are candidates
     626              :  *    for stripping.
     627              :  *
     628              :  * We identify a PlaceHolderVar as strippable only if its phnullingrels is
     629              :  * empty.
     630              :  */
     631              : static bool
     632      3187667 : contain_noop_phv_walker(Node *node, void *context)
     633              : {
     634      3187667 :     if (node == NULL)
     635         6767 :         return false;
     636              : 
     637      3180900 :     if (IsA(node, PlaceHolderVar))
     638              :     {
     639         1035 :         PlaceHolderVar *phv = (PlaceHolderVar *) node;
     640              : 
     641         1035 :         if (bms_is_empty(phv->phnullingrels))
     642          960 :             return true;
     643              :     }
     644              : 
     645      3179940 :     return expression_tree_walker(node, contain_noop_phv_walker,
     646              :                                   context);
     647              : }
     648              : 
     649              : /*
     650              :  * strip_noop_phvs_mutator
     651              :  *    Recursively remove PlaceHolderVars that are not marked nullable.
     652              :  *
     653              :  * We strip a PlaceHolderVar only if its phnullingrels is empty, replacing it
     654              :  * with its contained expression.
     655              :  */
     656              : static Node *
     657         2850 : strip_noop_phvs_mutator(Node *node, void *context)
     658              : {
     659         2850 :     if (node == NULL)
     660            0 :         return NULL;
     661              : 
     662         2850 :     if (IsA(node, PlaceHolderVar))
     663              :     {
     664          960 :         PlaceHolderVar *phv = (PlaceHolderVar *) node;
     665              : 
     666          960 :         if (bms_is_empty(phv->phnullingrels))
     667              :         {
     668              :             /* Recurse on its contained expression */
     669          960 :             return strip_noop_phvs_mutator((Node *) phv->phexpr,
     670              :                                            context);
     671              :         }
     672              : 
     673              :         /* Otherwise, keep this PHV but check its contained expression */
     674              :     }
     675              : 
     676         1890 :     return expression_tree_mutator(node, strip_noop_phvs_mutator,
     677              :                                    context);
     678              : }
        

Generated by: LCOV version 2.0-1