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

Generated by: LCOV version 2.0-1