OSDN Git Service

Update copyright to 2004.
[pg-rex/syncrep.git] / 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  *
13  *
14  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
15  * Portions Copyright (c) 1994, Regents of the University of California
16  *
17  * IDENTIFICATION
18  *        $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.70 2004/08/29 04:12:34 momjian Exp $
19  *
20  *-------------------------------------------------------------------------
21  */
22
23 #include "postgres.h"
24
25 #include "access/heapam.h"
26 #include "catalog/pg_type.h"
27 #include "nodes/makefuncs.h"
28 #include "optimizer/prep.h"
29 #include "parser/parsetree.h"
30 #include "parser/parse_coerce.h"
31
32
33 static List *expand_targetlist(List *tlist, int command_type,
34                                   Index result_relation, List *range_table);
35
36
37 /*
38  * preprocess_targetlist
39  *        Driver for preprocessing the parse tree targetlist.
40  *
41  *        Returns the new targetlist.
42  */
43 List *
44 preprocess_targetlist(List *tlist,
45                                           int command_type,
46                                           Index result_relation,
47                                           List *range_table)
48 {
49         /*
50          * Sanity check: if there is a result relation, it'd better be a real
51          * relation not a subquery.  Else parser or rewriter messed up.
52          */
53         if (result_relation)
54         {
55                 RangeTblEntry *rte = rt_fetch(result_relation, range_table);
56
57                 if (rte->subquery != NULL || rte->relid == InvalidOid)
58                         elog(ERROR, "subquery cannot be result relation");
59         }
60
61         /*
62          * for heap_formtuple to work, the targetlist must match the exact
63          * order of the attributes. We also need to fill in any missing
64          * attributes.                                                  -ay 10/94
65          */
66         if (command_type == CMD_INSERT || command_type == CMD_UPDATE)
67                 tlist = expand_targetlist(tlist, command_type,
68                                                                   result_relation, range_table);
69
70         /*
71          * for "update" and "delete" queries, add ctid of the result relation
72          * into the target list so that the ctid will propagate through
73          * execution and ExecutePlan() will be able to identify the right
74          * tuple to replace or delete.  This extra field is marked "junk" so
75          * that it is not stored back into the tuple.
76          */
77         if (command_type == CMD_UPDATE || command_type == CMD_DELETE)
78         {
79                 Resdom     *resdom;
80                 Var                *var;
81
82                 resdom = makeResdom(list_length(tlist) + 1,
83                                                         TIDOID,
84                                                         -1,
85                                                         pstrdup("ctid"),
86                                                         true);
87
88                 var = makeVar(result_relation, SelfItemPointerAttributeNumber,
89                                           TIDOID, -1, 0);
90
91                 /*
92                  * For an UPDATE, expand_targetlist already created a fresh tlist.
93                  * For DELETE, better do a listCopy so that we don't destructively
94                  * modify the original tlist (is this really necessary?).
95                  */
96                 if (command_type == CMD_DELETE)
97                         tlist = list_copy(tlist);
98
99                 tlist = lappend(tlist, makeTargetEntry(resdom, (Expr *) var));
100         }
101
102         return tlist;
103 }
104
105 /*****************************************************************************
106  *
107  *              TARGETLIST EXPANSION
108  *
109  *****************************************************************************/
110
111 /*
112  * expand_targetlist
113  *        Given a target list as generated by the parser and a result relation,
114  *        add targetlist entries for any missing attributes, and ensure the
115  *        non-junk attributes appear in proper field order.
116  *
117  * NOTE: if you are tempted to put more processing here, consider whether
118  * it shouldn't go in the rewriter's rewriteTargetList() instead.
119  */
120 static List *
121 expand_targetlist(List *tlist, int command_type,
122                                   Index result_relation, List *range_table)
123 {
124         List       *new_tlist = NIL;
125         ListCell   *tlist_item;
126         Relation        rel;
127         int                     attrno,
128                                 numattrs;
129
130         tlist_item = list_head(tlist);
131
132         /*
133          * The rewriter should have already ensured that the TLEs are in
134          * correct order; but we have to insert TLEs for any missing
135          * attributes.
136          *
137          * Scan the tuple description in the relation's relcache entry to make
138          * sure we have all the user attributes in the right order.
139          */
140         rel = heap_open(getrelid(result_relation, range_table), AccessShareLock);
141
142         numattrs = RelationGetNumberOfAttributes(rel);
143
144         for (attrno = 1; attrno <= numattrs; attrno++)
145         {
146                 Form_pg_attribute att_tup = rel->rd_att->attrs[attrno - 1];
147                 TargetEntry *new_tle = NULL;
148
149                 if (tlist_item != NULL)
150                 {
151                         TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
152                         Resdom     *resdom = old_tle->resdom;
153
154                         if (!resdom->resjunk && resdom->resno == attrno)
155                         {
156                                 new_tle = old_tle;
157                                 tlist_item = lnext(tlist_item);
158                         }
159                 }
160
161                 if (new_tle == NULL)
162                 {
163                         /*
164                          * Didn't find a matching tlist entry, so make one.
165                          *
166                          * For INSERT, generate a NULL constant.  (We assume the rewriter
167                          * would have inserted any available default value.) Also, if
168                          * the column isn't dropped, apply any domain constraints that
169                          * might exist --- this is to catch domain NOT NULL.
170                          *
171                          * For UPDATE, generate a Var reference to the existing value of
172                          * the attribute, so that it gets copied to the new tuple. But
173                          * generate a NULL for dropped columns (we want to drop any
174                          * old values).
175                          *
176                          * When generating a NULL constant for a dropped column, we label
177                          * it INT4 (any other guaranteed-to-exist datatype would do as
178                          * well).  We can't label it with the dropped column's
179                          * datatype since that might not exist anymore.  It does not
180                          * really matter what we claim the type is, since NULL is NULL
181                          * --- its representation is datatype-independent.      This could
182                          * perhaps confuse code comparing the finished plan to the
183                          * target relation, however.
184                          */
185                         Oid                     atttype = att_tup->atttypid;
186                         int32           atttypmod = att_tup->atttypmod;
187                         Node       *new_expr;
188
189                         switch (command_type)
190                         {
191                                 case CMD_INSERT:
192                                         if (!att_tup->attisdropped)
193                                         {
194                                                 new_expr = (Node *) makeConst(atttype,
195                                                                                                           att_tup->attlen,
196                                                                                                           (Datum) 0,
197                                                                                                           true,         /* isnull */
198                                                                                                           att_tup->attbyval);
199                                                 new_expr = coerce_to_domain(new_expr,
200                                                                                                         InvalidOid,
201                                                                                                         atttype,
202                                                                                                         COERCE_IMPLICIT_CAST,
203                                                                                                         false);
204                                         }
205                                         else
206                                         {
207                                                 /* Insert NULL for dropped column */
208                                                 new_expr = (Node *) makeConst(INT4OID,
209                                                                                                           sizeof(int32),
210                                                                                                           (Datum) 0,
211                                                                                                           true,         /* isnull */
212                                                                                                           true /* byval */ );
213                                                 /* label resdom with INT4, too */
214                                                 atttype = INT4OID;
215                                                 atttypmod = -1;
216                                         }
217                                         break;
218                                 case CMD_UPDATE:
219                                         if (!att_tup->attisdropped)
220                                         {
221                                                 new_expr = (Node *) makeVar(result_relation,
222                                                                                                         attrno,
223                                                                                                         atttype,
224                                                                                                         atttypmod,
225                                                                                                         0);
226                                         }
227                                         else
228                                         {
229                                                 /* Insert NULL for dropped column */
230                                                 new_expr = (Node *) makeConst(INT4OID,
231                                                                                                           sizeof(int32),
232                                                                                                           (Datum) 0,
233                                                                                                           true,         /* isnull */
234                                                                                                           true /* byval */ );
235                                                 /* label resdom with INT4, too */
236                                                 atttype = INT4OID;
237                                                 atttypmod = -1;
238                                         }
239                                         break;
240                                 default:
241                                         elog(ERROR, "unrecognized command_type: %d",
242                                                  (int) command_type);
243                                         new_expr = NULL;        /* keep compiler quiet */
244                                         break;
245                         }
246
247                         new_tle = makeTargetEntry(makeResdom(attrno,
248                                                                                                  atttype,
249                                                                                                  atttypmod,
250                                                                           pstrdup(NameStr(att_tup->attname)),
251                                                                                                  false),
252                                                                           (Expr *) new_expr);
253                 }
254
255                 new_tlist = lappend(new_tlist, new_tle);
256         }
257
258         /*
259          * The remaining tlist entries should be resjunk; append them all to
260          * the end of the new tlist, making sure they have resnos higher than
261          * the last real attribute.  (Note: although the rewriter already did
262          * such renumbering, we have to do it again here in case we are doing
263          * an UPDATE in a table with dropped columns, or an inheritance child
264          * table with extra columns.)
265          */
266         while (tlist_item)
267         {
268                 TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
269                 Resdom     *resdom = old_tle->resdom;
270
271                 if (!resdom->resjunk)
272                         elog(ERROR, "targetlist is not sorted correctly");
273                 /* Get the resno right, but don't copy unnecessarily */
274                 if (resdom->resno != attrno)
275                 {
276                         resdom = (Resdom *) copyObject((Node *) resdom);
277                         resdom->resno = attrno;
278                         old_tle = makeTargetEntry(resdom, old_tle->expr);
279                 }
280                 new_tlist = lappend(new_tlist, old_tle);
281                 attrno++;
282                 tlist_item = lnext(tlist_item);
283         }
284
285         heap_close(rel, AccessShareLock);
286
287         return new_tlist;
288 }