LCOV - code coverage report
Current view: top level - src/backend/optimizer/util - placeholder.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 93.3 % 195 182
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 12 12
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 76.4 % 127 97

             Branch data     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                 :         401 : make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels)
      55                 :             : {
      56                 :         401 :         PlaceHolderVar *phv = makeNode(PlaceHolderVar);
      57                 :             : 
      58                 :         401 :         phv->phexpr = expr;
      59                 :         401 :         phv->phrels = phrels;
      60                 :         401 :         phv->phnullingrels = NULL;   /* caller may change this later */
      61                 :         401 :         phv->phid = ++(root->glob->lastPHId);
      62                 :         401 :         phv->phlevelsup = 0;         /* caller may change this later */
      63                 :             : 
      64                 :         802 :         return phv;
      65                 :         401 : }
      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                 :        1532 : find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv)
      84                 :             : {
      85                 :        1532 :         PlaceHolderInfo *phinfo;
      86                 :        1532 :         Relids          rels_used;
      87                 :             : 
      88                 :             :         /* if this ever isn't true, we'd need to be able to look in parent lists */
      89         [ +  - ]:        1532 :         Assert(phv->phlevelsup == 0);
      90                 :             : 
      91                 :             :         /* Use placeholder_array to look up existing PlaceHolderInfo quickly */
      92         [ +  + ]:        1532 :         if (phv->phid < root->placeholder_array_size)
      93                 :        1316 :                 phinfo = root->placeholder_array[phv->phid];
      94                 :             :         else
      95                 :         216 :                 phinfo = NULL;
      96         [ +  + ]:        1532 :         if (phinfo != NULL)
      97                 :             :         {
      98         [ +  - ]:        1195 :                 Assert(phinfo->phid == phv->phid);
      99                 :        1195 :                 return phinfo;
     100                 :             :         }
     101                 :             : 
     102                 :             :         /* Not found, so create it */
     103         [ +  - ]:         337 :         if (root->placeholdersFrozen)
     104   [ #  #  #  # ]:           0 :                 elog(ERROR, "too late to create a new PlaceHolderInfo");
     105                 :             : 
     106                 :         337 :         phinfo = makeNode(PlaceHolderInfo);
     107                 :             : 
     108                 :         337 :         phinfo->phid = phv->phid;
     109                 :         337 :         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                 :         337 :         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                 :         337 :         rels_used = pull_varnos(root, (Node *) phv->phexpr);
     127                 :         337 :         phinfo->ph_lateral = bms_difference(rels_used, phv->phrels);
     128                 :         337 :         phinfo->ph_eval_at = bms_int_members(rels_used, phv->phrels);
     129                 :             :         /* If no contained vars, force evaluation at syntactic location */
     130         [ +  + ]:         337 :         if (bms_is_empty(phinfo->ph_eval_at))
     131                 :             :         {
     132                 :         148 :                 phinfo->ph_eval_at = bms_copy(phv->phrels);
     133         [ +  - ]:         148 :                 Assert(!bms_is_empty(phinfo->ph_eval_at));
     134                 :         148 :         }
     135                 :         337 :         phinfo->ph_needed = NULL;    /* initially it's unused */
     136                 :             :         /* for the moment, estimate width using just the datatype info */
     137                 :         674 :         phinfo->ph_width = get_typavgwidth(exprType((Node *) phv->phexpr),
     138                 :         337 :                                                                            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                 :         337 :         root->placeholder_list = lappend(root->placeholder_list, phinfo);
     148                 :             : 
     149         [ +  + ]:         337 :         if (phinfo->phid >= root->placeholder_array_size)
     150                 :             :         {
     151                 :             :                 /* Must allocate or enlarge placeholder_array */
     152                 :         216 :                 int                     new_size;
     153                 :             : 
     154         [ -  + ]:         216 :                 new_size = root->placeholder_array_size ? root->placeholder_array_size * 2 : 8;
     155         [ -  + ]:         216 :                 while (phinfo->phid >= new_size)
     156                 :           0 :                         new_size *= 2;
     157         [ -  + ]:         216 :                 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                 :         216 :                         root->placeholder_array =
     162                 :         216 :                                 palloc0_array(PlaceHolderInfo *, new_size);
     163                 :         216 :                 root->placeholder_array_size = new_size;
     164                 :         216 :         }
     165                 :         337 :         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                 :         337 :         find_placeholders_in_expr(root, (Node *) phinfo->ph_var->phexpr);
     173                 :             : 
     174                 :         337 :         return phinfo;
     175                 :        1532 : }
     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                 :       33901 : find_placeholders_in_jointree(PlannerInfo *root)
     186                 :             : {
     187                 :             :         /* This must be done before freezing the set of PHIs */
     188         [ +  - ]:       33901 :         Assert(!root->placeholdersFrozen);
     189                 :             : 
     190                 :             :         /* We need do nothing if the query contains no PlaceHolderVars */
     191         [ +  + ]:       33901 :         if (root->glob->lastPHId != 0)
     192                 :             :         {
     193                 :             :                 /* Start recursion at top of jointree */
     194         [ +  - ]:         233 :                 Assert(root->parse->jointree != NULL &&
     195                 :             :                            IsA(root->parse->jointree, FromExpr));
     196                 :         233 :                 find_placeholders_recurse(root, (Node *) root->parse->jointree);
     197                 :         233 :         }
     198                 :       33901 : }
     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                 :        1176 : find_placeholders_recurse(PlannerInfo *root, Node *jtnode)
     208                 :             : {
     209         [ +  - ]:        1176 :         if (jtnode == NULL)
     210                 :           0 :                 return;
     211         [ +  + ]:        1176 :         if (IsA(jtnode, RangeTblRef))
     212                 :             :         {
     213                 :             :                 /* No quals to deal with here */
     214                 :         589 :         }
     215         [ +  + ]:         587 :         else if (IsA(jtnode, FromExpr))
     216                 :             :         {
     217                 :         277 :                 FromExpr   *f = (FromExpr *) jtnode;
     218                 :         277 :                 ListCell   *l;
     219                 :             : 
     220                 :             :                 /*
     221                 :             :                  * First, recurse to handle child joins.
     222                 :             :                  */
     223   [ +  -  +  +  :         600 :                 foreach(l, f->fromlist)
                   +  + ]
     224                 :             :                 {
     225                 :         323 :                         find_placeholders_recurse(root, lfirst(l));
     226                 :         323 :                 }
     227                 :             : 
     228                 :             :                 /*
     229                 :             :                  * Now process the top-level quals.
     230                 :             :                  */
     231                 :         277 :                 find_placeholders_in_expr(root, f->quals);
     232                 :         277 :         }
     233         [ +  - ]:         310 :         else if (IsA(jtnode, JoinExpr))
     234                 :             :         {
     235                 :         310 :                 JoinExpr   *j = (JoinExpr *) jtnode;
     236                 :             : 
     237                 :             :                 /*
     238                 :             :                  * First, recurse to handle child joins.
     239                 :             :                  */
     240                 :         310 :                 find_placeholders_recurse(root, j->larg);
     241                 :         310 :                 find_placeholders_recurse(root, j->rarg);
     242                 :             : 
     243                 :             :                 /* Process the qual clauses */
     244                 :         310 :                 find_placeholders_in_expr(root, j->quals);
     245                 :         310 :         }
     246                 :             :         else
     247   [ #  #  #  # ]:           0 :                 elog(ERROR, "unrecognized node type: %d",
     248                 :             :                          (int) nodeTag(jtnode));
     249                 :        1176 : }
     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                 :         924 : find_placeholders_in_expr(PlannerInfo *root, Node *expr)
     258                 :             : {
     259                 :         924 :         List       *vars;
     260                 :         924 :         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                 :         924 :         vars = pull_var_clause(expr,
     267                 :             :                                                    PVC_RECURSE_AGGREGATES |
     268                 :             :                                                    PVC_RECURSE_WINDOWFUNCS |
     269                 :             :                                                    PVC_INCLUDE_PLACEHOLDERS);
     270   [ +  +  +  +  :        1916 :         foreach(vl, vars)
                   +  + ]
     271                 :             :         {
     272                 :         992 :                 PlaceHolderVar *phv = (PlaceHolderVar *) lfirst(vl);
     273                 :             : 
     274                 :             :                 /* Ignore any plain Vars */
     275         [ +  + ]:         992 :                 if (!IsA(phv, PlaceHolderVar))
     276                 :         861 :                         continue;
     277                 :             : 
     278                 :             :                 /* Create a PlaceHolderInfo entry if there's not one already */
     279                 :         131 :                 (void) find_placeholder_info(root, phv);
     280      [ -  +  + ]:         992 :         }
     281                 :         924 :         list_free(vars);
     282                 :         924 : }
     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                 :       33901 : fix_placeholder_input_needed_levels(PlannerInfo *root)
     301                 :             : {
     302                 :       33901 :         ListCell   *lc;
     303                 :             : 
     304   [ +  +  +  +  :       34238 :         foreach(lc, root->placeholder_list)
                   +  + ]
     305                 :             :         {
     306                 :         337 :                 PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     307                 :         337 :                 List       *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
     308                 :             :                                                                                    PVC_RECURSE_AGGREGATES |
     309                 :             :                                                                                    PVC_RECURSE_WINDOWFUNCS |
     310                 :             :                                                                                    PVC_INCLUDE_PLACEHOLDERS);
     311                 :             : 
     312                 :         337 :                 add_vars_to_targetlist(root, vars, phinfo->ph_eval_at);
     313                 :         337 :                 list_free(vars);
     314                 :         337 :         }
     315                 :       33901 : }
     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                 :        1193 : rebuild_placeholder_attr_needed(PlannerInfo *root)
     328                 :             : {
     329                 :        1193 :         ListCell   *lc;
     330                 :             : 
     331   [ +  +  +  +  :        1212 :         foreach(lc, root->placeholder_list)
                   +  + ]
     332                 :             :         {
     333                 :          19 :                 PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     334                 :          19 :                 List       *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
     335                 :             :                                                                                    PVC_RECURSE_AGGREGATES |
     336                 :             :                                                                                    PVC_RECURSE_WINDOWFUNCS |
     337                 :             :                                                                                    PVC_INCLUDE_PLACEHOLDERS);
     338                 :             : 
     339                 :          19 :                 add_vars_to_attr_needed(root, vars, phinfo->ph_eval_at);
     340                 :          19 :                 list_free(vars);
     341                 :          19 :         }
     342                 :        1193 : }
     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                 :       33901 : add_placeholders_to_base_rels(PlannerInfo *root)
     357                 :             : {
     358                 :       33901 :         ListCell   *lc;
     359                 :             : 
     360   [ +  +  +  +  :       34237 :         foreach(lc, root->placeholder_list)
                   +  + ]
     361                 :             :         {
     362                 :         336 :                 PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     363                 :         336 :                 Relids          eval_at = phinfo->ph_eval_at;
     364                 :         336 :                 int                     varno;
     365                 :             : 
     366   [ +  +  +  + ]:         336 :                 if (bms_get_singleton_member(eval_at, &varno) &&
     367                 :         283 :                         bms_nonempty_difference(phinfo->ph_needed, eval_at))
     368                 :             :                 {
     369                 :         270 :                         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         [ -  + ]:         270 :                         Assert(phinfo->ph_var->phnullingrels == NULL);
     377                 :             : 
     378                 :             :                         /* Copying the PHV might be unnecessary here, but be safe */
     379                 :         540 :                         rel->reltarget->exprs = lappend(rel->reltarget->exprs,
     380                 :         270 :                                                                                         copyObject(phinfo->ph_var));
     381                 :             :                         /* reltarget's cost and width fields will be updated later */
     382                 :         270 :                 }
     383                 :         336 :         }
     384                 :       33901 : }
     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                 :       18791 : add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
     401                 :             :                                                         RelOptInfo *outer_rel, RelOptInfo *inner_rel,
     402                 :             :                                                         SpecialJoinInfo *sjinfo)
     403                 :             : {
     404                 :       18791 :         Relids          relids = joinrel->relids;
     405                 :       18791 :         int64           tuple_width = joinrel->reltarget->width;
     406                 :       18791 :         ListCell   *lc;
     407                 :             : 
     408   [ +  +  +  +  :       19487 :         foreach(lc, root->placeholder_list)
                   +  + ]
     409                 :             :         {
     410                 :         696 :                 PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
     411                 :             : 
     412                 :             :                 /* Is it computable here? */
     413         [ +  + ]:         696 :                 if (bms_is_subset(phinfo->ph_eval_at, relids))
     414                 :             :                 {
     415                 :             :                         /* Is it still needed above this joinrel? */
     416         [ +  + ]:         470 :                         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   [ +  +  +  + ]:         321 :                                 if (!bms_is_subset(phinfo->ph_eval_at, outer_rel->relids) &&
     432                 :         235 :                                         !bms_is_subset(phinfo->ph_eval_at, inner_rel->relids))
     433                 :             :                                 {
     434                 :             :                                         /* Copying might be unnecessary here, but be safe */
     435                 :          73 :                                         PlaceHolderVar *phv = copyObject(phinfo->ph_var);
     436                 :          73 :                                         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         [ -  + ]:          73 :                                         Assert(phv->phnullingrels == NULL);
     444                 :             : 
     445                 :         146 :                                         joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs,
     446                 :          73 :                                                                                                                 phv);
     447                 :          73 :                                         cost_qual_eval_node(&cost, (Node *) phv->phexpr, root);
     448                 :          73 :                                         joinrel->reltarget->cost.startup += cost.startup;
     449                 :          73 :                                         joinrel->reltarget->cost.per_tuple += cost.per_tuple;
     450                 :          73 :                                         tuple_width += phinfo->ph_width;
     451                 :          73 :                                 }
     452                 :         321 :                         }
     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                 :         470 :                         joinrel->direct_lateral_relids =
     470                 :         940 :                                 bms_add_members(joinrel->direct_lateral_relids,
     471                 :         470 :                                                                 phinfo->ph_lateral);
     472                 :         470 :                 }
     473                 :         696 :         }
     474                 :             : 
     475                 :       18791 :         joinrel->reltarget->width = clamp_width_est(tuple_width);
     476                 :       18791 : }
     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                 :        1289 : contain_placeholder_references_to(PlannerInfo *root, Node *clause,
     492                 :             :                                                                   int relid)
     493                 :             : {
     494                 :        1289 :         contain_placeholder_references_context context;
     495                 :             : 
     496                 :             :         /* We can answer quickly in the common case that there's no PHVs at all */
     497         [ +  + ]:        1289 :         if (root->glob->lastPHId == 0)
     498                 :        1187 :                 return false;
     499                 :             :         /* Else run the recursive search */
     500                 :         102 :         context.relid = relid;
     501                 :         102 :         context.sublevels_up = 0;
     502                 :         102 :         return contain_placeholder_references_walker(clause, &context);
     503                 :        1289 : }
     504                 :             : 
     505                 :             : static bool
     506                 :         344 : contain_placeholder_references_walker(Node *node,
     507                 :             :                                                                           contain_placeholder_references_context *context)
     508                 :             : {
     509         [ +  + ]:         344 :         if (node == NULL)
     510                 :          21 :                 return false;
     511         [ +  + ]:         323 :         if (IsA(node, PlaceHolderVar))
     512                 :             :         {
     513                 :           9 :                 PlaceHolderVar *phv = (PlaceHolderVar *) node;
     514                 :             : 
     515                 :             :                 /* We should just look through PHVs of other query levels */
     516         [ +  - ]:           9 :                 if (phv->phlevelsup == context->sublevels_up)
     517                 :             :                 {
     518                 :             :                         /* If phrels matches, we found what we came for */
     519         [ +  + ]:           9 :                         if (bms_is_member(context->relid, phv->phrels))
     520                 :           2 :                                 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                 :           7 :                         return false;
     530                 :             :                 }
     531      [ -  +  - ]:           9 :         }
     532         [ -  + ]:         314 :         else if (IsA(node, Query))
     533                 :             :         {
     534                 :             :                 /* Recurse into RTE subquery or not-yet-planned sublink subquery */
     535                 :           0 :                 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                 :           0 :         }
     545                 :         314 :         return expression_tree_walker(node, contain_placeholder_references_walker,
     546                 :             :                                                                   context);
     547                 :         344 : }
     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                 :          42 : get_placeholder_nulling_relids(PlannerInfo *root, PlaceHolderInfo *phinfo)
     560                 :             : {
     561                 :          42 :         Relids          result = NULL;
     562                 :          42 :         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         [ +  + ]:          96 :         while ((relid = bms_next_member(phinfo->ph_eval_at, relid)) > 0)
     569                 :             :         {
     570                 :          54 :                 RelOptInfo *rel = root->simple_rel_array[relid];
     571                 :             : 
     572                 :             :                 /* ignore the RTE_GROUP RTE */
     573         [ -  + ]:          54 :                 if (relid == root->group_rtindex)
     574                 :           0 :                         continue;
     575                 :             : 
     576         [ +  + ]:          54 :                 if (rel == NULL)                /* must be an outer join */
     577                 :             :                 {
     578         [ -  + ]:           2 :                         Assert(bms_is_member(relid, root->outer_join_rels));
     579                 :           2 :                         continue;
     580                 :             :                 }
     581                 :          52 :                 result = bms_add_members(result, rel->nulling_relids);
     582      [ -  +  + ]:          54 :         }
     583                 :             : 
     584                 :             :         /* Now remove any OJs already included in ph_eval_at, and we're done. */
     585                 :          42 :         result = bms_del_members(result, phinfo->ph_eval_at);
     586                 :          84 :         return result;
     587                 :          42 : }
        

Generated by: LCOV version 2.3.2-1