LCOV - code coverage report
Current view: top level - src/backend/optimizer/util - placeholder.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 125 135 92.6 %
Date: 2024-11-21 09:14:53 Functions: 11 11 100.0 %
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-2024, 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        1682 : make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels)
      55             : {
      56        1682 :     PlaceHolderVar *phv = makeNode(PlaceHolderVar);
      57             : 
      58        1682 :     phv->phexpr = expr;
      59        1682 :     phv->phrels = phrels;
      60        1682 :     phv->phnullingrels = NULL;   /* caller may change this later */
      61        1682 :     phv->phid = ++(root->glob->lastPHId);
      62        1682 :     phv->phlevelsup = 0;     /* caller may change this later */
      63             : 
      64        1682 :     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        6290 : 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        6290 :     if (phv->phid < root->placeholder_array_size)
      93        5380 :         phinfo = root->placeholder_array[phv->phid];
      94             :     else
      95         910 :         phinfo = NULL;
      96        6290 :     if (phinfo != NULL)
      97             :     {
      98             :         Assert(phinfo->phid == phv->phid);
      99        5078 :         return phinfo;
     100             :     }
     101             : 
     102             :     /* Not found, so create it */
     103        1212 :     if (root->placeholdersFrozen)
     104           0 :         elog(ERROR, "too late to create a new PlaceHolderInfo");
     105             : 
     106        1212 :     phinfo = makeNode(PlaceHolderInfo);
     107             : 
     108        1212 :     phinfo->phid = phv->phid;
     109        1212 :     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        1212 :     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        1212 :     rels_used = pull_varnos(root, (Node *) phv->phexpr);
     127        1212 :     phinfo->ph_lateral = bms_difference(rels_used, phv->phrels);
     128        1212 :     phinfo->ph_eval_at = bms_int_members(rels_used, phv->phrels);
     129             :     /* If no contained vars, force evaluation at syntactic location */
     130        1212 :     if (bms_is_empty(phinfo->ph_eval_at))
     131             :     {
     132         900 :         phinfo->ph_eval_at = bms_copy(phv->phrels);
     133             :         Assert(!bms_is_empty(phinfo->ph_eval_at));
     134             :     }
     135        1212 :     phinfo->ph_needed = NULL;    /* initially it's unused */
     136             :     /* for the moment, estimate width using just the datatype info */
     137        1212 :     phinfo->ph_width = get_typavgwidth(exprType((Node *) phv->phexpr),
     138        1212 :                                        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        1212 :     root->placeholder_list = lappend(root->placeholder_list, phinfo);
     148             : 
     149        1212 :     if (phinfo->phid >= root->placeholder_array_size)
     150             :     {
     151             :         /* Must allocate or enlarge placeholder_array */
     152             :         int         new_size;
     153             : 
     154         910 :         new_size = root->placeholder_array_size ? root->placeholder_array_size * 2 : 8;
     155         910 :         while (phinfo->phid >= new_size)
     156           0 :             new_size *= 2;
     157         910 :         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         910 :             root->placeholder_array =
     162         910 :                 palloc0_array(PlaceHolderInfo *, new_size);
     163         910 :         root->placeholder_array_size = new_size;
     164             :     }
     165        1212 :     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        1212 :     find_placeholders_in_expr(root, (Node *) phinfo->ph_var->phexpr);
     173             : 
     174        1212 :     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      287648 : 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      287648 :     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        1250 :         find_placeholders_recurse(root, (Node *) root->parse->jointree);
     197             :     }
     198      287648 : }
     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        6632 : find_placeholders_recurse(PlannerInfo *root, Node *jtnode)
     208             : {
     209        6632 :     if (jtnode == NULL)
     210           0 :         return;
     211        6632 :     if (IsA(jtnode, RangeTblRef))
     212             :     {
     213             :         /* No quals to deal with here */
     214             :     }
     215        3346 :     else if (IsA(jtnode, FromExpr))
     216             :     {
     217        1488 :         FromExpr   *f = (FromExpr *) jtnode;
     218             :         ListCell   *l;
     219             : 
     220             :         /*
     221             :          * First, recurse to handle child joins.
     222             :          */
     223        3154 :         foreach(l, f->fromlist)
     224             :         {
     225        1666 :             find_placeholders_recurse(root, lfirst(l));
     226             :         }
     227             : 
     228             :         /*
     229             :          * Now process the top-level quals.
     230             :          */
     231        1488 :         find_placeholders_in_expr(root, f->quals);
     232             :     }
     233        1858 :     else if (IsA(jtnode, JoinExpr))
     234             :     {
     235        1858 :         JoinExpr   *j = (JoinExpr *) jtnode;
     236             : 
     237             :         /*
     238             :          * First, recurse to handle child joins.
     239             :          */
     240        1858 :         find_placeholders_recurse(root, j->larg);
     241        1858 :         find_placeholders_recurse(root, j->rarg);
     242             : 
     243             :         /* Process the qual clauses */
     244        1858 :         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        4558 : 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        4558 :     vars = pull_var_clause(expr,
     267             :                            PVC_RECURSE_AGGREGATES |
     268             :                            PVC_RECURSE_WINDOWFUNCS |
     269             :                            PVC_INCLUDE_PLACEHOLDERS);
     270        9832 :     foreach(vl, vars)
     271             :     {
     272        5274 :         PlaceHolderVar *phv = (PlaceHolderVar *) lfirst(vl);
     273             : 
     274             :         /* Ignore any plain Vars */
     275        5274 :         if (!IsA(phv, PlaceHolderVar))
     276        4618 :             continue;
     277             : 
     278             :         /* Create a PlaceHolderInfo entry if there's not one already */
     279         656 :         (void) find_placeholder_info(root, phv);
     280             :     }
     281        4558 :     list_free(vars);
     282        4558 : }
     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      287648 : fix_placeholder_input_needed_levels(PlannerInfo *root)
     301             : {
     302             :     ListCell   *lc;
     303             : 
     304      288860 :     foreach(lc, root->placeholder_list)
     305             :     {
     306        1212 :         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     307        1212 :         List       *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
     308             :                                            PVC_RECURSE_AGGREGATES |
     309             :                                            PVC_RECURSE_WINDOWFUNCS |
     310             :                                            PVC_INCLUDE_PLACEHOLDERS);
     311             : 
     312        1212 :         add_vars_to_targetlist(root, vars, phinfo->ph_eval_at);
     313        1212 :         list_free(vars);
     314             :     }
     315      287648 : }
     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        9488 : rebuild_placeholder_attr_needed(PlannerInfo *root)
     328             : {
     329             :     ListCell   *lc;
     330             : 
     331        9608 :     foreach(lc, root->placeholder_list)
     332             :     {
     333         120 :         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     334         120 :         List       *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
     335             :                                            PVC_RECURSE_AGGREGATES |
     336             :                                            PVC_RECURSE_WINDOWFUNCS |
     337             :                                            PVC_INCLUDE_PLACEHOLDERS);
     338             : 
     339         120 :         add_vars_to_attr_needed(root, vars, phinfo->ph_eval_at);
     340         120 :         list_free(vars);
     341             :     }
     342        9488 : }
     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      287648 : add_placeholders_to_base_rels(PlannerInfo *root)
     357             : {
     358             :     ListCell   *lc;
     359             : 
     360      288854 :     foreach(lc, root->placeholder_list)
     361             :     {
     362        1206 :         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     363        1206 :         Relids      eval_at = phinfo->ph_eval_at;
     364             :         int         varno;
     365             : 
     366        2084 :         if (bms_get_singleton_member(eval_at, &varno) &&
     367         878 :             bms_nonempty_difference(phinfo->ph_needed, eval_at))
     368             :         {
     369         824 :             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         824 :             rel->reltarget->exprs = lappend(rel->reltarget->exprs,
     380         824 :                                             copyObject(phinfo->ph_var));
     381             :             /* reltarget's cost and width fields will be updated later */
     382             :         }
     383             :     }
     384      287648 : }
     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      178794 : add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
     401             :                             RelOptInfo *outer_rel, RelOptInfo *inner_rel,
     402             :                             SpecialJoinInfo *sjinfo)
     403             : {
     404      178794 :     Relids      relids = joinrel->relids;
     405      178794 :     int64       tuple_width = joinrel->reltarget->width;
     406             :     ListCell   *lc;
     407             : 
     408      182326 :     foreach(lc, root->placeholder_list)
     409             :     {
     410        3532 :         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     411             : 
     412             :         /* Is it computable here? */
     413        3532 :         if (bms_is_subset(phinfo->ph_eval_at, relids))
     414             :         {
     415             :             /* Is it still needed above this joinrel? */
     416        2474 :             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        1560 :                 if (!bms_is_subset(phinfo->ph_eval_at, outer_rel->relids) &&
     432        1198 :                     !bms_is_subset(phinfo->ph_eval_at, inner_rel->relids))
     433             :                 {
     434             :                     /* Copying might be unnecessary here, but be safe */
     435         368 :                     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         368 :                     joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs,
     446             :                                                         phv);
     447         368 :                     cost_qual_eval_node(&cost, (Node *) phv->phexpr, root);
     448         368 :                     joinrel->reltarget->cost.startup += cost.startup;
     449         368 :                     joinrel->reltarget->cost.per_tuple += cost.per_tuple;
     450         368 :                     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        2474 :             joinrel->direct_lateral_relids =
     470        2474 :                 bms_add_members(joinrel->direct_lateral_relids,
     471        2474 :                                 phinfo->ph_lateral);
     472             :         }
     473             :     }
     474             : 
     475      178794 :     joinrel->reltarget->width = clamp_width_est(tuple_width);
     476      178794 : }
     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       14352 : 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       14352 :     if (root->glob->lastPHId == 0)
     498       13750 :         return false;
     499             :     /* Else run the recursive search */
     500         602 :     context.relid = relid;
     501         602 :     context.sublevels_up = 0;
     502         602 :     return contain_placeholder_references_walker(clause, &context);
     503             : }
     504             : 
     505             : static bool
     506        2132 : contain_placeholder_references_walker(Node *node,
     507             :                                       contain_placeholder_references_context *context)
     508             : {
     509        2132 :     if (node == NULL)
     510          96 :         return false;
     511        2036 :     if (IsA(node, PlaceHolderVar))
     512             :     {
     513          42 :         PlaceHolderVar *phv = (PlaceHolderVar *) node;
     514             : 
     515             :         /* We should just look through PHVs of other query levels */
     516          42 :         if (phv->phlevelsup == context->sublevels_up)
     517             :         {
     518             :             /* If phrels matches, we found what we came for */
     519          42 :             if (bms_is_member(context->relid, phv->phrels))
     520          12 :                 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          30 :             return false;
     530             :         }
     531             :     }
     532        1994 :     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        1994 :     return expression_tree_walker(node, contain_placeholder_references_walker,
     546             :                                   context);
     547             : }

Generated by: LCOV version 1.14