]> granicus.if.org Git - postgresql/blob - src/backend/optimizer/prep/preptlist.c
Replace heapam.h includes with {table, relation}.h where applicable.
[postgresql] / src / backend / optimizer / prep / preptlist.c
1 /*-------------------------------------------------------------------------
2  *
3  * preptlist.c
4  *        Routines to preprocess the parse tree target list
5  *
6  * For INSERT and UPDATE queries, the targetlist must contain an entry for
7  * each attribute of the target relation in the correct order.  For UPDATE and
8  * DELETE queries, it must also contain junk tlist entries needed to allow the
9  * executor to identify the rows to be updated or deleted.  For all query
10  * types, we may need to add junk tlist entries for Vars used in the RETURNING
11  * list and row ID information needed for SELECT FOR UPDATE locking and/or
12  * EvalPlanQual checking.
13  *
14  * The query rewrite phase also does preprocessing of the targetlist (see
15  * rewriteTargetListIU).  The division of labor between here and there is
16  * partially historical, but it's not entirely arbitrary.  In particular,
17  * consider an UPDATE across an inheritance tree.  What rewriteTargetListIU
18  * does need be done only once (because it depends only on the properties of
19  * the parent relation).  What's done here has to be done over again for each
20  * child relation, because it depends on the properties of the child, which
21  * might be of a different relation type, or have more columns and/or a
22  * different column order than the parent.
23  *
24  * The fact that rewriteTargetListIU sorts non-resjunk tlist entries by column
25  * position, which expand_targetlist depends on, violates the above comment
26  * because the sorting is only valid for the parent relation.  In inherited
27  * UPDATE cases, adjust_inherited_tlist runs in between to take care of fixing
28  * the tlists for child tables to keep expand_targetlist happy.  We do it like
29  * that because it's faster in typical non-inherited cases.
30  *
31  *
32  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
33  * Portions Copyright (c) 1994, Regents of the University of California
34  *
35  * IDENTIFICATION
36  *        src/backend/optimizer/prep/preptlist.c
37  *
38  *-------------------------------------------------------------------------
39  */
40
41 #include "postgres.h"
42
43 #include "access/sysattr.h"
44 #include "access/table.h"
45 #include "catalog/pg_type.h"
46 #include "nodes/makefuncs.h"
47 #include "optimizer/prep.h"
48 #include "optimizer/tlist.h"
49 #include "optimizer/var.h"
50 #include "parser/parsetree.h"
51 #include "parser/parse_coerce.h"
52 #include "rewrite/rewriteHandler.h"
53 #include "utils/rel.h"
54
55
56 static List *expand_targetlist(List *tlist, int command_type,
57                                   Index result_relation, Relation rel);
58
59
60 /*
61  * preprocess_targetlist
62  *        Driver for preprocessing the parse tree targetlist.
63  *
64  *        Returns the new targetlist.
65  *
66  * As a side effect, if there's an ON CONFLICT UPDATE clause, its targetlist
67  * is also preprocessed (and updated in-place).
68  */
69 List *
70 preprocess_targetlist(PlannerInfo *root)
71 {
72         Query      *parse = root->parse;
73         int                     result_relation = parse->resultRelation;
74         List       *range_table = parse->rtable;
75         CmdType         command_type = parse->commandType;
76         RangeTblEntry *target_rte = NULL;
77         Relation        target_relation = NULL;
78         List       *tlist;
79         ListCell   *lc;
80
81         /*
82          * If there is a result relation, open it so we can look for missing
83          * columns and so on.  We assume that previous code already acquired at
84          * least AccessShareLock on the relation, so we need no lock here.
85          */
86         if (result_relation)
87         {
88                 target_rte = rt_fetch(result_relation, range_table);
89
90                 /*
91                  * Sanity check: it'd better be a real relation not, say, a subquery.
92                  * Else parser or rewriter messed up.
93                  */
94                 if (target_rte->rtekind != RTE_RELATION)
95                         elog(ERROR, "result relation must be a regular relation");
96
97                 target_relation = heap_open(target_rte->relid, NoLock);
98         }
99         else
100                 Assert(command_type == CMD_SELECT);
101
102         /*
103          * For UPDATE/DELETE, add any junk column(s) needed to allow the executor
104          * to identify the rows to be updated or deleted.  Note that this step
105          * scribbles on parse->targetList, which is not very desirable, but we
106          * keep it that way to avoid changing APIs used by FDWs.
107          */
108         if (command_type == CMD_UPDATE || command_type == CMD_DELETE)
109                 rewriteTargetListUD(parse, target_rte, target_relation);
110
111         /*
112          * for heap_form_tuple to work, the targetlist must match the exact order
113          * of the attributes. We also need to fill in any missing attributes. -ay
114          * 10/94
115          */
116         tlist = parse->targetList;
117         if (command_type == CMD_INSERT || command_type == CMD_UPDATE)
118                 tlist = expand_targetlist(tlist, command_type,
119                                                                   result_relation, target_relation);
120
121         /*
122          * Add necessary junk columns for rowmarked rels.  These values are needed
123          * for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual
124          * rechecking.  See comments for PlanRowMark in plannodes.h.
125          */
126         foreach(lc, root->rowMarks)
127         {
128                 PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
129                 Var                *var;
130                 char            resname[32];
131                 TargetEntry *tle;
132
133                 /* child rels use the same junk attrs as their parents */
134                 if (rc->rti != rc->prti)
135                         continue;
136
137                 if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
138                 {
139                         /* Need to fetch TID */
140                         var = makeVar(rc->rti,
141                                                   SelfItemPointerAttributeNumber,
142                                                   TIDOID,
143                                                   -1,
144                                                   InvalidOid,
145                                                   0);
146                         snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);
147                         tle = makeTargetEntry((Expr *) var,
148                                                                   list_length(tlist) + 1,
149                                                                   pstrdup(resname),
150                                                                   true);
151                         tlist = lappend(tlist, tle);
152                 }
153                 if (rc->allMarkTypes & (1 << ROW_MARK_COPY))
154                 {
155                         /* Need the whole row as a junk var */
156                         var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
157                                                                   rc->rti,
158                                                                   0,
159                                                                   false);
160                         snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
161                         tle = makeTargetEntry((Expr *) var,
162                                                                   list_length(tlist) + 1,
163                                                                   pstrdup(resname),
164                                                                   true);
165                         tlist = lappend(tlist, tle);
166                 }
167
168                 /* If parent of inheritance tree, always fetch the tableoid too. */
169                 if (rc->isParent)
170                 {
171                         var = makeVar(rc->rti,
172                                                   TableOidAttributeNumber,
173                                                   OIDOID,
174                                                   -1,
175                                                   InvalidOid,
176                                                   0);
177                         snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
178                         tle = makeTargetEntry((Expr *) var,
179                                                                   list_length(tlist) + 1,
180                                                                   pstrdup(resname),
181                                                                   true);
182                         tlist = lappend(tlist, tle);
183                 }
184         }
185
186         /*
187          * If the query has a RETURNING list, add resjunk entries for any Vars
188          * used in RETURNING that belong to other relations.  We need to do this
189          * to make these Vars available for the RETURNING calculation.  Vars that
190          * belong to the result rel don't need to be added, because they will be
191          * made to refer to the actual heap tuple.
192          */
193         if (parse->returningList && list_length(parse->rtable) > 1)
194         {
195                 List       *vars;
196                 ListCell   *l;
197
198                 vars = pull_var_clause((Node *) parse->returningList,
199                                                            PVC_RECURSE_AGGREGATES |
200                                                            PVC_RECURSE_WINDOWFUNCS |
201                                                            PVC_INCLUDE_PLACEHOLDERS);
202                 foreach(l, vars)
203                 {
204                         Var                *var = (Var *) lfirst(l);
205                         TargetEntry *tle;
206
207                         if (IsA(var, Var) &&
208                                 var->varno == result_relation)
209                                 continue;               /* don't need it */
210
211                         if (tlist_member((Expr *) var, tlist))
212                                 continue;               /* already got it */
213
214                         tle = makeTargetEntry((Expr *) var,
215                                                                   list_length(tlist) + 1,
216                                                                   NULL,
217                                                                   true);
218
219                         tlist = lappend(tlist, tle);
220                 }
221                 list_free(vars);
222         }
223
224         /*
225          * If there's an ON CONFLICT UPDATE clause, preprocess its targetlist too
226          * while we have the relation open.
227          */
228         if (parse->onConflict)
229                 parse->onConflict->onConflictSet =
230                         expand_targetlist(parse->onConflict->onConflictSet,
231                                                           CMD_UPDATE,
232                                                           result_relation,
233                                                           target_relation);
234
235         if (target_relation)
236                 heap_close(target_relation, NoLock);
237
238         return tlist;
239 }
240
241
242 /*****************************************************************************
243  *
244  *              TARGETLIST EXPANSION
245  *
246  *****************************************************************************/
247
248 /*
249  * expand_targetlist
250  *        Given a target list as generated by the parser and a result relation,
251  *        add targetlist entries for any missing attributes, and ensure the
252  *        non-junk attributes appear in proper field order.
253  */
254 static List *
255 expand_targetlist(List *tlist, int command_type,
256                                   Index result_relation, Relation rel)
257 {
258         List       *new_tlist = NIL;
259         ListCell   *tlist_item;
260         int                     attrno,
261                                 numattrs;
262
263         tlist_item = list_head(tlist);
264
265         /*
266          * The rewriter should have already ensured that the TLEs are in correct
267          * order; but we have to insert TLEs for any missing attributes.
268          *
269          * Scan the tuple description in the relation's relcache entry to make
270          * sure we have all the user attributes in the right order.
271          */
272         numattrs = RelationGetNumberOfAttributes(rel);
273
274         for (attrno = 1; attrno <= numattrs; attrno++)
275         {
276                 Form_pg_attribute att_tup = TupleDescAttr(rel->rd_att, attrno - 1);
277                 TargetEntry *new_tle = NULL;
278
279                 if (tlist_item != NULL)
280                 {
281                         TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
282
283                         if (!old_tle->resjunk && old_tle->resno == attrno)
284                         {
285                                 new_tle = old_tle;
286                                 tlist_item = lnext(tlist_item);
287                         }
288                 }
289
290                 if (new_tle == NULL)
291                 {
292                         /*
293                          * Didn't find a matching tlist entry, so make one.
294                          *
295                          * For INSERT, generate a NULL constant.  (We assume the rewriter
296                          * would have inserted any available default value.) Also, if the
297                          * column isn't dropped, apply any domain constraints that might
298                          * exist --- this is to catch domain NOT NULL.
299                          *
300                          * For UPDATE, generate a Var reference to the existing value of
301                          * the attribute, so that it gets copied to the new tuple. But
302                          * generate a NULL for dropped columns (we want to drop any old
303                          * values).
304                          *
305                          * When generating a NULL constant for a dropped column, we label
306                          * it INT4 (any other guaranteed-to-exist datatype would do as
307                          * well). We can't label it with the dropped column's datatype
308                          * since that might not exist anymore.  It does not really matter
309                          * what we claim the type is, since NULL is NULL --- its
310                          * representation is datatype-independent.  This could perhaps
311                          * confuse code comparing the finished plan to the target
312                          * relation, however.
313                          */
314                         Oid                     atttype = att_tup->atttypid;
315                         int32           atttypmod = att_tup->atttypmod;
316                         Oid                     attcollation = att_tup->attcollation;
317                         Node       *new_expr;
318
319                         switch (command_type)
320                         {
321                                 case CMD_INSERT:
322                                         if (!att_tup->attisdropped)
323                                         {
324                                                 new_expr = (Node *) makeConst(atttype,
325                                                                                                           -1,
326                                                                                                           attcollation,
327                                                                                                           att_tup->attlen,
328                                                                                                           (Datum) 0,
329                                                                                                           true, /* isnull */
330                                                                                                           att_tup->attbyval);
331                                                 new_expr = coerce_to_domain(new_expr,
332                                                                                                         InvalidOid, -1,
333                                                                                                         atttype,
334                                                                                                         COERCION_IMPLICIT,
335                                                                                                         COERCE_IMPLICIT_CAST,
336                                                                                                         -1,
337                                                                                                         false);
338                                         }
339                                         else
340                                         {
341                                                 /* Insert NULL for dropped column */
342                                                 new_expr = (Node *) makeConst(INT4OID,
343                                                                                                           -1,
344                                                                                                           InvalidOid,
345                                                                                                           sizeof(int32),
346                                                                                                           (Datum) 0,
347                                                                                                           true, /* isnull */
348                                                                                                           true /* byval */ );
349                                         }
350                                         break;
351                                 case CMD_UPDATE:
352                                         if (!att_tup->attisdropped)
353                                         {
354                                                 new_expr = (Node *) makeVar(result_relation,
355                                                                                                         attrno,
356                                                                                                         atttype,
357                                                                                                         atttypmod,
358                                                                                                         attcollation,
359                                                                                                         0);
360                                         }
361                                         else
362                                         {
363                                                 /* Insert NULL for dropped column */
364                                                 new_expr = (Node *) makeConst(INT4OID,
365                                                                                                           -1,
366                                                                                                           InvalidOid,
367                                                                                                           sizeof(int32),
368                                                                                                           (Datum) 0,
369                                                                                                           true, /* isnull */
370                                                                                                           true /* byval */ );
371                                         }
372                                         break;
373                                 default:
374                                         elog(ERROR, "unrecognized command_type: %d",
375                                                  (int) command_type);
376                                         new_expr = NULL;        /* keep compiler quiet */
377                                         break;
378                         }
379
380                         new_tle = makeTargetEntry((Expr *) new_expr,
381                                                                           attrno,
382                                                                           pstrdup(NameStr(att_tup->attname)),
383                                                                           false);
384                 }
385
386                 new_tlist = lappend(new_tlist, new_tle);
387         }
388
389         /*
390          * The remaining tlist entries should be resjunk; append them all to the
391          * end of the new tlist, making sure they have resnos higher than the last
392          * real attribute.  (Note: although the rewriter already did such
393          * renumbering, we have to do it again here in case we are doing an UPDATE
394          * in a table with dropped columns, or an inheritance child table with
395          * extra columns.)
396          */
397         while (tlist_item)
398         {
399                 TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
400
401                 if (!old_tle->resjunk)
402                         elog(ERROR, "targetlist is not sorted correctly");
403                 /* Get the resno right, but don't copy unnecessarily */
404                 if (old_tle->resno != attrno)
405                 {
406                         old_tle = flatCopyTargetEntry(old_tle);
407                         old_tle->resno = attrno;
408                 }
409                 new_tlist = lappend(new_tlist, old_tle);
410                 attrno++;
411                 tlist_item = lnext(tlist_item);
412         }
413
414         return new_tlist;
415 }
416
417
418 /*
419  * Locate PlanRowMark for given RT index, or return NULL if none
420  *
421  * This probably ought to be elsewhere, but there's no very good place
422  */
423 PlanRowMark *
424 get_plan_rowmark(List *rowmarks, Index rtindex)
425 {
426         ListCell   *l;
427
428         foreach(l, rowmarks)
429         {
430                 PlanRowMark *rc = (PlanRowMark *) lfirst(l);
431
432                 if (rc->rti == rtindex)
433                         return rc;
434         }
435         return NULL;
436 }