LCOV - differential code coverage report
Current view: top level - src/backend/optimizer/prep - preptlist.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 88.1 % 135 119 16 119
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 4 4 4
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * preptlist.c
       4                 :  *    Routines to preprocess the parse tree target list
       5                 :  *
       6                 :  * For an INSERT, the targetlist must contain an entry for each attribute of
       7                 :  * the target relation in the correct order.
       8                 :  *
       9                 :  * For an UPDATE, the targetlist just contains the expressions for the new
      10                 :  * column values.
      11                 :  *
      12                 :  * For UPDATE and DELETE queries, the targetlist must also contain "junk"
      13                 :  * tlist entries needed to allow the executor to identify the rows to be
      14                 :  * updated or deleted; for example, the ctid of a heap row.  (The planner
      15                 :  * adds these; they're not in what we receive from the parser/rewriter.)
      16                 :  *
      17                 :  * For all query types, there can be additional junk tlist entries, such as
      18                 :  * sort keys, Vars needed for a RETURNING list, and row ID information needed
      19                 :  * for SELECT FOR UPDATE locking and/or EvalPlanQual checking.
      20                 :  *
      21                 :  * The query rewrite phase also does preprocessing of the targetlist (see
      22                 :  * rewriteTargetListIU).  The division of labor between here and there is
      23                 :  * partially historical, but it's not entirely arbitrary.  The stuff done
      24                 :  * here is closely connected to physical access to tables, whereas the
      25                 :  * rewriter's work is more concerned with SQL semantics.
      26                 :  *
      27                 :  *
      28                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
      29                 :  * Portions Copyright (c) 1994, Regents of the University of California
      30                 :  *
      31                 :  * IDENTIFICATION
      32                 :  *    src/backend/optimizer/prep/preptlist.c
      33                 :  *
      34                 :  *-------------------------------------------------------------------------
      35                 :  */
      36                 : 
      37                 : #include "postgres.h"
      38                 : 
      39                 : #include "access/table.h"
      40                 : #include "nodes/makefuncs.h"
      41                 : #include "optimizer/appendinfo.h"
      42                 : #include "optimizer/optimizer.h"
      43                 : #include "optimizer/prep.h"
      44                 : #include "optimizer/tlist.h"
      45                 : #include "parser/parse_coerce.h"
      46                 : #include "parser/parsetree.h"
      47                 : #include "utils/rel.h"
      48                 : 
      49                 : static List *expand_insert_targetlist(List *tlist, Relation rel);
      50                 : 
      51                 : 
      52                 : /*
      53                 :  * preprocess_targetlist
      54                 :  *    Driver for preprocessing the parse tree targetlist.
      55                 :  *
      56                 :  * The preprocessed targetlist is returned in root->processed_tlist.
      57                 :  * Also, if this is an UPDATE, we return a list of target column numbers
      58                 :  * in root->update_colnos.  (Resnos in processed_tlist will be consecutive,
      59                 :  * so do not look at that to find out which columns are targets!)
      60                 :  */
      61                 : void
      62 CBC      222898 : preprocess_targetlist(PlannerInfo *root)
      63                 : {
      64          222898 :     Query      *parse = root->parse;
      65          222898 :     int         result_relation = parse->resultRelation;
      66          222898 :     List       *range_table = parse->rtable;
      67          222898 :     CmdType     command_type = parse->commandType;
      68          222898 :     RangeTblEntry *target_rte = NULL;
      69          222898 :     Relation    target_relation = NULL;
      70                 :     List       *tlist;
      71                 :     ListCell   *lc;
      72                 : 
      73                 :     /*
      74                 :      * If there is a result relation, open it so we can look for missing
      75                 :      * columns and so on.  We assume that previous code already acquired at
      76                 :      * least AccessShareLock on the relation, so we need no lock here.
      77                 :      */
      78          222898 :     if (result_relation)
      79                 :     {
      80           52265 :         target_rte = rt_fetch(result_relation, range_table);
      81                 : 
      82                 :         /*
      83                 :          * Sanity check: it'd better be a real relation not, say, a subquery.
      84                 :          * Else parser or rewriter messed up.
      85                 :          */
      86           52265 :         if (target_rte->rtekind != RTE_RELATION)
      87 UBC           0 :             elog(ERROR, "result relation must be a regular relation");
      88                 : 
      89 CBC       52265 :         target_relation = table_open(target_rte->relid, NoLock);
      90                 :     }
      91                 :     else
      92          170633 :         Assert(command_type == CMD_SELECT);
      93                 : 
      94                 :     /*
      95                 :      * In an INSERT, the executor expects the targetlist to match the exact
      96                 :      * order of the target table's attributes, including entries for
      97                 :      * attributes not mentioned in the source query.
      98                 :      *
      99                 :      * In an UPDATE, we don't rearrange the tlist order, but we need to make a
     100                 :      * separate list of the target attribute numbers, in tlist order, and then
     101                 :      * renumber the processed_tlist entries to be consecutive.
     102                 :      */
     103          222898 :     tlist = parse->targetList;
     104          222898 :     if (command_type == CMD_INSERT)
     105           42251 :         tlist = expand_insert_targetlist(tlist, target_relation);
     106          180647 :     else if (command_type == CMD_UPDATE)
     107            7582 :         root->update_colnos = extract_update_targetlist_colnos(tlist);
     108                 : 
     109                 :     /*
     110                 :      * For non-inherited UPDATE/DELETE/MERGE, register any junk column(s)
     111                 :      * needed to allow the executor to identify the rows to be updated or
     112                 :      * deleted.  In the inheritance case, we do nothing now, leaving this to
     113                 :      * be dealt with when expand_inherited_rtentry() makes the leaf target
     114                 :      * relations.  (But there might not be any leaf target relations, in which
     115                 :      * case we must do this in distribute_row_identity_vars().)
     116                 :      */
     117          222898 :     if ((command_type == CMD_UPDATE || command_type == CMD_DELETE ||
     118           10014 :          command_type == CMD_MERGE) &&
     119           10014 :         !target_rte->inh)
     120                 :     {
     121                 :         /* row-identity logic expects to add stuff to processed_tlist */
     122            8848 :         root->processed_tlist = tlist;
     123            8848 :         add_row_identity_columns(root, result_relation,
     124                 :                                  target_rte, target_relation);
     125            8848 :         tlist = root->processed_tlist;
     126                 :     }
     127                 : 
     128                 :     /*
     129                 :      * For MERGE we also need to handle the target list for each INSERT and
     130                 :      * UPDATE action separately.  In addition, we examine the qual of each
     131                 :      * action and add any Vars there (other than those of the target rel) to
     132                 :      * the subplan targetlist.
     133                 :      */
     134          222898 :     if (command_type == CMD_MERGE)
     135                 :     {
     136                 :         ListCell   *l;
     137                 : 
     138                 :         /*
     139                 :          * For MERGE, handle targetlist of each MergeAction separately. Give
     140                 :          * the same treatment to MergeAction->targetList as we would have
     141                 :          * given to a regular INSERT.  For UPDATE, collect the column numbers
     142                 :          * being modified.
     143                 :          */
     144            1176 :         foreach(l, parse->mergeActionList)
     145                 :         {
     146             711 :             MergeAction *action = (MergeAction *) lfirst(l);
     147                 :             List       *vars;
     148                 :             ListCell   *l2;
     149                 : 
     150             711 :             if (action->commandType == CMD_INSERT)
     151             277 :                 action->targetList = expand_insert_targetlist(action->targetList,
     152                 :                                                               target_relation);
     153             434 :             else if (action->commandType == CMD_UPDATE)
     154             322 :                 action->updateColnos =
     155             322 :                     extract_update_targetlist_colnos(action->targetList);
     156                 : 
     157                 :             /*
     158                 :              * Add resjunk entries for any Vars and PlaceHolderVars used in
     159                 :              * each action's targetlist and WHEN condition that belong to
     160                 :              * relations other than the target.  We don't expect to see any
     161                 :              * aggregates or window functions here.
     162                 :              */
     163             711 :             vars = pull_var_clause((Node *)
     164             711 :                                    list_concat_copy((List *) action->qual,
     165             711 :                                                     action->targetList),
     166                 :                                    PVC_INCLUDE_PLACEHOLDERS);
     167            1705 :             foreach(l2, vars)
     168                 :             {
     169             994 :                 Var        *var = (Var *) lfirst(l2);
     170                 :                 TargetEntry *tle;
     171                 : 
     172             994 :                 if (IsA(var, Var) && var->varno == result_relation)
     173             452 :                     continue;   /* don't need it */
     174                 : 
     175             542 :                 if (tlist_member((Expr *) var, tlist))
     176             128 :                     continue;   /* already got it */
     177                 : 
     178             414 :                 tle = makeTargetEntry((Expr *) var,
     179             414 :                                       list_length(tlist) + 1,
     180                 :                                       NULL, true);
     181             414 :                 tlist = lappend(tlist, tle);
     182                 :             }
     183             711 :             list_free(vars);
     184                 :         }
     185                 :     }
     186                 : 
     187                 :     /*
     188                 :      * Add necessary junk columns for rowmarked rels.  These values are needed
     189                 :      * for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual
     190                 :      * rechecking.  See comments for PlanRowMark in plannodes.h.  If you
     191                 :      * change this stanza, see also expand_inherited_rtentry(), which has to
     192                 :      * be able to add on junk columns equivalent to these.
     193                 :      *
     194                 :      * (Someday it might be useful to fold these resjunk columns into the
     195                 :      * row-identity-column management used for UPDATE/DELETE.  Today is not
     196                 :      * that day, however.  One notable issue is that it seems important that
     197                 :      * the whole-row Vars made here use the real table rowtype, not RECORD, so
     198                 :      * that conversion to/from child relations' rowtypes will happen.  Also,
     199                 :      * since these entries don't potentially bloat with more and more child
     200                 :      * relations, there's not really much need for column sharing.)
     201                 :      */
     202          227068 :     foreach(lc, root->rowMarks)
     203                 :     {
     204            4170 :         PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
     205                 :         Var        *var;
     206                 :         char        resname[32];
     207                 :         TargetEntry *tle;
     208                 : 
     209                 :         /* child rels use the same junk attrs as their parents */
     210            4170 :         if (rc->rti != rc->prti)
     211 UBC           0 :             continue;
     212                 : 
     213 CBC        4170 :         if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
     214                 :         {
     215                 :             /* Need to fetch TID */
     216            3991 :             var = makeVar(rc->rti,
     217                 :                           SelfItemPointerAttributeNumber,
     218                 :                           TIDOID,
     219                 :                           -1,
     220                 :                           InvalidOid,
     221                 :                           0);
     222            3991 :             snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);
     223            3991 :             tle = makeTargetEntry((Expr *) var,
     224            3991 :                                   list_length(tlist) + 1,
     225                 :                                   pstrdup(resname),
     226                 :                                   true);
     227            3991 :             tlist = lappend(tlist, tle);
     228                 :         }
     229            4170 :         if (rc->allMarkTypes & (1 << ROW_MARK_COPY))
     230                 :         {
     231                 :             /* Need the whole row as a junk var */
     232             179 :             var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
     233             179 :                                   rc->rti,
     234                 :                                   0,
     235                 :                                   false);
     236             179 :             snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
     237             179 :             tle = makeTargetEntry((Expr *) var,
     238             179 :                                   list_length(tlist) + 1,
     239                 :                                   pstrdup(resname),
     240                 :                                   true);
     241             179 :             tlist = lappend(tlist, tle);
     242                 :         }
     243                 : 
     244                 :         /* If parent of inheritance tree, always fetch the tableoid too. */
     245            4170 :         if (rc->isParent)
     246                 :         {
     247 UBC           0 :             var = makeVar(rc->rti,
     248                 :                           TableOidAttributeNumber,
     249                 :                           OIDOID,
     250                 :                           -1,
     251                 :                           InvalidOid,
     252                 :                           0);
     253               0 :             snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
     254               0 :             tle = makeTargetEntry((Expr *) var,
     255               0 :                                   list_length(tlist) + 1,
     256                 :                                   pstrdup(resname),
     257                 :                                   true);
     258               0 :             tlist = lappend(tlist, tle);
     259                 :         }
     260                 :     }
     261                 : 
     262                 :     /*
     263                 :      * If the query has a RETURNING list, add resjunk entries for any Vars
     264                 :      * used in RETURNING that belong to other relations.  We need to do this
     265                 :      * to make these Vars available for the RETURNING calculation.  Vars that
     266                 :      * belong to the result rel don't need to be added, because they will be
     267                 :      * made to refer to the actual heap tuple.
     268                 :      */
     269 CBC      222898 :     if (parse->returningList && list_length(parse->rtable) > 1)
     270                 :     {
     271                 :         List       *vars;
     272                 :         ListCell   *l;
     273                 : 
     274             667 :         vars = pull_var_clause((Node *) parse->returningList,
     275                 :                                PVC_RECURSE_AGGREGATES |
     276                 :                                PVC_RECURSE_WINDOWFUNCS |
     277                 :                                PVC_INCLUDE_PLACEHOLDERS);
     278            2444 :         foreach(l, vars)
     279                 :         {
     280            1777 :             Var        *var = (Var *) lfirst(l);
     281                 :             TargetEntry *tle;
     282                 : 
     283            1777 :             if (IsA(var, Var) &&
     284            1777 :                 var->varno == result_relation)
     285            1596 :                 continue;       /* don't need it */
     286                 : 
     287             181 :             if (tlist_member((Expr *) var, tlist))
     288              29 :                 continue;       /* already got it */
     289                 : 
     290             152 :             tle = makeTargetEntry((Expr *) var,
     291             152 :                                   list_length(tlist) + 1,
     292                 :                                   NULL,
     293                 :                                   true);
     294                 : 
     295             152 :             tlist = lappend(tlist, tle);
     296                 :         }
     297             667 :         list_free(vars);
     298                 :     }
     299                 : 
     300          222898 :     root->processed_tlist = tlist;
     301                 : 
     302          222898 :     if (target_relation)
     303           52265 :         table_close(target_relation, NoLock);
     304          222898 : }
     305                 : 
     306                 : /*
     307                 :  * extract_update_targetlist_colnos
     308                 :  *      Extract a list of the target-table column numbers that
     309                 :  *      an UPDATE's targetlist wants to assign to, then renumber.
     310                 :  *
     311                 :  * The convention in the parser and rewriter is that the resnos in an
     312                 :  * UPDATE's non-resjunk TLE entries are the target column numbers
     313                 :  * to assign to.  Here, we extract that info into a separate list, and
     314                 :  * then convert the tlist to the sequential-numbering convention that's
     315                 :  * used by all other query types.
     316                 :  *
     317                 :  * This is also applied to the tlist associated with INSERT ... ON CONFLICT
     318                 :  * ... UPDATE, although not till much later in planning.
     319                 :  */
     320                 : List *
     321            8619 : extract_update_targetlist_colnos(List *tlist)
     322                 : {
     323            8619 :     List       *update_colnos = NIL;
     324            8619 :     AttrNumber  nextresno = 1;
     325                 :     ListCell   *lc;
     326                 : 
     327           19341 :     foreach(lc, tlist)
     328                 :     {
     329           10722 :         TargetEntry *tle = (TargetEntry *) lfirst(lc);
     330                 : 
     331           10722 :         if (!tle->resjunk)
     332           10590 :             update_colnos = lappend_int(update_colnos, tle->resno);
     333           10722 :         tle->resno = nextresno++;
     334                 :     }
     335            8619 :     return update_colnos;
     336                 : }
     337                 : 
     338                 : 
     339                 : /*****************************************************************************
     340                 :  *
     341                 :  *      TARGETLIST EXPANSION
     342                 :  *
     343                 :  *****************************************************************************/
     344                 : 
     345                 : /*
     346                 :  * expand_insert_targetlist
     347                 :  *    Given a target list as generated by the parser and a result relation,
     348                 :  *    add targetlist entries for any missing attributes, and ensure the
     349                 :  *    non-junk attributes appear in proper field order.
     350                 :  *
     351                 :  * Once upon a time we also did more or less this with UPDATE targetlists,
     352                 :  * but now this code is only applied to INSERT targetlists.
     353                 :  */
     354                 : static List *
     355           42528 : expand_insert_targetlist(List *tlist, Relation rel)
     356                 : {
     357           42528 :     List       *new_tlist = NIL;
     358                 :     ListCell   *tlist_item;
     359                 :     int         attrno,
     360                 :                 numattrs;
     361                 : 
     362           42528 :     tlist_item = list_head(tlist);
     363                 : 
     364                 :     /*
     365                 :      * The rewriter should have already ensured that the TLEs are in correct
     366                 :      * order; but we have to insert TLEs for any missing attributes.
     367                 :      *
     368                 :      * Scan the tuple description in the relation's relcache entry to make
     369                 :      * sure we have all the user attributes in the right order.
     370                 :      */
     371           42528 :     numattrs = RelationGetNumberOfAttributes(rel);
     372                 : 
     373          182926 :     for (attrno = 1; attrno <= numattrs; attrno++)
     374                 :     {
     375          140398 :         Form_pg_attribute att_tup = TupleDescAttr(rel->rd_att, attrno - 1);
     376          140398 :         TargetEntry *new_tle = NULL;
     377                 : 
     378          140398 :         if (tlist_item != NULL)
     379                 :         {
     380          135647 :             TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
     381                 : 
     382          135647 :             if (!old_tle->resjunk && old_tle->resno == attrno)
     383                 :             {
     384          131206 :                 new_tle = old_tle;
     385          131206 :                 tlist_item = lnext(tlist, tlist_item);
     386                 :             }
     387                 :         }
     388                 : 
     389          140398 :         if (new_tle == NULL)
     390                 :         {
     391                 :             /*
     392                 :              * Didn't find a matching tlist entry, so make one.
     393                 :              *
     394                 :              * INSERTs should insert NULL in this case.  (We assume the
     395                 :              * rewriter would have inserted any available non-NULL default
     396                 :              * value.)  Also, if the column isn't dropped, apply any domain
     397                 :              * constraints that might exist --- this is to catch domain NOT
     398                 :              * NULL.
     399                 :              *
     400                 :              * When generating a NULL constant for a dropped column, we label
     401                 :              * it INT4 (any other guaranteed-to-exist datatype would do as
     402                 :              * well). We can't label it with the dropped column's datatype
     403                 :              * since that might not exist anymore.  It does not really matter
     404                 :              * what we claim the type is, since NULL is NULL --- its
     405                 :              * representation is datatype-independent.  This could perhaps
     406                 :              * confuse code comparing the finished plan to the target
     407                 :              * relation, however.
     408                 :              */
     409            9192 :             Oid         atttype = att_tup->atttypid;
     410            9192 :             Oid         attcollation = att_tup->attcollation;
     411                 :             Node       *new_expr;
     412                 : 
     413            9192 :             if (!att_tup->attisdropped)
     414                 :             {
     415            8885 :                 new_expr = (Node *) makeConst(atttype,
     416                 :                                               -1,
     417                 :                                               attcollation,
     418            8885 :                                               att_tup->attlen,
     419                 :                                               (Datum) 0,
     420                 :                                               true, /* isnull */
     421            8885 :                                               att_tup->attbyval);
     422            8885 :                 new_expr = coerce_to_domain(new_expr,
     423                 :                                             InvalidOid, -1,
     424                 :                                             atttype,
     425                 :                                             COERCION_IMPLICIT,
     426                 :                                             COERCE_IMPLICIT_CAST,
     427                 :                                             -1,
     428                 :                                             false);
     429                 :             }
     430                 :             else
     431                 :             {
     432                 :                 /* Insert NULL for dropped column */
     433             307 :                 new_expr = (Node *) makeConst(INT4OID,
     434                 :                                               -1,
     435                 :                                               InvalidOid,
     436                 :                                               sizeof(int32),
     437                 :                                               (Datum) 0,
     438                 :                                               true, /* isnull */
     439                 :                                               true /* byval */ );
     440                 :             }
     441                 : 
     442            9192 :             new_tle = makeTargetEntry((Expr *) new_expr,
     443                 :                                       attrno,
     444            9192 :                                       pstrdup(NameStr(att_tup->attname)),
     445                 :                                       false);
     446                 :         }
     447                 : 
     448          140398 :         new_tlist = lappend(new_tlist, new_tle);
     449                 :     }
     450                 : 
     451                 :     /*
     452                 :      * The remaining tlist entries should be resjunk; append them all to the
     453                 :      * end of the new tlist, making sure they have resnos higher than the last
     454                 :      * real attribute.  (Note: although the rewriter already did such
     455                 :      * renumbering, we have to do it again here in case we added NULL entries
     456                 :      * above.)
     457                 :      */
     458           42528 :     while (tlist_item)
     459                 :     {
     460 UBC           0 :         TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
     461                 : 
     462               0 :         if (!old_tle->resjunk)
     463               0 :             elog(ERROR, "targetlist is not sorted correctly");
     464                 :         /* Get the resno right, but don't copy unnecessarily */
     465               0 :         if (old_tle->resno != attrno)
     466                 :         {
     467               0 :             old_tle = flatCopyTargetEntry(old_tle);
     468               0 :             old_tle->resno = attrno;
     469                 :         }
     470               0 :         new_tlist = lappend(new_tlist, old_tle);
     471               0 :         attrno++;
     472               0 :         tlist_item = lnext(tlist, tlist_item);
     473                 :     }
     474                 : 
     475 CBC       42528 :     return new_tlist;
     476                 : }
     477                 : 
     478                 : 
     479                 : /*
     480                 :  * Locate PlanRowMark for given RT index, or return NULL if none
     481                 :  *
     482                 :  * This probably ought to be elsewhere, but there's no very good place
     483                 :  */
     484                 : PlanRowMark *
     485            9630 : get_plan_rowmark(List *rowmarks, Index rtindex)
     486                 : {
     487                 :     ListCell   *l;
     488                 : 
     489           10229 :     foreach(l, rowmarks)
     490                 :     {
     491            1592 :         PlanRowMark *rc = (PlanRowMark *) lfirst(l);
     492                 : 
     493            1592 :         if (rc->rti == rtindex)
     494             993 :             return rc;
     495                 :     }
     496            8637 :     return NULL;
     497                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a