]> granicus.if.org Git - postgresql/blob - src/backend/optimizer/prep/preptlist.c
Get rid of some old and crufty global variables in the planner. When
[postgresql] / src / backend / optimizer / prep / preptlist.c
1 /*-------------------------------------------------------------------------
2  *
3  * preptlist.c
4  *        Routines to preprocess the parse tree target list
5  *
6  * This module takes care of altering the query targetlist as needed for
7  * INSERT, UPDATE, and DELETE queries.  For INSERT and UPDATE queries,
8  * the targetlist must contain an entry for each attribute of the target
9  * relation in the correct order.  For both UPDATE and DELETE queries,
10  * we need a junk targetlist entry holding the CTID attribute --- the
11  * executor relies on this to find the tuple to be replaced/deleted.
12  * We may also need junk tlist entries for Vars used in the RETURNING list.
13  *
14  *
15  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
16  * Portions Copyright (c) 1994, Regents of the University of California
17  *
18  * IDENTIFICATION
19  *        $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.86 2007/02/19 07:03:30 tgl Exp $
20  *
21  *-------------------------------------------------------------------------
22  */
23
24 #include "postgres.h"
25
26 #include "access/heapam.h"
27 #include "catalog/pg_type.h"
28 #include "nodes/makefuncs.h"
29 #include "optimizer/prep.h"
30 #include "optimizer/subselect.h"
31 #include "optimizer/tlist.h"
32 #include "optimizer/var.h"
33 #include "parser/analyze.h"
34 #include "parser/parsetree.h"
35 #include "parser/parse_coerce.h"
36
37
38 static List *expand_targetlist(List *tlist, int command_type,
39                                   Index result_relation, List *range_table);
40
41
42 /*
43  * preprocess_targetlist
44  *        Driver for preprocessing the parse tree targetlist.
45  *
46  *        Returns the new targetlist.
47  */
48 List *
49 preprocess_targetlist(PlannerInfo *root, List *tlist)
50 {
51         Query      *parse = root->parse;
52         int                     result_relation = parse->resultRelation;
53         List       *range_table = parse->rtable;
54         CmdType         command_type = parse->commandType;
55
56         /*
57          * Sanity check: if there is a result relation, it'd better be a real
58          * relation not a subquery.  Else parser or rewriter messed up.
59          */
60         if (result_relation)
61         {
62                 RangeTblEntry *rte = rt_fetch(result_relation, range_table);
63
64                 if (rte->subquery != NULL || rte->relid == InvalidOid)
65                         elog(ERROR, "subquery cannot be result relation");
66         }
67
68         /*
69          * for heap_formtuple to work, the targetlist must match the exact order
70          * of the attributes. We also need to fill in any missing attributes. -ay
71          * 10/94
72          */
73         if (command_type == CMD_INSERT || command_type == CMD_UPDATE)
74                 tlist = expand_targetlist(tlist, command_type,
75                                                                   result_relation, range_table);
76
77         /*
78          * for "update" and "delete" queries, add ctid of the result relation into
79          * the target list so that the ctid will propagate through execution and
80          * ExecutePlan() will be able to identify the right tuple to replace or
81          * delete.      This extra field is marked "junk" so that it is not stored
82          * back into the tuple.
83          */
84         if (command_type == CMD_UPDATE || command_type == CMD_DELETE)
85         {
86                 TargetEntry *tle;
87                 Var                *var;
88
89                 var = makeVar(result_relation, SelfItemPointerAttributeNumber,
90                                           TIDOID, -1, 0);
91
92                 tle = makeTargetEntry((Expr *) var,
93                                                           list_length(tlist) + 1,
94                                                           pstrdup("ctid"),
95                                                           true);
96
97                 /*
98                  * For an UPDATE, expand_targetlist already created a fresh tlist. For
99                  * DELETE, better do a listCopy so that we don't destructively modify
100                  * the original tlist (is this really necessary?).
101                  */
102                 if (command_type == CMD_DELETE)
103                         tlist = list_copy(tlist);
104
105                 tlist = lappend(tlist, tle);
106         }
107
108         /*
109          * Add TID targets for rels selected FOR UPDATE/SHARE.  The executor uses
110          * the TID to know which rows to lock, much as for UPDATE or DELETE.
111          */
112         if (parse->rowMarks)
113         {
114                 ListCell   *l;
115
116                 /*
117                  * We've got trouble if the FOR UPDATE/SHARE appears inside grouping,
118                  * since grouping renders a reference to individual tuple CTIDs
119                  * invalid.  This is also checked at parse time, but that's
120                  * insufficient because of rule substitution, query pullup, etc.
121                  */
122                 CheckSelectLocking(parse);
123
124                 /*
125                  * Currently the executor only supports FOR UPDATE/SHARE at top level
126                  */
127                 if (root->query_level > 1)
128                         ereport(ERROR,
129                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
130                         errmsg("SELECT FOR UPDATE/SHARE is not allowed in subqueries")));
131
132                 foreach(l, parse->rowMarks)
133                 {
134                         RowMarkClause *rc = (RowMarkClause *) lfirst(l);
135                         Var                *var;
136                         char       *resname;
137                         TargetEntry *tle;
138
139                         var = makeVar(rc->rti,
140                                                   SelfItemPointerAttributeNumber,
141                                                   TIDOID,
142                                                   -1,
143                                                   0);
144
145                         resname = (char *) palloc(32);
146                         snprintf(resname, 32, "ctid%u", rc->rti);
147
148                         tle = makeTargetEntry((Expr *) var,
149                                                                   list_length(tlist) + 1,
150                                                                   resname,
151                                                                   true);
152
153                         tlist = lappend(tlist, tle);
154                 }
155         }
156
157         /*
158          * If the query has a RETURNING list, add resjunk entries for any Vars
159          * used in RETURNING that belong to other relations.  We need to do this
160          * to make these Vars available for the RETURNING calculation.  Vars that
161          * belong to the result rel don't need to be added, because they will be
162          * made to refer to the actual heap tuple.
163          */
164         if (parse->returningList && list_length(parse->rtable) > 1)
165         {
166                 List       *vars;
167                 ListCell   *l;
168
169                 vars = pull_var_clause((Node *) parse->returningList, false);
170                 foreach(l, vars)
171                 {
172                         Var                *var = (Var *) lfirst(l);
173                         TargetEntry *tle;
174
175                         if (var->varno == result_relation)
176                                 continue;               /* don't need it */
177
178                         if (tlist_member((Node *) var, tlist))
179                                 continue;               /* already got it */
180
181                         tle = makeTargetEntry((Expr *) var,
182                                                                   list_length(tlist) + 1,
183                                                                   NULL,
184                                                                   true);
185
186                         tlist = lappend(tlist, tle);
187                 }
188                 list_free(vars);
189         }
190
191         return tlist;
192 }
193
194 /*****************************************************************************
195  *
196  *              TARGETLIST EXPANSION
197  *
198  *****************************************************************************/
199
200 /*
201  * expand_targetlist
202  *        Given a target list as generated by the parser and a result relation,
203  *        add targetlist entries for any missing attributes, and ensure the
204  *        non-junk attributes appear in proper field order.
205  *
206  * NOTE: if you are tempted to put more processing here, consider whether
207  * it shouldn't go in the rewriter's rewriteTargetList() instead.
208  */
209 static List *
210 expand_targetlist(List *tlist, int command_type,
211                                   Index result_relation, List *range_table)
212 {
213         List       *new_tlist = NIL;
214         ListCell   *tlist_item;
215         Relation        rel;
216         int                     attrno,
217                                 numattrs;
218
219         tlist_item = list_head(tlist);
220
221         /*
222          * The rewriter should have already ensured that the TLEs are in correct
223          * order; but we have to insert TLEs for any missing attributes.
224          *
225          * Scan the tuple description in the relation's relcache entry to make
226          * sure we have all the user attributes in the right order.  We assume
227          * that the rewriter already acquired at least AccessShareLock on the
228          * relation, so we need no lock here.
229          */
230         rel = heap_open(getrelid(result_relation, range_table), NoLock);
231
232         numattrs = RelationGetNumberOfAttributes(rel);
233
234         for (attrno = 1; attrno <= numattrs; attrno++)
235         {
236                 Form_pg_attribute att_tup = rel->rd_att->attrs[attrno - 1];
237                 TargetEntry *new_tle = NULL;
238
239                 if (tlist_item != NULL)
240                 {
241                         TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
242
243                         if (!old_tle->resjunk && old_tle->resno == attrno)
244                         {
245                                 new_tle = old_tle;
246                                 tlist_item = lnext(tlist_item);
247                         }
248                 }
249
250                 if (new_tle == NULL)
251                 {
252                         /*
253                          * Didn't find a matching tlist entry, so make one.
254                          *
255                          * For INSERT, generate a NULL constant.  (We assume the rewriter
256                          * would have inserted any available default value.) Also, if the
257                          * column isn't dropped, apply any domain constraints that might
258                          * exist --- this is to catch domain NOT NULL.
259                          *
260                          * For UPDATE, generate a Var reference to the existing value of
261                          * the attribute, so that it gets copied to the new tuple. But
262                          * generate a NULL for dropped columns (we want to drop any old
263                          * values).
264                          *
265                          * When generating a NULL constant for a dropped column, we label
266                          * it INT4 (any other guaranteed-to-exist datatype would do as
267                          * well). We can't label it with the dropped column's datatype
268                          * since that might not exist anymore.  It does not really matter
269                          * what we claim the type is, since NULL is NULL --- its
270                          * representation is datatype-independent.      This could perhaps
271                          * confuse code comparing the finished plan to the target
272                          * relation, however.
273                          */
274                         Oid                     atttype = att_tup->atttypid;
275                         int32           atttypmod = att_tup->atttypmod;
276                         Node       *new_expr;
277
278                         switch (command_type)
279                         {
280                                 case CMD_INSERT:
281                                         if (!att_tup->attisdropped)
282                                         {
283                                                 new_expr = (Node *) makeConst(atttype,
284                                                                                                           att_tup->attlen,
285                                                                                                           (Datum) 0,
286                                                                                                           true,         /* isnull */
287                                                                                                           att_tup->attbyval);
288                                                 new_expr = coerce_to_domain(new_expr,
289                                                                                                         InvalidOid, -1,
290                                                                                                         atttype,
291                                                                                                         COERCE_IMPLICIT_CAST,
292                                                                                                         false,
293                                                                                                         false);
294                                         }
295                                         else
296                                         {
297                                                 /* Insert NULL for dropped column */
298                                                 new_expr = (Node *) makeConst(INT4OID,
299                                                                                                           sizeof(int32),
300                                                                                                           (Datum) 0,
301                                                                                                           true,         /* isnull */
302                                                                                                           true /* byval */ );
303                                         }
304                                         break;
305                                 case CMD_UPDATE:
306                                         if (!att_tup->attisdropped)
307                                         {
308                                                 new_expr = (Node *) makeVar(result_relation,
309                                                                                                         attrno,
310                                                                                                         atttype,
311                                                                                                         atttypmod,
312                                                                                                         0);
313                                         }
314                                         else
315                                         {
316                                                 /* Insert NULL for dropped column */
317                                                 new_expr = (Node *) makeConst(INT4OID,
318                                                                                                           sizeof(int32),
319                                                                                                           (Datum) 0,
320                                                                                                           true,         /* isnull */
321                                                                                                           true /* byval */ );
322                                         }
323                                         break;
324                                 default:
325                                         elog(ERROR, "unrecognized command_type: %d",
326                                                  (int) command_type);
327                                         new_expr = NULL;        /* keep compiler quiet */
328                                         break;
329                         }
330
331                         new_tle = makeTargetEntry((Expr *) new_expr,
332                                                                           attrno,
333                                                                           pstrdup(NameStr(att_tup->attname)),
334                                                                           false);
335                 }
336
337                 new_tlist = lappend(new_tlist, new_tle);
338         }
339
340         /*
341          * The remaining tlist entries should be resjunk; append them all to the
342          * end of the new tlist, making sure they have resnos higher than the last
343          * real attribute.      (Note: although the rewriter already did such
344          * renumbering, we have to do it again here in case we are doing an UPDATE
345          * in a table with dropped columns, or an inheritance child table with
346          * extra columns.)
347          */
348         while (tlist_item)
349         {
350                 TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
351
352                 if (!old_tle->resjunk)
353                         elog(ERROR, "targetlist is not sorted correctly");
354                 /* Get the resno right, but don't copy unnecessarily */
355                 if (old_tle->resno != attrno)
356                 {
357                         old_tle = flatCopyTargetEntry(old_tle);
358                         old_tle->resno = attrno;
359                 }
360                 new_tlist = lappend(new_tlist, old_tle);
361                 attrno++;
362                 tlist_item = lnext(tlist_item);
363         }
364
365         heap_close(rel, NoLock);
366
367         return new_tlist;
368 }