Header And Logo

PostgreSQL
| The world's most advanced open source database.

preptlist.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * preptlist.c
00004  *    Routines to preprocess the parse tree target list
00005  *
00006  * For INSERT and UPDATE queries, the targetlist must contain an entry for
00007  * each attribute of the target relation in the correct order.  For all query
00008  * types, we may need to add junk tlist entries for Vars used in the RETURNING
00009  * list and row ID information needed for SELECT FOR UPDATE locking and/or
00010  * EvalPlanQual checking.
00011  *
00012  * NOTE: the rewriter's rewriteTargetListIU and rewriteTargetListUD
00013  * routines also do preprocessing of the targetlist.  The division of labor
00014  * between here and there is a bit arbitrary and historical.
00015  *
00016  *
00017  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00018  * Portions Copyright (c) 1994, Regents of the University of California
00019  *
00020  * IDENTIFICATION
00021  *    src/backend/optimizer/prep/preptlist.c
00022  *
00023  *-------------------------------------------------------------------------
00024  */
00025 
00026 #include "postgres.h"
00027 
00028 #include "access/heapam.h"
00029 #include "access/sysattr.h"
00030 #include "catalog/pg_type.h"
00031 #include "nodes/makefuncs.h"
00032 #include "optimizer/prep.h"
00033 #include "optimizer/tlist.h"
00034 #include "parser/parsetree.h"
00035 #include "parser/parse_coerce.h"
00036 #include "utils/rel.h"
00037 
00038 
00039 static List *expand_targetlist(List *tlist, int command_type,
00040                   Index result_relation, List *range_table);
00041 
00042 
00043 /*
00044  * preprocess_targetlist
00045  *    Driver for preprocessing the parse tree targetlist.
00046  *
00047  *    Returns the new targetlist.
00048  */
00049 List *
00050 preprocess_targetlist(PlannerInfo *root, List *tlist)
00051 {
00052     Query      *parse = root->parse;
00053     int         result_relation = parse->resultRelation;
00054     List       *range_table = parse->rtable;
00055     CmdType     command_type = parse->commandType;
00056     ListCell   *lc;
00057 
00058     /*
00059      * Sanity check: if there is a result relation, it'd better be a real
00060      * relation not a subquery.  Else parser or rewriter messed up.
00061      */
00062     if (result_relation)
00063     {
00064         RangeTblEntry *rte = rt_fetch(result_relation, range_table);
00065 
00066         if (rte->subquery != NULL || rte->relid == InvalidOid)
00067             elog(ERROR, "subquery cannot be result relation");
00068     }
00069 
00070     /*
00071      * for heap_form_tuple to work, the targetlist must match the exact order
00072      * of the attributes. We also need to fill in any missing attributes. -ay
00073      * 10/94
00074      */
00075     if (command_type == CMD_INSERT || command_type == CMD_UPDATE)
00076         tlist = expand_targetlist(tlist, command_type,
00077                                   result_relation, range_table);
00078 
00079     /*
00080      * Add necessary junk columns for rowmarked rels.  These values are needed
00081      * for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual
00082      * rechecking.  See comments for PlanRowMark in plannodes.h.
00083      */
00084     foreach(lc, root->rowMarks)
00085     {
00086         PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
00087         Var        *var;
00088         char        resname[32];
00089         TargetEntry *tle;
00090 
00091         /* child rels use the same junk attrs as their parents */
00092         if (rc->rti != rc->prti)
00093             continue;
00094 
00095         if (rc->markType != ROW_MARK_COPY)
00096         {
00097             /* It's a regular table, so fetch its TID */
00098             var = makeVar(rc->rti,
00099                           SelfItemPointerAttributeNumber,
00100                           TIDOID,
00101                           -1,
00102                           InvalidOid,
00103                           0);
00104             snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);
00105             tle = makeTargetEntry((Expr *) var,
00106                                   list_length(tlist) + 1,
00107                                   pstrdup(resname),
00108                                   true);
00109             tlist = lappend(tlist, tle);
00110 
00111             /* if parent of inheritance tree, need the tableoid too */
00112             if (rc->isParent)
00113             {
00114                 var = makeVar(rc->rti,
00115                               TableOidAttributeNumber,
00116                               OIDOID,
00117                               -1,
00118                               InvalidOid,
00119                               0);
00120                 snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
00121                 tle = makeTargetEntry((Expr *) var,
00122                                       list_length(tlist) + 1,
00123                                       pstrdup(resname),
00124                                       true);
00125                 tlist = lappend(tlist, tle);
00126             }
00127         }
00128         else
00129         {
00130             /* Not a table, so we need the whole row as a junk var */
00131             var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
00132                                   rc->rti,
00133                                   0,
00134                                   false);
00135             snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
00136             tle = makeTargetEntry((Expr *) var,
00137                                   list_length(tlist) + 1,
00138                                   pstrdup(resname),
00139                                   true);
00140             tlist = lappend(tlist, tle);
00141         }
00142     }
00143 
00144     /*
00145      * If the query has a RETURNING list, add resjunk entries for any Vars
00146      * used in RETURNING that belong to other relations.  We need to do this
00147      * to make these Vars available for the RETURNING calculation.  Vars that
00148      * belong to the result rel don't need to be added, because they will be
00149      * made to refer to the actual heap tuple.
00150      */
00151     if (parse->returningList && list_length(parse->rtable) > 1)
00152     {
00153         List       *vars;
00154         ListCell   *l;
00155 
00156         vars = pull_var_clause((Node *) parse->returningList,
00157                                PVC_RECURSE_AGGREGATES,
00158                                PVC_INCLUDE_PLACEHOLDERS);
00159         foreach(l, vars)
00160         {
00161             Var        *var = (Var *) lfirst(l);
00162             TargetEntry *tle;
00163 
00164             if (IsA(var, Var) &&
00165                 var->varno == result_relation)
00166                 continue;       /* don't need it */
00167 
00168             if (tlist_member((Node *) var, tlist))
00169                 continue;       /* already got it */
00170 
00171             tle = makeTargetEntry((Expr *) var,
00172                                   list_length(tlist) + 1,
00173                                   NULL,
00174                                   true);
00175 
00176             tlist = lappend(tlist, tle);
00177         }
00178         list_free(vars);
00179     }
00180 
00181     return tlist;
00182 }
00183 
00184 /*****************************************************************************
00185  *
00186  *      TARGETLIST EXPANSION
00187  *
00188  *****************************************************************************/
00189 
00190 /*
00191  * expand_targetlist
00192  *    Given a target list as generated by the parser and a result relation,
00193  *    add targetlist entries for any missing attributes, and ensure the
00194  *    non-junk attributes appear in proper field order.
00195  */
00196 static List *
00197 expand_targetlist(List *tlist, int command_type,
00198                   Index result_relation, List *range_table)
00199 {
00200     List       *new_tlist = NIL;
00201     ListCell   *tlist_item;
00202     Relation    rel;
00203     int         attrno,
00204                 numattrs;
00205 
00206     tlist_item = list_head(tlist);
00207 
00208     /*
00209      * The rewriter should have already ensured that the TLEs are in correct
00210      * order; but we have to insert TLEs for any missing attributes.
00211      *
00212      * Scan the tuple description in the relation's relcache entry to make
00213      * sure we have all the user attributes in the right order.  We assume
00214      * that the rewriter already acquired at least AccessShareLock on the
00215      * relation, so we need no lock here.
00216      */
00217     rel = heap_open(getrelid(result_relation, range_table), NoLock);
00218 
00219     numattrs = RelationGetNumberOfAttributes(rel);
00220 
00221     for (attrno = 1; attrno <= numattrs; attrno++)
00222     {
00223         Form_pg_attribute att_tup = rel->rd_att->attrs[attrno - 1];
00224         TargetEntry *new_tle = NULL;
00225 
00226         if (tlist_item != NULL)
00227         {
00228             TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
00229 
00230             if (!old_tle->resjunk && old_tle->resno == attrno)
00231             {
00232                 new_tle = old_tle;
00233                 tlist_item = lnext(tlist_item);
00234             }
00235         }
00236 
00237         if (new_tle == NULL)
00238         {
00239             /*
00240              * Didn't find a matching tlist entry, so make one.
00241              *
00242              * For INSERT, generate a NULL constant.  (We assume the rewriter
00243              * would have inserted any available default value.) Also, if the
00244              * column isn't dropped, apply any domain constraints that might
00245              * exist --- this is to catch domain NOT NULL.
00246              *
00247              * For UPDATE, generate a Var reference to the existing value of
00248              * the attribute, so that it gets copied to the new tuple. But
00249              * generate a NULL for dropped columns (we want to drop any old
00250              * values).
00251              *
00252              * When generating a NULL constant for a dropped column, we label
00253              * it INT4 (any other guaranteed-to-exist datatype would do as
00254              * well). We can't label it with the dropped column's datatype
00255              * since that might not exist anymore.  It does not really matter
00256              * what we claim the type is, since NULL is NULL --- its
00257              * representation is datatype-independent.  This could perhaps
00258              * confuse code comparing the finished plan to the target
00259              * relation, however.
00260              */
00261             Oid         atttype = att_tup->atttypid;
00262             int32       atttypmod = att_tup->atttypmod;
00263             Oid         attcollation = att_tup->attcollation;
00264             Node       *new_expr;
00265 
00266             switch (command_type)
00267             {
00268                 case CMD_INSERT:
00269                     if (!att_tup->attisdropped)
00270                     {
00271                         new_expr = (Node *) makeConst(atttype,
00272                                                       -1,
00273                                                       attcollation,
00274                                                       att_tup->attlen,
00275                                                       (Datum) 0,
00276                                                       true,     /* isnull */
00277                                                       att_tup->attbyval);
00278                         new_expr = coerce_to_domain(new_expr,
00279                                                     InvalidOid, -1,
00280                                                     atttype,
00281                                                     COERCE_IMPLICIT_CAST,
00282                                                     -1,
00283                                                     false,
00284                                                     false);
00285                     }
00286                     else
00287                     {
00288                         /* Insert NULL for dropped column */
00289                         new_expr = (Node *) makeConst(INT4OID,
00290                                                       -1,
00291                                                       InvalidOid,
00292                                                       sizeof(int32),
00293                                                       (Datum) 0,
00294                                                       true,     /* isnull */
00295                                                       true /* byval */ );
00296                     }
00297                     break;
00298                 case CMD_UPDATE:
00299                     if (!att_tup->attisdropped)
00300                     {
00301                         new_expr = (Node *) makeVar(result_relation,
00302                                                     attrno,
00303                                                     atttype,
00304                                                     atttypmod,
00305                                                     attcollation,
00306                                                     0);
00307                     }
00308                     else
00309                     {
00310                         /* Insert NULL for dropped column */
00311                         new_expr = (Node *) makeConst(INT4OID,
00312                                                       -1,
00313                                                       InvalidOid,
00314                                                       sizeof(int32),
00315                                                       (Datum) 0,
00316                                                       true,     /* isnull */
00317                                                       true /* byval */ );
00318                     }
00319                     break;
00320                 default:
00321                     elog(ERROR, "unrecognized command_type: %d",
00322                          (int) command_type);
00323                     new_expr = NULL;    /* keep compiler quiet */
00324                     break;
00325             }
00326 
00327             new_tle = makeTargetEntry((Expr *) new_expr,
00328                                       attrno,
00329                                       pstrdup(NameStr(att_tup->attname)),
00330                                       false);
00331         }
00332 
00333         new_tlist = lappend(new_tlist, new_tle);
00334     }
00335 
00336     /*
00337      * The remaining tlist entries should be resjunk; append them all to the
00338      * end of the new tlist, making sure they have resnos higher than the last
00339      * real attribute.  (Note: although the rewriter already did such
00340      * renumbering, we have to do it again here in case we are doing an UPDATE
00341      * in a table with dropped columns, or an inheritance child table with
00342      * extra columns.)
00343      */
00344     while (tlist_item)
00345     {
00346         TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
00347 
00348         if (!old_tle->resjunk)
00349             elog(ERROR, "targetlist is not sorted correctly");
00350         /* Get the resno right, but don't copy unnecessarily */
00351         if (old_tle->resno != attrno)
00352         {
00353             old_tle = flatCopyTargetEntry(old_tle);
00354             old_tle->resno = attrno;
00355         }
00356         new_tlist = lappend(new_tlist, old_tle);
00357         attrno++;
00358         tlist_item = lnext(tlist_item);
00359     }
00360 
00361     heap_close(rel, NoLock);
00362 
00363     return new_tlist;
00364 }
00365 
00366 
00367 /*
00368  * Locate PlanRowMark for given RT index, or return NULL if none
00369  *
00370  * This probably ought to be elsewhere, but there's no very good place
00371  */
00372 PlanRowMark *
00373 get_plan_rowmark(List *rowmarks, Index rtindex)
00374 {
00375     ListCell   *l;
00376 
00377     foreach(l, rowmarks)
00378     {
00379         PlanRowMark *rc = (PlanRowMark *) lfirst(l);
00380 
00381         if (rc->rti == rtindex)
00382             return rc;
00383     }
00384     return NULL;
00385 }