1 /*-------------------------------------------------------------------------
4 * Routines to preprocess the parse tree target list
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.
14 * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
15 * Portions Copyright (c) 1994, Regents of the University of California
18 * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.70 2004/08/29 04:12:34 momjian Exp $
20 *-------------------------------------------------------------------------
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"
33 static List *expand_targetlist(List *tlist, int command_type,
34 Index result_relation, List *range_table);
38 * preprocess_targetlist
39 * Driver for preprocessing the parse tree targetlist.
41 * Returns the new targetlist.
44 preprocess_targetlist(List *tlist,
46 Index result_relation,
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.
55 RangeTblEntry *rte = rt_fetch(result_relation, range_table);
57 if (rte->subquery != NULL || rte->relid == InvalidOid)
58 elog(ERROR, "subquery cannot be result relation");
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
66 if (command_type == CMD_INSERT || command_type == CMD_UPDATE)
67 tlist = expand_targetlist(tlist, command_type,
68 result_relation, range_table);
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.
77 if (command_type == CMD_UPDATE || command_type == CMD_DELETE)
82 resdom = makeResdom(list_length(tlist) + 1,
88 var = makeVar(result_relation, SelfItemPointerAttributeNumber,
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?).
96 if (command_type == CMD_DELETE)
97 tlist = list_copy(tlist);
99 tlist = lappend(tlist, makeTargetEntry(resdom, (Expr *) var));
105 /*****************************************************************************
107 * TARGETLIST EXPANSION
109 *****************************************************************************/
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.
117 * NOTE: if you are tempted to put more processing here, consider whether
118 * it shouldn't go in the rewriter's rewriteTargetList() instead.
121 expand_targetlist(List *tlist, int command_type,
122 Index result_relation, List *range_table)
124 List *new_tlist = NIL;
125 ListCell *tlist_item;
130 tlist_item = list_head(tlist);
133 * The rewriter should have already ensured that the TLEs are in
134 * correct order; but we have to insert TLEs for any missing
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.
140 rel = heap_open(getrelid(result_relation, range_table), AccessShareLock);
142 numattrs = RelationGetNumberOfAttributes(rel);
144 for (attrno = 1; attrno <= numattrs; attrno++)
146 Form_pg_attribute att_tup = rel->rd_att->attrs[attrno - 1];
147 TargetEntry *new_tle = NULL;
149 if (tlist_item != NULL)
151 TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
152 Resdom *resdom = old_tle->resdom;
154 if (!resdom->resjunk && resdom->resno == attrno)
157 tlist_item = lnext(tlist_item);
164 * Didn't find a matching tlist entry, so make one.
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.
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
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.
185 Oid atttype = att_tup->atttypid;
186 int32 atttypmod = att_tup->atttypmod;
189 switch (command_type)
192 if (!att_tup->attisdropped)
194 new_expr = (Node *) makeConst(atttype,
199 new_expr = coerce_to_domain(new_expr,
202 COERCE_IMPLICIT_CAST,
207 /* Insert NULL for dropped column */
208 new_expr = (Node *) makeConst(INT4OID,
213 /* label resdom with INT4, too */
219 if (!att_tup->attisdropped)
221 new_expr = (Node *) makeVar(result_relation,
229 /* Insert NULL for dropped column */
230 new_expr = (Node *) makeConst(INT4OID,
235 /* label resdom with INT4, too */
241 elog(ERROR, "unrecognized command_type: %d",
243 new_expr = NULL; /* keep compiler quiet */
247 new_tle = makeTargetEntry(makeResdom(attrno,
250 pstrdup(NameStr(att_tup->attname)),
255 new_tlist = lappend(new_tlist, new_tle);
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.)
268 TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);
269 Resdom *resdom = old_tle->resdom;
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)
276 resdom = (Resdom *) copyObject((Node *) resdom);
277 resdom->resno = attrno;
278 old_tle = makeTargetEntry(resdom, old_tle->expr);
280 new_tlist = lappend(new_tlist, old_tle);
282 tlist_item = lnext(tlist_item);
285 heap_close(rel, AccessShareLock);