LCOV - code coverage report
Current view: top level - src/backend/optimizer/util - placeholder.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 92.5 % 147 136
Test Date: 2026-02-17 17:20:33 Functions: 100.0 % 12 12
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              : 
      39              : 
      40              : /*
      41              :  * make_placeholder_expr
      42              :  *      Make a PlaceHolderVar for the given expression.
      43              :  *
      44              :  * phrels is the syntactic location (as a set of relids) to attribute
      45              :  * to the expression.
      46              :  *
      47              :  * The caller is responsible for adjusting phlevelsup and phnullingrels
      48              :  * as needed.  Because we do not know here which query level the PHV
      49              :  * will be associated with, it's important that this function touches
      50              :  * only root->glob; messing with other parts of PlannerInfo would be
      51              :  * likely to do the wrong thing.
      52              :  */
      53              : PlaceHolderVar *
      54         1382 : make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels)
      55              : {
      56         1382 :     PlaceHolderVar *phv = makeNode(PlaceHolderVar);
      57              : 
      58         1382 :     phv->phexpr = expr;
      59         1382 :     phv->phrels = phrels;
      60         1382 :     phv->phnullingrels = NULL;   /* caller may change this later */
      61         1382 :     phv->phid = ++(root->glob->lastPHId);
      62         1382 :     phv->phlevelsup = 0;     /* caller may change this later */
      63              : 
      64         1382 :     return phv;
      65              : }
      66              : 
      67              : /*
      68              :  * find_placeholder_info
      69              :  *      Fetch the PlaceHolderInfo for the given PHV
      70              :  *
      71              :  * If the PlaceHolderInfo doesn't exist yet, create it if we haven't yet
      72              :  * frozen the set of PlaceHolderInfos for the query; else throw an error.
      73              :  *
      74              :  * This is separate from make_placeholder_expr because subquery pullup has
      75              :  * to make PlaceHolderVars for expressions that might not be used at all in
      76              :  * the upper query, or might not remain after const-expression simplification.
      77              :  * We build PlaceHolderInfos only for PHVs that are still present in the
      78              :  * simplified query passed to query_planner().
      79              :  *
      80              :  * Note: this should only be called after query_planner() has started.
      81              :  */
      82              : PlaceHolderInfo *
      83         4990 : find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv)
      84              : {
      85              :     PlaceHolderInfo *phinfo;
      86              :     Relids      rels_used;
      87              : 
      88              :     /* if this ever isn't true, we'd need to be able to look in parent lists */
      89              :     Assert(phv->phlevelsup == 0);
      90              : 
      91              :     /* Use placeholder_array to look up existing PlaceHolderInfo quickly */
      92         4990 :     if (phv->phid < root->placeholder_array_size)
      93         4269 :         phinfo = root->placeholder_array[phv->phid];
      94              :     else
      95          721 :         phinfo = NULL;
      96         4990 :     if (phinfo != NULL)
      97              :     {
      98              :         Assert(phinfo->phid == phv->phid);
      99         3853 :         return phinfo;
     100              :     }
     101              : 
     102              :     /* Not found, so create it */
     103         1137 :     if (root->placeholdersFrozen)
     104            0 :         elog(ERROR, "too late to create a new PlaceHolderInfo");
     105              : 
     106         1137 :     phinfo = makeNode(PlaceHolderInfo);
     107              : 
     108         1137 :     phinfo->phid = phv->phid;
     109         1137 :     phinfo->ph_var = copyObject(phv);
     110              : 
     111              :     /*
     112              :      * By convention, phinfo->ph_var->phnullingrels is always empty, since the
     113              :      * PlaceHolderInfo represents the initially-calculated state of the
     114              :      * PlaceHolderVar.  PlaceHolderVars appearing in the query tree might have
     115              :      * varying values of phnullingrels, reflecting outer joins applied above
     116              :      * the calculation level.
     117              :      */
     118         1137 :     phinfo->ph_var->phnullingrels = NULL;
     119              : 
     120              :     /*
     121              :      * Any referenced rels that are outside the PHV's syntactic scope are
     122              :      * LATERAL references, which should be included in ph_lateral but not in
     123              :      * ph_eval_at.  If no referenced rels are within the syntactic scope,
     124              :      * force evaluation at the syntactic location.
     125              :      */
     126         1137 :     rels_used = pull_varnos(root, (Node *) phv->phexpr);
     127         1137 :     phinfo->ph_lateral = bms_difference(rels_used, phv->phrels);
     128         1137 :     phinfo->ph_eval_at = bms_int_members(rels_used, phv->phrels);
     129              :     /* If no contained vars, force evaluation at syntactic location */
     130         1137 :     if (bms_is_empty(phinfo->ph_eval_at))
     131              :     {
     132          561 :         phinfo->ph_eval_at = bms_copy(phv->phrels);
     133              :         Assert(!bms_is_empty(phinfo->ph_eval_at));
     134              :     }
     135         1137 :     phinfo->ph_needed = NULL;    /* initially it's unused */
     136              :     /* for the moment, estimate width using just the datatype info */
     137         1137 :     phinfo->ph_width = get_typavgwidth(exprType((Node *) phv->phexpr),
     138         1137 :                                        exprTypmod((Node *) phv->phexpr));
     139              : 
     140              :     /*
     141              :      * Add to both placeholder_list and placeholder_array.  Note: because we
     142              :      * store pointers to the PlaceHolderInfos in two data structures, it'd be
     143              :      * unsafe to pass the whole placeholder_list structure through
     144              :      * expression_tree_mutator or the like --- or at least, you'd have to
     145              :      * rebuild the placeholder_array afterwards.
     146              :      */
     147         1137 :     root->placeholder_list = lappend(root->placeholder_list, phinfo);
     148              : 
     149         1137 :     if (phinfo->phid >= root->placeholder_array_size)
     150              :     {
     151              :         /* Must allocate or enlarge placeholder_array */
     152              :         int         new_size;
     153              : 
     154          721 :         new_size = root->placeholder_array_size ? root->placeholder_array_size * 2 : 8;
     155          721 :         while (phinfo->phid >= new_size)
     156            0 :             new_size *= 2;
     157          721 :         if (root->placeholder_array)
     158            0 :             root->placeholder_array =
     159            0 :                 repalloc0_array(root->placeholder_array, PlaceHolderInfo *, root->placeholder_array_size, new_size);
     160              :         else
     161          721 :             root->placeholder_array =
     162          721 :                 palloc0_array(PlaceHolderInfo *, new_size);
     163          721 :         root->placeholder_array_size = new_size;
     164              :     }
     165         1137 :     root->placeholder_array[phinfo->phid] = phinfo;
     166              : 
     167              :     /*
     168              :      * The PHV's contained expression may contain other, lower-level PHVs.  We
     169              :      * now know we need to get those into the PlaceHolderInfo list, too, so we
     170              :      * may as well do that immediately.
     171              :      */
     172         1137 :     find_placeholders_in_expr(root, (Node *) phinfo->ph_var->phexpr);
     173              : 
     174         1137 :     return phinfo;
     175              : }
     176              : 
     177              : /*
     178              :  * find_placeholders_in_jointree
     179              :  *      Search the jointree for PlaceHolderVars, and build PlaceHolderInfos
     180              :  *
     181              :  * We don't need to look at the targetlist because build_base_rel_tlists()
     182              :  * will already have made entries for any PHVs in the tlist.
     183              :  */
     184              : void
     185       172439 : find_placeholders_in_jointree(PlannerInfo *root)
     186              : {
     187              :     /* This must be done before freezing the set of PHIs */
     188              :     Assert(!root->placeholdersFrozen);
     189              : 
     190              :     /* We need do nothing if the query contains no PlaceHolderVars */
     191       172439 :     if (root->glob->lastPHId != 0)
     192              :     {
     193              :         /* Start recursion at top of jointree */
     194              :         Assert(root->parse->jointree != NULL &&
     195              :                IsA(root->parse->jointree, FromExpr));
     196          961 :         find_placeholders_recurse(root, (Node *) root->parse->jointree);
     197              :     }
     198       172439 : }
     199              : 
     200              : /*
     201              :  * find_placeholders_recurse
     202              :  *    One recursion level of find_placeholders_in_jointree.
     203              :  *
     204              :  * jtnode is the current jointree node to examine.
     205              :  */
     206              : static void
     207         4557 : find_placeholders_recurse(PlannerInfo *root, Node *jtnode)
     208              : {
     209         4557 :     if (jtnode == NULL)
     210            0 :         return;
     211         4557 :     if (IsA(jtnode, RangeTblRef))
     212              :     {
     213              :         /* No quals to deal with here */
     214              :     }
     215         2306 :     else if (IsA(jtnode, FromExpr))
     216              :     {
     217         1156 :         FromExpr   *f = (FromExpr *) jtnode;
     218              :         ListCell   *l;
     219              : 
     220              :         /*
     221              :          * First, recurse to handle child joins.
     222              :          */
     223         2452 :         foreach(l, f->fromlist)
     224              :         {
     225         1296 :             find_placeholders_recurse(root, lfirst(l));
     226              :         }
     227              : 
     228              :         /*
     229              :          * Now process the top-level quals.
     230              :          */
     231         1156 :         find_placeholders_in_expr(root, f->quals);
     232              :     }
     233         1150 :     else if (IsA(jtnode, JoinExpr))
     234              :     {
     235         1150 :         JoinExpr   *j = (JoinExpr *) jtnode;
     236              : 
     237              :         /*
     238              :          * First, recurse to handle child joins.
     239              :          */
     240         1150 :         find_placeholders_recurse(root, j->larg);
     241         1150 :         find_placeholders_recurse(root, j->rarg);
     242              : 
     243              :         /* Process the qual clauses */
     244         1150 :         find_placeholders_in_expr(root, j->quals);
     245              :     }
     246              :     else
     247            0 :         elog(ERROR, "unrecognized node type: %d",
     248              :              (int) nodeTag(jtnode));
     249              : }
     250              : 
     251              : /*
     252              :  * find_placeholders_in_expr
     253              :  *      Find all PlaceHolderVars in the given expression, and create
     254              :  *      PlaceHolderInfo entries for them.
     255              :  */
     256              : static void
     257         3443 : find_placeholders_in_expr(PlannerInfo *root, Node *expr)
     258              : {
     259              :     List       *vars;
     260              :     ListCell   *vl;
     261              : 
     262              :     /*
     263              :      * pull_var_clause does more than we need here, but it'll do and it's
     264              :      * convenient to use.
     265              :      */
     266         3443 :     vars = pull_var_clause(expr,
     267              :                            PVC_RECURSE_AGGREGATES |
     268              :                            PVC_RECURSE_WINDOWFUNCS |
     269              :                            PVC_INCLUDE_PLACEHOLDERS);
     270         7529 :     foreach(vl, vars)
     271              :     {
     272         4086 :         PlaceHolderVar *phv = (PlaceHolderVar *) lfirst(vl);
     273              : 
     274              :         /* Ignore any plain Vars */
     275         4086 :         if (!IsA(phv, PlaceHolderVar))
     276         3635 :             continue;
     277              : 
     278              :         /* Create a PlaceHolderInfo entry if there's not one already */
     279          451 :         (void) find_placeholder_info(root, phv);
     280              :     }
     281         3443 :     list_free(vars);
     282         3443 : }
     283              : 
     284              : /*
     285              :  * fix_placeholder_input_needed_levels
     286              :  *      Adjust the "needed at" levels for placeholder inputs
     287              :  *
     288              :  * This is called after we've finished determining the eval_at levels for
     289              :  * all placeholders.  We need to make sure that all vars and placeholders
     290              :  * needed to evaluate each placeholder will be available at the scan or join
     291              :  * level where the evaluation will be done.  (It might seem that scan-level
     292              :  * evaluations aren't interesting, but that's not so: a LATERAL reference
     293              :  * within a placeholder's expression needs to cause the referenced var or
     294              :  * placeholder to be marked as needed in the scan where it's evaluated.)
     295              :  * Note that this loop can have side-effects on the ph_needed sets of other
     296              :  * PlaceHolderInfos; that's okay because we don't examine ph_needed here, so
     297              :  * there are no ordering issues to worry about.
     298              :  */
     299              : void
     300       172439 : fix_placeholder_input_needed_levels(PlannerInfo *root)
     301              : {
     302              :     ListCell   *lc;
     303              : 
     304       173576 :     foreach(lc, root->placeholder_list)
     305              :     {
     306         1137 :         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     307         1137 :         List       *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
     308              :                                            PVC_RECURSE_AGGREGATES |
     309              :                                            PVC_RECURSE_WINDOWFUNCS |
     310              :                                            PVC_INCLUDE_PLACEHOLDERS);
     311              : 
     312         1137 :         add_vars_to_targetlist(root, vars, phinfo->ph_eval_at);
     313         1137 :         list_free(vars);
     314              :     }
     315       172439 : }
     316              : 
     317              : /*
     318              :  * rebuild_placeholder_attr_needed
     319              :  *    Put back attr_needed bits for Vars/PHVs needed in PlaceHolderVars.
     320              :  *
     321              :  * This is used to rebuild attr_needed/ph_needed sets after removal of a
     322              :  * useless outer join.  It should match what
     323              :  * fix_placeholder_input_needed_levels did, except that we call
     324              :  * add_vars_to_attr_needed not add_vars_to_targetlist.
     325              :  */
     326              : void
     327         6263 : rebuild_placeholder_attr_needed(PlannerInfo *root)
     328              : {
     329              :     ListCell   *lc;
     330              : 
     331         6374 :     foreach(lc, root->placeholder_list)
     332              :     {
     333          111 :         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     334          111 :         List       *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
     335              :                                            PVC_RECURSE_AGGREGATES |
     336              :                                            PVC_RECURSE_WINDOWFUNCS |
     337              :                                            PVC_INCLUDE_PLACEHOLDERS);
     338              : 
     339          111 :         add_vars_to_attr_needed(root, vars, phinfo->ph_eval_at);
     340          111 :         list_free(vars);
     341              :     }
     342         6263 : }
     343              : 
     344              : /*
     345              :  * add_placeholders_to_base_rels
     346              :  *      Add any required PlaceHolderVars to base rels' targetlists.
     347              :  *
     348              :  * If any placeholder can be computed at a base rel and is needed above it,
     349              :  * add it to that rel's targetlist.  This might look like it could be merged
     350              :  * with fix_placeholder_input_needed_levels, but it must be separate because
     351              :  * join removal happens in between, and can change the ph_eval_at sets.  There
     352              :  * is essentially the same logic in add_placeholders_to_joinrel, but we can't
     353              :  * do that part until joinrels are formed.
     354              :  */
     355              : void
     356       172439 : add_placeholders_to_base_rels(PlannerInfo *root)
     357              : {
     358              :     ListCell   *lc;
     359              : 
     360       173573 :     foreach(lc, root->placeholder_list)
     361              :     {
     362         1134 :         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     363         1134 :         Relids      eval_at = phinfo->ph_eval_at;
     364              :         int         varno;
     365              : 
     366         2056 :         if (bms_get_singleton_member(eval_at, &varno) &&
     367          922 :             bms_nonempty_difference(phinfo->ph_needed, eval_at))
     368              :         {
     369          883 :             RelOptInfo *rel = find_base_rel(root, varno);
     370              : 
     371              :             /*
     372              :              * As in add_vars_to_targetlist(), a value computed at scan level
     373              :              * has not yet been nulled by any outer join, so its phnullingrels
     374              :              * should be empty.
     375              :              */
     376              :             Assert(phinfo->ph_var->phnullingrels == NULL);
     377              : 
     378              :             /* Copying the PHV might be unnecessary here, but be safe */
     379          883 :             rel->reltarget->exprs = lappend(rel->reltarget->exprs,
     380          883 :                                             copyObject(phinfo->ph_var));
     381              :             /* reltarget's cost and width fields will be updated later */
     382              :         }
     383              :     }
     384       172439 : }
     385              : 
     386              : /*
     387              :  * add_placeholders_to_joinrel
     388              :  *      Add any newly-computable PlaceHolderVars to a join rel's targetlist;
     389              :  *      and if computable PHVs contain lateral references, add those
     390              :  *      references to the joinrel's direct_lateral_relids.
     391              :  *
     392              :  * A join rel should emit a PlaceHolderVar if (a) the PHV can be computed
     393              :  * at or below this join level and (b) the PHV is needed above this level.
     394              :  * Our caller build_join_rel() has already added any PHVs that were computed
     395              :  * in either join input rel, so we need add only newly-computable ones to
     396              :  * the targetlist.  However, direct_lateral_relids must be updated for every
     397              :  * PHV computable at or below this join, as explained below.
     398              :  */
     399              : void
     400       113477 : add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
     401              :                             RelOptInfo *outer_rel, RelOptInfo *inner_rel,
     402              :                             SpecialJoinInfo *sjinfo)
     403              : {
     404       113477 :     Relids      relids = joinrel->relids;
     405       113477 :     int64       tuple_width = joinrel->reltarget->width;
     406              :     ListCell   *lc;
     407              : 
     408       115708 :     foreach(lc, root->placeholder_list)
     409              :     {
     410         2231 :         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     411              : 
     412              :         /* Is it computable here? */
     413         2231 :         if (bms_is_subset(phinfo->ph_eval_at, relids))
     414              :         {
     415              :             /* Is it still needed above this joinrel? */
     416         1543 :             if (bms_nonempty_difference(phinfo->ph_needed, relids))
     417              :             {
     418              :                 /*
     419              :                  * Yes, but only add to tlist if it wasn't computed in either
     420              :                  * input; otherwise it should be there already.  Also, we
     421              :                  * charge the cost of evaluating the contained expression if
     422              :                  * the PHV can be computed here but not in either input.  This
     423              :                  * is a bit bogus because we make the decision based on the
     424              :                  * first pair of possible input relations considered for the
     425              :                  * joinrel.  With other pairs, it might be possible to compute
     426              :                  * the PHV in one input or the other, and then we'd be double
     427              :                  * charging the PHV's cost for some join paths.  For now, live
     428              :                  * with that; but we might want to improve it later by
     429              :                  * refiguring the reltarget costs for each pair of inputs.
     430              :                  */
     431         1044 :                 if (!bms_is_subset(phinfo->ph_eval_at, outer_rel->relids) &&
     432          779 :                     !bms_is_subset(phinfo->ph_eval_at, inner_rel->relids))
     433              :                 {
     434              :                     /* Copying might be unnecessary here, but be safe */
     435          247 :                     PlaceHolderVar *phv = copyObject(phinfo->ph_var);
     436              :                     QualCost    cost;
     437              : 
     438              :                     /*
     439              :                      * It'll start out not nulled by anything.  Joins above
     440              :                      * this one might add to its phnullingrels later, in much
     441              :                      * the same way as for Vars.
     442              :                      */
     443              :                     Assert(phv->phnullingrels == NULL);
     444              : 
     445          247 :                     joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs,
     446              :                                                         phv);
     447          247 :                     cost_qual_eval_node(&cost, (Node *) phv->phexpr, root);
     448          247 :                     joinrel->reltarget->cost.startup += cost.startup;
     449          247 :                     joinrel->reltarget->cost.per_tuple += cost.per_tuple;
     450          247 :                     tuple_width += phinfo->ph_width;
     451              :                 }
     452              :             }
     453              : 
     454              :             /*
     455              :              * Also adjust joinrel's direct_lateral_relids to include the
     456              :              * PHV's source rel(s).  We must do this even if we're not
     457              :              * actually going to emit the PHV, otherwise join_is_legal() will
     458              :              * reject valid join orderings.  (In principle maybe we could
     459              :              * instead remove the joinrel's lateral_relids dependency; but
     460              :              * that's complicated to get right, and cases where we're not
     461              :              * going to emit the PHV are too rare to justify the work.)
     462              :              *
     463              :              * In principle we should only do this if the join doesn't yet
     464              :              * include the PHV's source rel(s).  But our caller
     465              :              * build_join_rel() will clean things up by removing the join's
     466              :              * own relids from its direct_lateral_relids, so we needn't
     467              :              * account for that here.
     468              :              */
     469         1543 :             joinrel->direct_lateral_relids =
     470         1543 :                 bms_add_members(joinrel->direct_lateral_relids,
     471         1543 :                                 phinfo->ph_lateral);
     472              :         }
     473              :     }
     474              : 
     475       113477 :     joinrel->reltarget->width = clamp_width_est(tuple_width);
     476       113477 : }
     477              : 
     478              : /*
     479              :  * contain_placeholder_references_to
     480              :  *      Detect whether any PlaceHolderVars in the given clause contain
     481              :  *      references to the given relid (typically an OJ relid).
     482              :  *
     483              :  * "Contain" means that there's a use of the relid inside the PHV's
     484              :  * contained expression, so that changing the nullability status of
     485              :  * the rel might change what the PHV computes.
     486              :  *
     487              :  * The code here to cope with upper-level PHVs is likely dead, but keep it
     488              :  * anyway just in case.
     489              :  */
     490              : bool
     491         8932 : contain_placeholder_references_to(PlannerInfo *root, Node *clause,
     492              :                                   int relid)
     493              : {
     494              :     contain_placeholder_references_context context;
     495              : 
     496              :     /* We can answer quickly in the common case that there's no PHVs at all */
     497         8932 :     if (root->glob->lastPHId == 0)
     498         8616 :         return false;
     499              :     /* Else run the recursive search */
     500          316 :     context.relid = relid;
     501          316 :     context.sublevels_up = 0;
     502          316 :     return contain_placeholder_references_walker(clause, &context);
     503              : }
     504              : 
     505              : static bool
     506         1075 : contain_placeholder_references_walker(Node *node,
     507              :                                       contain_placeholder_references_context *context)
     508              : {
     509         1075 :     if (node == NULL)
     510           63 :         return false;
     511         1012 :     if (IsA(node, PlaceHolderVar))
     512              :     {
     513           27 :         PlaceHolderVar *phv = (PlaceHolderVar *) node;
     514              : 
     515              :         /* We should just look through PHVs of other query levels */
     516           27 :         if (phv->phlevelsup == context->sublevels_up)
     517              :         {
     518              :             /* If phrels matches, we found what we came for */
     519           27 :             if (bms_is_member(context->relid, phv->phrels))
     520            6 :                 return true;
     521              : 
     522              :             /*
     523              :              * We should not examine phnullingrels: what we are looking for is
     524              :              * references in the contained expression, not OJs that might null
     525              :              * the result afterwards.  Also, we don't need to recurse into the
     526              :              * contained expression, because phrels should adequately
     527              :              * summarize what's in there.  So we're done here.
     528              :              */
     529           21 :             return false;
     530              :         }
     531              :     }
     532          985 :     else if (IsA(node, Query))
     533              :     {
     534              :         /* Recurse into RTE subquery or not-yet-planned sublink subquery */
     535              :         bool        result;
     536              : 
     537            0 :         context->sublevels_up++;
     538            0 :         result = query_tree_walker((Query *) node,
     539              :                                    contain_placeholder_references_walker,
     540              :                                    context,
     541              :                                    0);
     542            0 :         context->sublevels_up--;
     543            0 :         return result;
     544              :     }
     545          985 :     return expression_tree_walker(node, contain_placeholder_references_walker,
     546              :                                   context);
     547              : }
     548              : 
     549              : /*
     550              :  * Compute the set of outer-join relids that can null a placeholder.
     551              :  *
     552              :  * This is analogous to RelOptInfo.nulling_relids for Vars, but we compute it
     553              :  * on-the-fly rather than saving it somewhere.  Currently the value is needed
     554              :  * at most once per query, so there's little value in doing otherwise.  If it
     555              :  * ever gains more widespread use, perhaps we should cache the result in
     556              :  * PlaceHolderInfo.
     557              :  */
     558              : Relids
     559          126 : get_placeholder_nulling_relids(PlannerInfo *root, PlaceHolderInfo *phinfo)
     560              : {
     561          126 :     Relids      result = NULL;
     562          126 :     int         relid = -1;
     563              : 
     564              :     /*
     565              :      * Form the union of all potential nulling OJs for each baserel included
     566              :      * in ph_eval_at.
     567              :      */
     568          288 :     while ((relid = bms_next_member(phinfo->ph_eval_at, relid)) > 0)
     569              :     {
     570          162 :         RelOptInfo *rel = root->simple_rel_array[relid];
     571              : 
     572              :         /* ignore the RTE_GROUP RTE */
     573          162 :         if (relid == root->group_rtindex)
     574            0 :             continue;
     575              : 
     576          162 :         if (rel == NULL)        /* must be an outer join */
     577              :         {
     578              :             Assert(bms_is_member(relid, root->outer_join_rels));
     579            6 :             continue;
     580              :         }
     581          156 :         result = bms_add_members(result, rel->nulling_relids);
     582              :     }
     583              : 
     584              :     /* Now remove any OJs already included in ph_eval_at, and we're done. */
     585          126 :     result = bms_del_members(result, phinfo->ph_eval_at);
     586          126 :     return result;
     587              : }
        

Generated by: LCOV version 2.0-1