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