LCOV - code coverage report
Current view: top level - src/backend/optimizer/util - appendinfo.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 94.7 % 338 320
Test Date: 2026-03-22 07:16:17 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * appendinfo.c
       4              :  *    Routines for mapping between append parent(s) and children
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *    src/backend/optimizer/util/appendinfo.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : #include "postgres.h"
      16              : 
      17              : #include "access/htup_details.h"
      18              : #include "access/sysattr.h"
      19              : #include "access/table.h"
      20              : #include "foreign/fdwapi.h"
      21              : #include "nodes/makefuncs.h"
      22              : #include "nodes/nodeFuncs.h"
      23              : #include "optimizer/appendinfo.h"
      24              : #include "optimizer/pathnode.h"
      25              : #include "optimizer/planmain.h"
      26              : #include "parser/parsetree.h"
      27              : #include "utils/lsyscache.h"
      28              : #include "utils/rel.h"
      29              : #include "utils/syscache.h"
      30              : 
      31              : 
      32              : typedef struct
      33              : {
      34              :     PlannerInfo *root;
      35              :     int         nappinfos;
      36              :     AppendRelInfo **appinfos;
      37              : } adjust_appendrel_attrs_context;
      38              : 
      39              : static void make_inh_translation_list(Relation oldrelation,
      40              :                                       Relation newrelation,
      41              :                                       Index newvarno,
      42              :                                       AppendRelInfo *appinfo);
      43              : static Node *adjust_appendrel_attrs_mutator(Node *node,
      44              :                                             adjust_appendrel_attrs_context *context);
      45              : 
      46              : 
      47              : /*
      48              :  * make_append_rel_info
      49              :  *    Build an AppendRelInfo for the parent-child pair
      50              :  */
      51              : AppendRelInfo *
      52        34926 : make_append_rel_info(Relation parentrel, Relation childrel,
      53              :                      Index parentRTindex, Index childRTindex)
      54              : {
      55        34926 :     AppendRelInfo *appinfo = makeNode(AppendRelInfo);
      56              : 
      57        34926 :     appinfo->parent_relid = parentRTindex;
      58        34926 :     appinfo->child_relid = childRTindex;
      59        34926 :     appinfo->parent_reltype = parentrel->rd_rel->reltype;
      60        34926 :     appinfo->child_reltype = childrel->rd_rel->reltype;
      61        34926 :     make_inh_translation_list(parentrel, childrel, childRTindex, appinfo);
      62        34925 :     appinfo->parent_reloid = RelationGetRelid(parentrel);
      63              : 
      64        34925 :     return appinfo;
      65              : }
      66              : 
      67              : /*
      68              :  * make_inh_translation_list
      69              :  *    Build the list of translations from parent Vars to child Vars for
      70              :  *    an inheritance child, as well as a reverse-translation array.
      71              :  *
      72              :  * The reverse-translation array has an entry for each child relation
      73              :  * column, which is either the 1-based index of the corresponding parent
      74              :  * column, or 0 if there's no match (that happens for dropped child columns,
      75              :  * as well as child columns beyond those of the parent, which are allowed in
      76              :  * traditional inheritance though not partitioning).
      77              :  *
      78              :  * For paranoia's sake, we match type/collation as well as attribute name.
      79              :  */
      80              : static void
      81        34926 : make_inh_translation_list(Relation oldrelation, Relation newrelation,
      82              :                           Index newvarno,
      83              :                           AppendRelInfo *appinfo)
      84              : {
      85        34926 :     List       *vars = NIL;
      86              :     AttrNumber *pcolnos;
      87        34926 :     TupleDesc   old_tupdesc = RelationGetDescr(oldrelation);
      88        34926 :     TupleDesc   new_tupdesc = RelationGetDescr(newrelation);
      89        34926 :     Oid         new_relid = RelationGetRelid(newrelation);
      90        34926 :     int         oldnatts = old_tupdesc->natts;
      91        34926 :     int         newnatts = new_tupdesc->natts;
      92              :     int         old_attno;
      93        34926 :     int         new_attno = 0;
      94              : 
      95              :     /* Initialize reverse-translation array with all entries zero */
      96        34926 :     appinfo->num_child_cols = newnatts;
      97        34926 :     appinfo->parent_colnos = pcolnos =
      98        34926 :         (AttrNumber *) palloc0(newnatts * sizeof(AttrNumber));
      99              : 
     100       129045 :     for (old_attno = 0; old_attno < oldnatts; old_attno++)
     101              :     {
     102              :         Form_pg_attribute att;
     103              :         char       *attname;
     104              :         Oid         atttypid;
     105              :         int32       atttypmod;
     106              :         Oid         attcollation;
     107              : 
     108        94120 :         att = TupleDescAttr(old_tupdesc, old_attno);
     109        94120 :         if (att->attisdropped)
     110              :         {
     111              :             /* Just put NULL into this list entry */
     112         2643 :             vars = lappend(vars, NULL);
     113         2643 :             continue;
     114              :         }
     115        91477 :         attname = NameStr(att->attname);
     116        91477 :         atttypid = att->atttypid;
     117        91477 :         atttypmod = att->atttypmod;
     118        91477 :         attcollation = att->attcollation;
     119              : 
     120              :         /*
     121              :          * When we are generating the "translation list" for the parent table
     122              :          * of an inheritance set, no need to search for matches.
     123              :          */
     124        91477 :         if (oldrelation == newrelation)
     125              :         {
     126         5789 :             vars = lappend(vars, makeVar(newvarno,
     127         5789 :                                          (AttrNumber) (old_attno + 1),
     128              :                                          atttypid,
     129              :                                          atttypmod,
     130              :                                          attcollation,
     131              :                                          0));
     132         5789 :             pcolnos[old_attno] = old_attno + 1;
     133         5789 :             continue;
     134              :         }
     135              : 
     136              :         /*
     137              :          * Otherwise we have to search for the matching column by name.
     138              :          * There's no guarantee it'll have the same column position, because
     139              :          * of cases like ALTER TABLE ADD COLUMN and multiple inheritance.
     140              :          * However, in simple cases, the relative order of columns is mostly
     141              :          * the same in both relations, so try the column of newrelation that
     142              :          * follows immediately after the one that we just found, and if that
     143              :          * fails, let syscache handle it.
     144              :          */
     145        85688 :         if (new_attno >= newnatts ||
     146        83852 :             (att = TupleDescAttr(new_tupdesc, new_attno))->attisdropped ||
     147        83072 :             strcmp(attname, NameStr(att->attname)) != 0)
     148              :         {
     149              :             HeapTuple   newtup;
     150              : 
     151         6887 :             newtup = SearchSysCacheAttName(new_relid, attname);
     152         6887 :             if (!HeapTupleIsValid(newtup))
     153            0 :                 elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
     154              :                      attname, RelationGetRelationName(newrelation));
     155         6887 :             new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;
     156              :             Assert(new_attno >= 0 && new_attno < newnatts);
     157         6887 :             ReleaseSysCache(newtup);
     158              : 
     159         6887 :             att = TupleDescAttr(new_tupdesc, new_attno);
     160              :         }
     161              : 
     162              :         /* Found it, check type and collation match */
     163        85688 :         if (atttypid != att->atttypid || atttypmod != att->atttypmod)
     164            1 :             ereport(ERROR,
     165              :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
     166              :                      errmsg("attribute \"%s\" of relation \"%s\" does not match parent's type",
     167              :                             attname, RelationGetRelationName(newrelation))));
     168        85687 :         if (attcollation != att->attcollation)
     169            0 :             ereport(ERROR,
     170              :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
     171              :                      errmsg("attribute \"%s\" of relation \"%s\" does not match parent's collation",
     172              :                             attname, RelationGetRelationName(newrelation))));
     173              : 
     174        85687 :         vars = lappend(vars, makeVar(newvarno,
     175        85687 :                                      (AttrNumber) (new_attno + 1),
     176              :                                      atttypid,
     177              :                                      atttypmod,
     178              :                                      attcollation,
     179              :                                      0));
     180        85687 :         pcolnos[new_attno] = old_attno + 1;
     181        85687 :         new_attno++;
     182              :     }
     183              : 
     184        34925 :     appinfo->translated_vars = vars;
     185        34925 : }
     186              : 
     187              : /*
     188              :  * adjust_appendrel_attrs
     189              :  *    Copy the specified query or expression and translate Vars referring to a
     190              :  *    parent rel to refer to the corresponding child rel instead.  We also
     191              :  *    update rtindexes appearing outside Vars, such as resultRelation and
     192              :  *    jointree relids.
     193              :  *
     194              :  * Note: this is only applied after conversion of sublinks to subplans,
     195              :  * so we don't need to cope with recursion into sub-queries.
     196              :  *
     197              :  * Note: this is not hugely different from what pullup_replace_vars() does;
     198              :  * maybe we should try to fold the two routines together.
     199              :  */
     200              : Node *
     201       243847 : adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos,
     202              :                        AppendRelInfo **appinfos)
     203              : {
     204              :     adjust_appendrel_attrs_context context;
     205              : 
     206       243847 :     context.root = root;
     207       243847 :     context.nappinfos = nappinfos;
     208       243847 :     context.appinfos = appinfos;
     209              : 
     210              :     /* If there's nothing to adjust, don't call this function. */
     211              :     Assert(nappinfos >= 1 && appinfos != NULL);
     212              : 
     213              :     /* Should never be translating a Query tree. */
     214              :     Assert(node == NULL || !IsA(node, Query));
     215              : 
     216       243847 :     return adjust_appendrel_attrs_mutator(node, &context);
     217              : }
     218              : 
     219              : static Node *
     220      1274205 : adjust_appendrel_attrs_mutator(Node *node,
     221              :                                adjust_appendrel_attrs_context *context)
     222              : {
     223      1274205 :     AppendRelInfo **appinfos = context->appinfos;
     224      1274205 :     int         nappinfos = context->nappinfos;
     225              :     int         cnt;
     226              : 
     227      1274205 :     if (node == NULL)
     228       236891 :         return NULL;
     229      1037314 :     if (IsA(node, Var))
     230              :     {
     231       464973 :         Var        *var = (Var *) copyObject(node);
     232       464973 :         AppendRelInfo *appinfo = NULL;
     233              : 
     234       464973 :         if (var->varlevelsup != 0)
     235            0 :             return (Node *) var;    /* no changes needed */
     236              : 
     237              :         /*
     238              :          * You might think we need to adjust var->varnullingrels, but that
     239              :          * shouldn't need any changes.  It will contain outer-join relids,
     240              :          * while the transformation we are making affects only baserels.
     241              :          * Below, we just merge var->varnullingrels into the translated Var.
     242              :          * (We must merge not just copy: the child Var could have some
     243              :          * nullingrel bits set already, and we mustn't drop those.)
     244              :          *
     245              :          * If var->varnullingrels isn't empty, and the translation wouldn't be
     246              :          * a Var, we have to fail.  One could imagine wrapping the translated
     247              :          * expression in a PlaceHolderVar, but that won't work because this is
     248              :          * typically used after freezing placeholders.  Fortunately, the case
     249              :          * appears unreachable at the moment.  We can see nonempty
     250              :          * var->varnullingrels here, but only in cases involving partitionwise
     251              :          * joining, and in such cases the translations will always be Vars.
     252              :          * (Non-Var translations occur only for appendrels made by flattening
     253              :          * UNION ALL subqueries.)  Should we need to make this work in future,
     254              :          * a possible fix is to mandate that prepjointree.c create PHVs for
     255              :          * all non-Var outputs of such subqueries, and then we could look up
     256              :          * the pre-existing PHV here.  Or perhaps just wrap the translations
     257              :          * that way to begin with?
     258              :          *
     259              :          * If var->varreturningtype is not VAR_RETURNING_DEFAULT, then that
     260              :          * also needs to be copied to the translated Var.  That too would fail
     261              :          * if the translation wasn't a Var, but that should never happen since
     262              :          * a non-default var->varreturningtype is only used for Vars referring
     263              :          * to the result relation, which should never be a flattened UNION ALL
     264              :          * subquery.
     265              :          */
     266              : 
     267       580867 :         for (cnt = 0; cnt < nappinfos; cnt++)
     268              :         {
     269       546951 :             if (var->varno == appinfos[cnt]->parent_relid)
     270              :             {
     271       431057 :                 appinfo = appinfos[cnt];
     272       431057 :                 break;
     273              :             }
     274              :         }
     275              : 
     276       464973 :         if (appinfo)
     277              :         {
     278       431057 :             var->varno = appinfo->child_relid;
     279              :             /* it's now a generated Var, so drop any syntactic labeling */
     280       431057 :             var->varnosyn = 0;
     281       431057 :             var->varattnosyn = 0;
     282       431057 :             if (var->varattno > 0)
     283              :             {
     284              :                 Node       *newnode;
     285              : 
     286       414248 :                 if (var->varattno > list_length(appinfo->translated_vars))
     287            0 :                     elog(ERROR, "attribute %d of relation \"%s\" does not exist",
     288              :                          var->varattno, get_rel_name(appinfo->parent_reloid));
     289       414248 :                 newnode = copyObject(list_nth(appinfo->translated_vars,
     290              :                                               var->varattno - 1));
     291       414248 :                 if (newnode == NULL)
     292            0 :                     elog(ERROR, "attribute %d of relation \"%s\" does not exist",
     293              :                          var->varattno, get_rel_name(appinfo->parent_reloid));
     294       414248 :                 if (IsA(newnode, Var))
     295              :                 {
     296       406492 :                     Var        *newvar = (Var *) newnode;
     297              : 
     298       406492 :                     newvar->varreturningtype = var->varreturningtype;
     299       406492 :                     newvar->varnullingrels = bms_add_members(newvar->varnullingrels,
     300       406492 :                                                              var->varnullingrels);
     301              :                 }
     302              :                 else
     303              :                 {
     304         7756 :                     if (var->varreturningtype != VAR_RETURNING_DEFAULT)
     305            0 :                         elog(ERROR, "failed to apply returningtype to a non-Var");
     306         7756 :                     if (var->varnullingrels != NULL)
     307            0 :                         elog(ERROR, "failed to apply nullingrels to a non-Var");
     308              :                 }
     309       414248 :                 return newnode;
     310              :             }
     311        16809 :             else if (var->varattno == 0)
     312              :             {
     313              :                 /*
     314              :                  * Whole-row Var: if we are dealing with named rowtypes, we
     315              :                  * can use a whole-row Var for the child table plus a coercion
     316              :                  * step to convert the tuple layout to the parent's rowtype.
     317              :                  * Otherwise we have to generate a RowExpr.
     318              :                  */
     319          845 :                 if (OidIsValid(appinfo->child_reltype))
     320              :                 {
     321              :                     Assert(var->vartype == appinfo->parent_reltype);
     322          801 :                     if (appinfo->parent_reltype != appinfo->child_reltype)
     323              :                     {
     324          655 :                         ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
     325              : 
     326          655 :                         r->arg = (Expr *) var;
     327          655 :                         r->resulttype = appinfo->parent_reltype;
     328          655 :                         r->convertformat = COERCE_IMPLICIT_CAST;
     329          655 :                         r->location = -1;
     330              :                         /* Make sure the Var node has the right type ID, too */
     331          655 :                         var->vartype = appinfo->child_reltype;
     332          655 :                         return (Node *) r;
     333              :                     }
     334              :                 }
     335              :                 else
     336              :                 {
     337              :                     /*
     338              :                      * Build a RowExpr containing the translated variables.
     339              :                      *
     340              :                      * In practice var->vartype will always be RECORDOID here,
     341              :                      * so we need to come up with some suitable column names.
     342              :                      * We use the parent RTE's column names.
     343              :                      *
     344              :                      * Note: we can't get here for inheritance cases, so there
     345              :                      * is no need to worry that translated_vars might contain
     346              :                      * some dummy NULLs.
     347              :                      */
     348              :                     RowExpr    *rowexpr;
     349              :                     List       *fields;
     350              :                     RangeTblEntry *rte;
     351              : 
     352           44 :                     rte = rt_fetch(appinfo->parent_relid,
     353              :                                    context->root->parse->rtable);
     354           44 :                     fields = copyObject(appinfo->translated_vars);
     355           44 :                     rowexpr = makeNode(RowExpr);
     356           44 :                     rowexpr->args = fields;
     357           44 :                     rowexpr->row_typeid = var->vartype;
     358           44 :                     rowexpr->row_format = COERCE_IMPLICIT_CAST;
     359           44 :                     rowexpr->colnames = copyObject(rte->eref->colnames);
     360           44 :                     rowexpr->location = -1;
     361              : 
     362           44 :                     if (var->varreturningtype != VAR_RETURNING_DEFAULT)
     363            0 :                         elog(ERROR, "failed to apply returningtype to a non-Var");
     364           44 :                     if (var->varnullingrels != NULL)
     365            0 :                         elog(ERROR, "failed to apply nullingrels to a non-Var");
     366              : 
     367           44 :                     return (Node *) rowexpr;
     368              :                 }
     369              :             }
     370              :             /* system attributes don't need any other translation */
     371              :         }
     372        33916 :         else if (var->varno == ROWID_VAR)
     373              :         {
     374              :             /*
     375              :              * If it's a ROWID_VAR placeholder, see if we've reached a leaf
     376              :              * target rel, for which we can translate the Var to a specific
     377              :              * instantiation.  We should never be asked to translate to a set
     378              :              * of relids containing more than one leaf target rel, so the
     379              :              * answer will be unique.  If we're still considering non-leaf
     380              :              * inheritance levels, return the ROWID_VAR Var as-is.
     381              :              */
     382        15187 :             Relids      leaf_result_relids = context->root->leaf_result_relids;
     383        15187 :             Index       leaf_relid = 0;
     384              : 
     385        30374 :             for (cnt = 0; cnt < nappinfos; cnt++)
     386              :             {
     387        15187 :                 if (bms_is_member(appinfos[cnt]->child_relid,
     388              :                                   leaf_result_relids))
     389              :                 {
     390        13321 :                     if (leaf_relid)
     391            0 :                         elog(ERROR, "cannot translate to multiple leaf relids");
     392        13321 :                     leaf_relid = appinfos[cnt]->child_relid;
     393              :                 }
     394              :             }
     395              : 
     396        15187 :             if (leaf_relid)
     397              :             {
     398              :                 RowIdentityVarInfo *ridinfo = (RowIdentityVarInfo *)
     399        13321 :                     list_nth(context->root->row_identity_vars, var->varattno - 1);
     400              : 
     401        13321 :                 if (bms_is_member(leaf_relid, ridinfo->rowidrels))
     402              :                 {
     403              :                     /* Substitute the Var given in the RowIdentityVarInfo */
     404        13275 :                     var = copyObject(ridinfo->rowidvar);
     405              :                     /* ... but use the correct relid */
     406        13275 :                     var->varno = leaf_relid;
     407              :                     /* identity vars shouldn't have nulling rels */
     408              :                     Assert(var->varnullingrels == NULL);
     409              :                     /* varnosyn in the RowIdentityVarInfo is probably wrong */
     410        13275 :                     var->varnosyn = 0;
     411        13275 :                     var->varattnosyn = 0;
     412              :                 }
     413              :                 else
     414              :                 {
     415              :                     /*
     416              :                      * This leaf rel can't return the desired value, so
     417              :                      * substitute a NULL of the correct type.
     418              :                      */
     419           46 :                     return (Node *) makeNullConst(var->vartype,
     420              :                                                   var->vartypmod,
     421              :                                                   var->varcollid);
     422              :                 }
     423              :             }
     424              :         }
     425        49980 :         return (Node *) var;
     426              :     }
     427       572341 :     if (IsA(node, CurrentOfExpr))
     428              :     {
     429          152 :         CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
     430              : 
     431          152 :         for (cnt = 0; cnt < nappinfos; cnt++)
     432              :         {
     433          152 :             AppendRelInfo *appinfo = appinfos[cnt];
     434              : 
     435          152 :             if (cexpr->cvarno == appinfo->parent_relid)
     436              :             {
     437          152 :                 cexpr->cvarno = appinfo->child_relid;
     438          152 :                 break;
     439              :             }
     440              :         }
     441          152 :         return (Node *) cexpr;
     442              :     }
     443       572189 :     if (IsA(node, PlaceHolderVar))
     444              :     {
     445              :         /* Copy the PlaceHolderVar node with correct mutation of subnodes */
     446              :         PlaceHolderVar *phv;
     447              : 
     448         2302 :         phv = (PlaceHolderVar *) expression_tree_mutator(node,
     449              :                                                          adjust_appendrel_attrs_mutator,
     450              :                                                          context);
     451              :         /* now fix PlaceHolderVar's relid sets */
     452         2302 :         if (phv->phlevelsup == 0)
     453              :         {
     454         2302 :             phv->phrels = adjust_child_relids(phv->phrels,
     455              :                                               nappinfos, appinfos);
     456              :             /* as above, we needn't touch phnullingrels */
     457              :         }
     458         2302 :         return (Node *) phv;
     459              :     }
     460              :     /* Shouldn't need to handle planner auxiliary nodes here */
     461              :     Assert(!IsA(node, SpecialJoinInfo));
     462              :     Assert(!IsA(node, AppendRelInfo));
     463              :     Assert(!IsA(node, PlaceHolderInfo));
     464              :     Assert(!IsA(node, MinMaxAggInfo));
     465              : 
     466              :     /*
     467              :      * We have to process RestrictInfo nodes specially.  (Note: although
     468              :      * set_append_rel_pathlist will hide RestrictInfos in the parent's
     469              :      * baserestrictinfo list from us, it doesn't hide those in joininfo.)
     470              :      */
     471       569887 :     if (IsA(node, RestrictInfo))
     472              :     {
     473        33437 :         RestrictInfo *oldinfo = (RestrictInfo *) node;
     474        33437 :         RestrictInfo *newinfo = makeNode(RestrictInfo);
     475              : 
     476              :         /* Copy all flat-copiable fields, notably including rinfo_serial */
     477        33437 :         memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
     478              : 
     479              :         /* Recursively fix the clause itself */
     480        33437 :         newinfo->clause = (Expr *)
     481        33437 :             adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);
     482              : 
     483              :         /* and the modified version, if an OR clause */
     484        33437 :         newinfo->orclause = (Expr *)
     485        33437 :             adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);
     486              : 
     487              :         /* adjust relid sets too */
     488        33437 :         newinfo->clause_relids = adjust_child_relids(oldinfo->clause_relids,
     489              :                                                      context->nappinfos,
     490              :                                                      context->appinfos);
     491        33437 :         newinfo->required_relids = adjust_child_relids(oldinfo->required_relids,
     492              :                                                        context->nappinfos,
     493              :                                                        context->appinfos);
     494        33437 :         newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids,
     495              :                                                     context->nappinfos,
     496              :                                                     context->appinfos);
     497        33437 :         newinfo->left_relids = adjust_child_relids(oldinfo->left_relids,
     498              :                                                    context->nappinfos,
     499              :                                                    context->appinfos);
     500        33437 :         newinfo->right_relids = adjust_child_relids(oldinfo->right_relids,
     501              :                                                     context->nappinfos,
     502              :                                                     context->appinfos);
     503              : 
     504              :         /*
     505              :          * Reset cached derivative fields, since these might need to have
     506              :          * different values when considering the child relation.  Note we
     507              :          * don't reset left_ec/right_ec: each child variable is implicitly
     508              :          * equivalent to its parent, so still a member of the same EC if any.
     509              :          */
     510        33437 :         newinfo->eval_cost.startup = -1;
     511        33437 :         newinfo->norm_selec = -1;
     512        33437 :         newinfo->outer_selec = -1;
     513        33437 :         newinfo->left_em = NULL;
     514        33437 :         newinfo->right_em = NULL;
     515        33437 :         newinfo->scansel_cache = NIL;
     516        33437 :         newinfo->left_bucketsize = -1;
     517        33437 :         newinfo->right_bucketsize = -1;
     518        33437 :         newinfo->left_mcvfreq = -1;
     519        33437 :         newinfo->right_mcvfreq = -1;
     520              : 
     521        33437 :         return (Node *) newinfo;
     522              :     }
     523              : 
     524              :     /*
     525              :      * We have to process RelAggInfo nodes specially.
     526              :      */
     527       536450 :     if (IsA(node, RelAggInfo))
     528              :     {
     529        15500 :         RelAggInfo *oldinfo = (RelAggInfo *) node;
     530        15500 :         RelAggInfo *newinfo = makeNode(RelAggInfo);
     531              : 
     532        15500 :         newinfo->target = (PathTarget *)
     533        15500 :             adjust_appendrel_attrs_mutator((Node *) oldinfo->target,
     534              :                                            context);
     535              : 
     536        15500 :         newinfo->agg_input = (PathTarget *)
     537        15500 :             adjust_appendrel_attrs_mutator((Node *) oldinfo->agg_input,
     538              :                                            context);
     539              : 
     540        15500 :         newinfo->group_clauses = oldinfo->group_clauses;
     541              : 
     542        15500 :         newinfo->group_exprs = (List *)
     543        15500 :             adjust_appendrel_attrs_mutator((Node *) oldinfo->group_exprs,
     544              :                                            context);
     545              : 
     546        15500 :         return (Node *) newinfo;
     547              :     }
     548              : 
     549              :     /*
     550              :      * We have to process PathTarget nodes specially.
     551              :      */
     552       520950 :     if (IsA(node, PathTarget))
     553              :     {
     554        31000 :         PathTarget *oldtarget = (PathTarget *) node;
     555        31000 :         PathTarget *newtarget = makeNode(PathTarget);
     556              : 
     557              :         /* Copy all flat-copiable fields */
     558        31000 :         memcpy(newtarget, oldtarget, sizeof(PathTarget));
     559              : 
     560        31000 :         newtarget->exprs = (List *)
     561        31000 :             adjust_appendrel_attrs_mutator((Node *) oldtarget->exprs,
     562              :                                            context);
     563              : 
     564        31000 :         if (oldtarget->sortgrouprefs)
     565              :         {
     566        31000 :             Size        nbytes = list_length(oldtarget->exprs) * sizeof(Index);
     567              : 
     568        31000 :             newtarget->sortgrouprefs = (Index *) palloc(nbytes);
     569        31000 :             memcpy(newtarget->sortgrouprefs, oldtarget->sortgrouprefs, nbytes);
     570              :         }
     571              : 
     572        31000 :         return (Node *) newtarget;
     573              :     }
     574              : 
     575              :     /*
     576              :      * NOTE: we do not need to recurse into sublinks, because they should
     577              :      * already have been converted to subplans before we see them.
     578              :      */
     579              :     Assert(!IsA(node, SubLink));
     580              :     Assert(!IsA(node, Query));
     581              :     /* We should never see these Query substructures, either. */
     582              :     Assert(!IsA(node, RangeTblRef));
     583              :     Assert(!IsA(node, JoinExpr));
     584              : 
     585       489950 :     return expression_tree_mutator(node, adjust_appendrel_attrs_mutator, context);
     586              : }
     587              : 
     588              : /*
     589              :  * adjust_appendrel_attrs_multilevel
     590              :  *    Apply Var translations from an appendrel parent down to a child.
     591              :  *
     592              :  * Replace Vars in the "node" expression that reference "parentrel" with
     593              :  * the appropriate Vars for "childrel".  childrel can be more than one
     594              :  * inheritance level removed from parentrel.
     595              :  */
     596              : Node *
     597        39590 : adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
     598              :                                   RelOptInfo *childrel,
     599              :                                   RelOptInfo *parentrel)
     600              : {
     601              :     AppendRelInfo **appinfos;
     602              :     int         nappinfos;
     603              : 
     604              :     /* Recurse if immediate parent is not the top parent. */
     605        39590 :     if (childrel->parent != parentrel)
     606              :     {
     607        13619 :         if (childrel->parent)
     608        13619 :             node = adjust_appendrel_attrs_multilevel(root, node,
     609        13619 :                                                      childrel->parent,
     610              :                                                      parentrel);
     611              :         else
     612            0 :             elog(ERROR, "childrel is not a child of parentrel");
     613              :     }
     614              : 
     615              :     /* Now translate for this child. */
     616        39590 :     appinfos = find_appinfos_by_relids(root, childrel->relids, &nappinfos);
     617              : 
     618        39590 :     node = adjust_appendrel_attrs(root, node, nappinfos, appinfos);
     619              : 
     620        39590 :     pfree(appinfos);
     621              : 
     622        39590 :     return node;
     623              : }
     624              : 
     625              : /*
     626              :  * Substitute child relids for parent relids in a Relid set.  The array of
     627              :  * appinfos specifies the substitutions to be performed.
     628              :  */
     629              : Relids
     630       196413 : adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)
     631              : {
     632       196413 :     Bitmapset  *result = NULL;
     633              :     int         cnt;
     634              : 
     635       532366 :     for (cnt = 0; cnt < nappinfos; cnt++)
     636              :     {
     637       335953 :         AppendRelInfo *appinfo = appinfos[cnt];
     638              : 
     639              :         /* Remove parent, add child */
     640       335953 :         if (bms_is_member(appinfo->parent_relid, relids))
     641              :         {
     642              :             /* Make a copy if we are changing the set. */
     643       214109 :             if (!result)
     644       153479 :                 result = bms_copy(relids);
     645              : 
     646       214109 :             result = bms_del_member(result, appinfo->parent_relid);
     647       214109 :             result = bms_add_member(result, appinfo->child_relid);
     648              :         }
     649              :     }
     650              : 
     651              :     /* If we made any changes, return the modified copy. */
     652       196413 :     if (result)
     653       153479 :         return result;
     654              : 
     655              :     /* Otherwise, return the original set without modification. */
     656        42934 :     return relids;
     657              : }
     658              : 
     659              : /*
     660              :  * Substitute child's relids for parent's relids in a Relid set.
     661              :  * The childrel can be multiple inheritance levels below the parent.
     662              :  */
     663              : Relids
     664          967 : adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
     665              :                                RelOptInfo *childrel,
     666              :                                RelOptInfo *parentrel)
     667              : {
     668              :     AppendRelInfo **appinfos;
     669              :     int         nappinfos;
     670              : 
     671              :     /*
     672              :      * If the given relids set doesn't contain any of the parent relids, it
     673              :      * will remain unchanged.
     674              :      */
     675          967 :     if (!bms_overlap(relids, parentrel->relids))
     676            0 :         return relids;
     677              : 
     678              :     /* Recurse if immediate parent is not the top parent. */
     679          967 :     if (childrel->parent != parentrel)
     680              :     {
     681          120 :         if (childrel->parent)
     682          120 :             relids = adjust_child_relids_multilevel(root, relids,
     683          120 :                                                     childrel->parent,
     684              :                                                     parentrel);
     685              :         else
     686            0 :             elog(ERROR, "childrel is not a child of parentrel");
     687              :     }
     688              : 
     689              :     /* Now translate for this child. */
     690          967 :     appinfos = find_appinfos_by_relids(root, childrel->relids, &nappinfos);
     691              : 
     692          967 :     relids = adjust_child_relids(relids, nappinfos, appinfos);
     693              : 
     694          967 :     pfree(appinfos);
     695              : 
     696          967 :     return relids;
     697              : }
     698              : 
     699              : /*
     700              :  * adjust_inherited_attnums
     701              :  *    Translate an integer list of attribute numbers from parent to child.
     702              :  */
     703              : List *
     704         4038 : adjust_inherited_attnums(List *attnums, AppendRelInfo *context)
     705              : {
     706         4038 :     List       *result = NIL;
     707              :     ListCell   *lc;
     708              : 
     709              :     /* This should only happen for an inheritance case, not UNION ALL */
     710              :     Assert(OidIsValid(context->parent_reloid));
     711              : 
     712              :     /* Look up each attribute in the AppendRelInfo's translated_vars list */
     713         9019 :     foreach(lc, attnums)
     714              :     {
     715         4981 :         AttrNumber  parentattno = lfirst_int(lc);
     716              :         Var        *childvar;
     717              : 
     718              :         /* Look up the translation of this column: it must be a Var */
     719         9962 :         if (parentattno <= 0 ||
     720         4981 :             parentattno > list_length(context->translated_vars))
     721            0 :             elog(ERROR, "attribute %d of relation \"%s\" does not exist",
     722              :                  parentattno, get_rel_name(context->parent_reloid));
     723         4981 :         childvar = (Var *) list_nth(context->translated_vars, parentattno - 1);
     724         4981 :         if (childvar == NULL || !IsA(childvar, Var))
     725            0 :             elog(ERROR, "attribute %d of relation \"%s\" does not exist",
     726              :                  parentattno, get_rel_name(context->parent_reloid));
     727              : 
     728         4981 :         result = lappend_int(result, childvar->varattno);
     729              :     }
     730         4038 :     return result;
     731              : }
     732              : 
     733              : /*
     734              :  * adjust_inherited_attnums_multilevel
     735              :  *    As above, but traverse multiple inheritance levels as needed.
     736              :  */
     737              : List *
     738         4038 : adjust_inherited_attnums_multilevel(PlannerInfo *root, List *attnums,
     739              :                                     Index child_relid, Index top_parent_relid)
     740              : {
     741         4038 :     AppendRelInfo *appinfo = root->append_rel_array[child_relid];
     742              : 
     743         4038 :     if (!appinfo)
     744            0 :         elog(ERROR, "child rel %d not found in append_rel_array", child_relid);
     745              : 
     746              :     /* Recurse if immediate parent is not the top parent. */
     747         4038 :     if (appinfo->parent_relid != top_parent_relid)
     748          677 :         attnums = adjust_inherited_attnums_multilevel(root, attnums,
     749              :                                                       appinfo->parent_relid,
     750              :                                                       top_parent_relid);
     751              : 
     752              :     /* Now translate for this child */
     753         4038 :     return adjust_inherited_attnums(attnums, appinfo);
     754              : }
     755              : 
     756              : /*
     757              :  * get_translated_update_targetlist
     758              :  *    Get the processed_tlist of an UPDATE query, translated as needed to
     759              :  *    match a child target relation.
     760              :  *
     761              :  * Optionally also return the list of target column numbers translated
     762              :  * to this target relation.  (The resnos in processed_tlist MUST NOT be
     763              :  * relied on for this purpose.)
     764              :  */
     765              : void
     766           50 : get_translated_update_targetlist(PlannerInfo *root, Index relid,
     767              :                                  List **processed_tlist, List **update_colnos)
     768              : {
     769              :     /* This is pretty meaningless for commands other than UPDATE. */
     770              :     Assert(root->parse->commandType == CMD_UPDATE);
     771           50 :     if (relid == root->parse->resultRelation)
     772              :     {
     773              :         /*
     774              :          * Non-inheritance case, so it's easy.  The caller might be expecting
     775              :          * a tree it can scribble on, though, so copy.
     776              :          */
     777           33 :         *processed_tlist = copyObject(root->processed_tlist);
     778           33 :         if (update_colnos)
     779           33 :             *update_colnos = copyObject(root->update_colnos);
     780              :     }
     781              :     else
     782              :     {
     783              :         Assert(bms_is_member(relid, root->all_result_relids));
     784           17 :         *processed_tlist = (List *)
     785           17 :             adjust_appendrel_attrs_multilevel(root,
     786           17 :                                               (Node *) root->processed_tlist,
     787              :                                               find_base_rel(root, relid),
     788           17 :                                               find_base_rel(root, root->parse->resultRelation));
     789           17 :         if (update_colnos)
     790           17 :             *update_colnos =
     791           17 :                 adjust_inherited_attnums_multilevel(root, root->update_colnos,
     792              :                                                     relid,
     793           17 :                                                     root->parse->resultRelation);
     794              :     }
     795           50 : }
     796              : 
     797              : /*
     798              :  * find_appinfos_by_relids
     799              :  *      Find AppendRelInfo structures for base relations listed in relids.
     800              :  *
     801              :  * The relids argument is typically a join relation's relids, which can
     802              :  * include outer-join RT indexes in addition to baserels.  We silently
     803              :  * ignore the outer joins.
     804              :  *
     805              :  * The AppendRelInfos are returned in an array, which can be pfree'd by the
     806              :  * caller. *nappinfos is set to the number of entries in the array.
     807              :  */
     808              : AppendRelInfo **
     809        83116 : find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
     810              : {
     811              :     AppendRelInfo **appinfos;
     812        83116 :     int         cnt = 0;
     813              :     int         i;
     814              : 
     815              :     /* Allocate an array that's certainly big enough */
     816        83116 :     appinfos = palloc_array(AppendRelInfo *, bms_num_members(relids));
     817              : 
     818        83116 :     i = -1;
     819       209417 :     while ((i = bms_next_member(relids, i)) >= 0)
     820              :     {
     821       126301 :         AppendRelInfo *appinfo = root->append_rel_array[i];
     822              : 
     823       126301 :         if (!appinfo)
     824              :         {
     825              :             /* Probably i is an OJ index, but let's check */
     826         2976 :             if (find_base_rel_ignore_join(root, i) == NULL)
     827         2976 :                 continue;
     828              :             /* It's a base rel, but we lack an append_rel_array entry */
     829            0 :             elog(ERROR, "child rel %d not found in append_rel_array", i);
     830              :         }
     831              : 
     832       123325 :         appinfos[cnt++] = appinfo;
     833              :     }
     834        83116 :     *nappinfos = cnt;
     835        83116 :     return appinfos;
     836              : }
     837              : 
     838              : 
     839              : /*****************************************************************************
     840              :  *
     841              :  *      ROW-IDENTITY VARIABLE MANAGEMENT
     842              :  *
     843              :  * This code lacks a good home, perhaps.  We choose to keep it here because
     844              :  * adjust_appendrel_attrs_mutator() is its principal co-conspirator.  That
     845              :  * function does most of what is needed to expand ROWID_VAR Vars into the
     846              :  * right things.
     847              :  *
     848              :  *****************************************************************************/
     849              : 
     850              : /*
     851              :  * add_row_identity_var
     852              :  *    Register a row-identity column to be used in UPDATE/DELETE/MERGE.
     853              :  *
     854              :  * The Var must be equal(), aside from varno, to any other row-identity
     855              :  * column with the same rowid_name.  Thus, for example, "wholerow"
     856              :  * row identities had better use vartype == RECORDOID.
     857              :  *
     858              :  * rtindex is currently redundant with rowid_var->varno, but we specify
     859              :  * it as a separate parameter in case this is ever generalized to support
     860              :  * non-Var expressions.  (We could reasonably handle expressions over
     861              :  * Vars of the specified rtindex, but for now that seems unnecessary.)
     862              :  */
     863              : void
     864        20770 : add_row_identity_var(PlannerInfo *root, Var *orig_var,
     865              :                      Index rtindex, const char *rowid_name)
     866              : {
     867              :     TargetEntry *tle;
     868              :     Var        *rowid_var;
     869              :     RowIdentityVarInfo *ridinfo;
     870              :     ListCell   *lc;
     871              : 
     872              :     /* For now, the argument must be just a Var of the given rtindex */
     873              :     Assert(IsA(orig_var, Var));
     874              :     Assert(orig_var->varno == rtindex);
     875              :     Assert(orig_var->varlevelsup == 0);
     876              :     Assert(orig_var->varnullingrels == NULL);
     877              : 
     878              :     /*
     879              :      * If we're doing non-inherited UPDATE/DELETE/MERGE, there's little need
     880              :      * for ROWID_VAR shenanigans.  Just shove the presented Var into the
     881              :      * processed_tlist, and we're done.
     882              :      */
     883        20770 :     if (rtindex == root->parse->resultRelation)
     884              :     {
     885        12051 :         tle = makeTargetEntry((Expr *) orig_var,
     886        12051 :                               list_length(root->processed_tlist) + 1,
     887              :                               pstrdup(rowid_name),
     888              :                               true);
     889        12051 :         root->processed_tlist = lappend(root->processed_tlist, tle);
     890        12051 :         return;
     891              :     }
     892              : 
     893              :     /*
     894              :      * Otherwise, rtindex should reference a leaf target relation that's being
     895              :      * added to the query during expand_inherited_rtentry().
     896              :      */
     897              :     Assert(bms_is_member(rtindex, root->leaf_result_relids));
     898              :     Assert(root->append_rel_array[rtindex] != NULL);
     899              : 
     900              :     /*
     901              :      * We have to find a matching RowIdentityVarInfo, or make one if there is
     902              :      * none.  To allow using equal() to match the vars, change the varno to
     903              :      * ROWID_VAR, leaving all else alone.
     904              :      */
     905         8719 :     rowid_var = copyObject(orig_var);
     906              :     /* This could eventually become ChangeVarNodes() */
     907         8719 :     rowid_var->varno = ROWID_VAR;
     908              : 
     909              :     /* Look for an existing row-id column of the same name */
     910        13141 :     foreach(lc, root->row_identity_vars)
     911              :     {
     912         8666 :         ridinfo = (RowIdentityVarInfo *) lfirst(lc);
     913         8666 :         if (strcmp(rowid_name, ridinfo->rowidname) != 0)
     914         4422 :             continue;
     915         4244 :         if (equal(rowid_var, ridinfo->rowidvar))
     916              :         {
     917              :             /* Found a match; we need only record that rtindex needs it too */
     918         4244 :             ridinfo->rowidrels = bms_add_member(ridinfo->rowidrels, rtindex);
     919         4244 :             return;
     920              :         }
     921              :         else
     922              :         {
     923              :             /* Ooops, can't handle this */
     924            0 :             elog(ERROR, "conflicting uses of row-identity name \"%s\"",
     925              :                  rowid_name);
     926              :         }
     927              :     }
     928              : 
     929              :     /* No request yet, so add a new RowIdentityVarInfo */
     930         4475 :     ridinfo = makeNode(RowIdentityVarInfo);
     931         4475 :     ridinfo->rowidvar = copyObject(rowid_var);
     932              :     /* for the moment, estimate width using just the datatype info */
     933         4475 :     ridinfo->rowidwidth = get_typavgwidth(exprType((Node *) rowid_var),
     934              :                                           exprTypmod((Node *) rowid_var));
     935         4475 :     ridinfo->rowidname = pstrdup(rowid_name);
     936         4475 :     ridinfo->rowidrels = bms_make_singleton(rtindex);
     937              : 
     938         4475 :     root->row_identity_vars = lappend(root->row_identity_vars, ridinfo);
     939              : 
     940              :     /* Change rowid_var into a reference to this row_identity_vars entry */
     941         4475 :     rowid_var->varattno = list_length(root->row_identity_vars);
     942              : 
     943              :     /* Push the ROWID_VAR reference variable into processed_tlist */
     944         4475 :     tle = makeTargetEntry((Expr *) rowid_var,
     945         4475 :                           list_length(root->processed_tlist) + 1,
     946              :                           pstrdup(rowid_name),
     947              :                           true);
     948         4475 :     root->processed_tlist = lappend(root->processed_tlist, tle);
     949              : }
     950              : 
     951              : /*
     952              :  * add_row_identity_columns
     953              :  *
     954              :  * This function adds the row identity columns needed by the core code.
     955              :  * FDWs might call add_row_identity_var() for themselves to add nonstandard
     956              :  * columns.  (Duplicate requests are fine.)
     957              :  */
     958              : void
     959        16562 : add_row_identity_columns(PlannerInfo *root, Index rtindex,
     960              :                          RangeTblEntry *target_rte,
     961              :                          Relation target_relation)
     962              : {
     963        16562 :     CmdType     commandType = root->parse->commandType;
     964        16562 :     char        relkind = target_relation->rd_rel->relkind;
     965              :     Var        *var;
     966              : 
     967              :     Assert(commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE);
     968              : 
     969        16562 :     if (relkind == RELKIND_RELATION ||
     970          470 :         relkind == RELKIND_MATVIEW ||
     971              :         relkind == RELKIND_PARTITIONED_TABLE)
     972              :     {
     973              :         /*
     974              :          * Emit CTID so that executor can find the row to merge, update or
     975              :          * delete.
     976              :          */
     977        16125 :         var = makeVar(rtindex,
     978              :                       SelfItemPointerAttributeNumber,
     979              :                       TIDOID,
     980              :                       -1,
     981              :                       InvalidOid,
     982              :                       0);
     983        16125 :         add_row_identity_var(root, var, rtindex, "ctid");
     984              :     }
     985          437 :     else if (relkind == RELKIND_FOREIGN_TABLE)
     986              :     {
     987              :         /*
     988              :          * Let the foreign table's FDW add whatever junk TLEs it wants.
     989              :          */
     990              :         FdwRoutine *fdwroutine;
     991              : 
     992          196 :         fdwroutine = GetFdwRoutineForRelation(target_relation, false);
     993              : 
     994          196 :         if (fdwroutine->AddForeignUpdateTargets != NULL)
     995          189 :             fdwroutine->AddForeignUpdateTargets(root, rtindex,
     996              :                                                 target_rte, target_relation);
     997              : 
     998              :         /*
     999              :          * For UPDATE, we need to make the FDW fetch unchanged columns by
    1000              :          * asking it to fetch a whole-row Var.  That's because the top-level
    1001              :          * targetlist only contains entries for changed columns, but
    1002              :          * ExecUpdate will need to build the complete new tuple.  (Actually,
    1003              :          * we only really need this in UPDATEs that are not pushed to the
    1004              :          * remote side, but it's hard to tell if that will be the case at the
    1005              :          * point when this function is called.)
    1006              :          *
    1007              :          * We will also need the whole row if there are any row triggers, so
    1008              :          * that the executor will have the "old" row to pass to the trigger.
    1009              :          * Alas, this misses system columns.
    1010              :          */
    1011          196 :         if (commandType == CMD_UPDATE ||
    1012           88 :             (target_relation->trigdesc &&
    1013           15 :              (target_relation->trigdesc->trig_delete_after_row ||
    1014            9 :               target_relation->trigdesc->trig_delete_before_row)))
    1015              :         {
    1016          116 :             var = makeVar(rtindex,
    1017              :                           InvalidAttrNumber,
    1018              :                           RECORDOID,
    1019              :                           -1,
    1020              :                           InvalidOid,
    1021              :                           0);
    1022          116 :             add_row_identity_var(root, var, rtindex, "wholerow");
    1023              :         }
    1024              :     }
    1025        16562 : }
    1026              : 
    1027              : /*
    1028              :  * distribute_row_identity_vars
    1029              :  *
    1030              :  * After we have finished identifying all the row identity columns
    1031              :  * needed by an inherited UPDATE/DELETE/MERGE query, make sure that
    1032              :  * these columns will be generated by all the target relations.
    1033              :  *
    1034              :  * This is more or less like what build_base_rel_tlists() does,
    1035              :  * except that it would not understand what to do with ROWID_VAR Vars.
    1036              :  * Since that function runs before inheritance relations are expanded,
    1037              :  * it will never see any such Vars anyway.
    1038              :  */
    1039              : void
    1040       252405 : distribute_row_identity_vars(PlannerInfo *root)
    1041              : {
    1042       252405 :     Query      *parse = root->parse;
    1043       252405 :     int         result_relation = parse->resultRelation;
    1044              :     RangeTblEntry *target_rte;
    1045              :     RelOptInfo *target_rel;
    1046              :     ListCell   *lc;
    1047              : 
    1048              :     /*
    1049              :      * There's nothing to do if this isn't an inherited UPDATE/DELETE/MERGE.
    1050              :      */
    1051       252405 :     if (parse->commandType != CMD_UPDATE && parse->commandType != CMD_DELETE &&
    1052       239441 :         parse->commandType != CMD_MERGE)
    1053              :     {
    1054              :         Assert(root->row_identity_vars == NIL);
    1055       237971 :         return;
    1056              :     }
    1057        14434 :     target_rte = rt_fetch(result_relation, parse->rtable);
    1058        14434 :     if (!target_rte->inh)
    1059              :     {
    1060              :         Assert(root->row_identity_vars == NIL);
    1061        12188 :         return;
    1062              :     }
    1063              : 
    1064              :     /*
    1065              :      * Ordinarily, we expect that leaf result relation(s) will have added some
    1066              :      * ROWID_VAR Vars to the query.  However, it's possible that constraint
    1067              :      * exclusion suppressed every leaf relation.  The executor will get upset
    1068              :      * if the plan has no row identity columns at all, even though it will
    1069              :      * certainly process no rows.  Handle this edge case by re-opening the top
    1070              :      * result relation and adding the row identity columns it would have used,
    1071              :      * as preprocess_targetlist() would have done if it weren't marked "inh".
    1072              :      * Then re-run build_base_rel_tlists() to ensure that the added columns
    1073              :      * get propagated to the relation's reltarget.  (This is a bit ugly, but
    1074              :      * it seems better to confine the ugliness and extra cycles to this
    1075              :      * unusual corner case.)
    1076              :      */
    1077         2246 :     if (root->row_identity_vars == NIL)
    1078              :     {
    1079              :         Relation    target_relation;
    1080              : 
    1081           28 :         target_relation = table_open(target_rte->relid, NoLock);
    1082           28 :         add_row_identity_columns(root, result_relation,
    1083              :                                  target_rte, target_relation);
    1084           28 :         table_close(target_relation, NoLock);
    1085           28 :         build_base_rel_tlists(root, root->processed_tlist);
    1086              :         /* There are no ROWID_VAR Vars in this case, so we're done. */
    1087           28 :         return;
    1088              :     }
    1089              : 
    1090              :     /*
    1091              :      * Dig through the processed_tlist to find the ROWID_VAR reference Vars,
    1092              :      * and forcibly copy them into the reltarget list of the topmost target
    1093              :      * relation.  That's sufficient because they'll be copied to the
    1094              :      * individual leaf target rels (with appropriate translation) later,
    1095              :      * during appendrel expansion --- see set_append_rel_size().
    1096              :      */
    1097         2218 :     target_rel = find_base_rel(root, result_relation);
    1098              : 
    1099         9303 :     foreach(lc, root->processed_tlist)
    1100              :     {
    1101         7085 :         TargetEntry *tle = lfirst(lc);
    1102         7085 :         Var        *var = (Var *) tle->expr;
    1103              : 
    1104         7085 :         if (var && IsA(var, Var) && var->varno == ROWID_VAR)
    1105              :         {
    1106         4475 :             target_rel->reltarget->exprs =
    1107         4475 :                 lappend(target_rel->reltarget->exprs, copyObject(var));
    1108              :             /* reltarget cost and width will be computed later */
    1109              :         }
    1110              :     }
    1111              : }
        

Generated by: LCOV version 2.0-1