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/optimizer.h" |
48 | #include "optimizer/prep.h" |
49 | #include "optimizer/tlist.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 = table_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. If you |
125 | * change this stanza, see also expand_inherited_rtentry(), which has to |
126 | * be able to add on junk columns equivalent to these. |
127 | */ |
128 | foreach(lc, root->rowMarks) |
129 | { |
130 | PlanRowMark *rc = (PlanRowMark *) lfirst(lc); |
131 | Var *var; |
132 | char resname[32]; |
133 | TargetEntry *tle; |
134 | |
135 | /* child rels use the same junk attrs as their parents */ |
136 | if (rc->rti != rc->prti) |
137 | continue; |
138 | |
139 | if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY)) |
140 | { |
141 | /* Need to fetch TID */ |
142 | var = makeVar(rc->rti, |
143 | SelfItemPointerAttributeNumber, |
144 | TIDOID, |
145 | -1, |
146 | InvalidOid, |
147 | 0); |
148 | snprintf(resname, sizeof(resname), "ctid%u" , rc->rowmarkId); |
149 | tle = makeTargetEntry((Expr *) var, |
150 | list_length(tlist) + 1, |
151 | pstrdup(resname), |
152 | true); |
153 | tlist = lappend(tlist, tle); |
154 | } |
155 | if (rc->allMarkTypes & (1 << ROW_MARK_COPY)) |
156 | { |
157 | /* Need the whole row as a junk var */ |
158 | var = makeWholeRowVar(rt_fetch(rc->rti, range_table), |
159 | rc->rti, |
160 | 0, |
161 | false); |
162 | snprintf(resname, sizeof(resname), "wholerow%u" , rc->rowmarkId); |
163 | tle = makeTargetEntry((Expr *) var, |
164 | list_length(tlist) + 1, |
165 | pstrdup(resname), |
166 | true); |
167 | tlist = lappend(tlist, tle); |
168 | } |
169 | |
170 | /* If parent of inheritance tree, always fetch the tableoid too. */ |
171 | if (rc->isParent) |
172 | { |
173 | var = makeVar(rc->rti, |
174 | TableOidAttributeNumber, |
175 | OIDOID, |
176 | -1, |
177 | InvalidOid, |
178 | 0); |
179 | snprintf(resname, sizeof(resname), "tableoid%u" , rc->rowmarkId); |
180 | tle = makeTargetEntry((Expr *) var, |
181 | list_length(tlist) + 1, |
182 | pstrdup(resname), |
183 | true); |
184 | tlist = lappend(tlist, tle); |
185 | } |
186 | } |
187 | |
188 | /* |
189 | * If the query has a RETURNING list, add resjunk entries for any Vars |
190 | * used in RETURNING that belong to other relations. We need to do this |
191 | * to make these Vars available for the RETURNING calculation. Vars that |
192 | * belong to the result rel don't need to be added, because they will be |
193 | * made to refer to the actual heap tuple. |
194 | */ |
195 | if (parse->returningList && list_length(parse->rtable) > 1) |
196 | { |
197 | List *vars; |
198 | ListCell *l; |
199 | |
200 | vars = pull_var_clause((Node *) parse->returningList, |
201 | PVC_RECURSE_AGGREGATES | |
202 | PVC_RECURSE_WINDOWFUNCS | |
203 | PVC_INCLUDE_PLACEHOLDERS); |
204 | foreach(l, vars) |
205 | { |
206 | Var *var = (Var *) lfirst(l); |
207 | TargetEntry *tle; |
208 | |
209 | if (IsA(var, Var) && |
210 | var->varno == result_relation) |
211 | continue; /* don't need it */ |
212 | |
213 | if (tlist_member((Expr *) var, tlist)) |
214 | continue; /* already got it */ |
215 | |
216 | tle = makeTargetEntry((Expr *) var, |
217 | list_length(tlist) + 1, |
218 | NULL, |
219 | true); |
220 | |
221 | tlist = lappend(tlist, tle); |
222 | } |
223 | list_free(vars); |
224 | } |
225 | |
226 | /* |
227 | * If there's an ON CONFLICT UPDATE clause, preprocess its targetlist too |
228 | * while we have the relation open. |
229 | */ |
230 | if (parse->onConflict) |
231 | parse->onConflict->onConflictSet = |
232 | expand_targetlist(parse->onConflict->onConflictSet, |
233 | CMD_UPDATE, |
234 | result_relation, |
235 | target_relation); |
236 | |
237 | if (target_relation) |
238 | table_close(target_relation, NoLock); |
239 | |
240 | return tlist; |
241 | } |
242 | |
243 | |
244 | /***************************************************************************** |
245 | * |
246 | * TARGETLIST EXPANSION |
247 | * |
248 | *****************************************************************************/ |
249 | |
250 | /* |
251 | * expand_targetlist |
252 | * Given a target list as generated by the parser and a result relation, |
253 | * add targetlist entries for any missing attributes, and ensure the |
254 | * non-junk attributes appear in proper field order. |
255 | */ |
256 | static List * |
257 | expand_targetlist(List *tlist, int command_type, |
258 | Index result_relation, Relation rel) |
259 | { |
260 | List *new_tlist = NIL; |
261 | ListCell *tlist_item; |
262 | int attrno, |
263 | numattrs; |
264 | |
265 | tlist_item = list_head(tlist); |
266 | |
267 | /* |
268 | * The rewriter should have already ensured that the TLEs are in correct |
269 | * order; but we have to insert TLEs for any missing attributes. |
270 | * |
271 | * Scan the tuple description in the relation's relcache entry to make |
272 | * sure we have all the user attributes in the right order. |
273 | */ |
274 | numattrs = RelationGetNumberOfAttributes(rel); |
275 | |
276 | for (attrno = 1; attrno <= numattrs; attrno++) |
277 | { |
278 | Form_pg_attribute att_tup = TupleDescAttr(rel->rd_att, attrno - 1); |
279 | TargetEntry *new_tle = NULL; |
280 | |
281 | if (tlist_item != NULL) |
282 | { |
283 | TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item); |
284 | |
285 | if (!old_tle->resjunk && old_tle->resno == attrno) |
286 | { |
287 | new_tle = old_tle; |
288 | tlist_item = lnext(tlist_item); |
289 | } |
290 | } |
291 | |
292 | if (new_tle == NULL) |
293 | { |
294 | /* |
295 | * Didn't find a matching tlist entry, so make one. |
296 | * |
297 | * For INSERT, generate a NULL constant. (We assume the rewriter |
298 | * would have inserted any available default value.) Also, if the |
299 | * column isn't dropped, apply any domain constraints that might |
300 | * exist --- this is to catch domain NOT NULL. |
301 | * |
302 | * For UPDATE, generate a Var reference to the existing value of |
303 | * the attribute, so that it gets copied to the new tuple. But |
304 | * generate a NULL for dropped columns (we want to drop any old |
305 | * values). |
306 | * |
307 | * When generating a NULL constant for a dropped column, we label |
308 | * it INT4 (any other guaranteed-to-exist datatype would do as |
309 | * well). We can't label it with the dropped column's datatype |
310 | * since that might not exist anymore. It does not really matter |
311 | * what we claim the type is, since NULL is NULL --- its |
312 | * representation is datatype-independent. This could perhaps |
313 | * confuse code comparing the finished plan to the target |
314 | * relation, however. |
315 | */ |
316 | Oid atttype = att_tup->atttypid; |
317 | int32 atttypmod = att_tup->atttypmod; |
318 | Oid attcollation = att_tup->attcollation; |
319 | Node *new_expr; |
320 | |
321 | switch (command_type) |
322 | { |
323 | case CMD_INSERT: |
324 | if (!att_tup->attisdropped) |
325 | { |
326 | new_expr = (Node *) makeConst(atttype, |
327 | -1, |
328 | attcollation, |
329 | att_tup->attlen, |
330 | (Datum) 0, |
331 | true, /* isnull */ |
332 | att_tup->attbyval); |
333 | new_expr = coerce_to_domain(new_expr, |
334 | InvalidOid, -1, |
335 | atttype, |
336 | COERCION_IMPLICIT, |
337 | COERCE_IMPLICIT_CAST, |
338 | -1, |
339 | false); |
340 | } |
341 | else |
342 | { |
343 | /* Insert NULL for dropped column */ |
344 | new_expr = (Node *) makeConst(INT4OID, |
345 | -1, |
346 | InvalidOid, |
347 | sizeof(int32), |
348 | (Datum) 0, |
349 | true, /* isnull */ |
350 | true /* byval */ ); |
351 | } |
352 | break; |
353 | case CMD_UPDATE: |
354 | if (!att_tup->attisdropped) |
355 | { |
356 | new_expr = (Node *) makeVar(result_relation, |
357 | attrno, |
358 | atttype, |
359 | atttypmod, |
360 | attcollation, |
361 | 0); |
362 | } |
363 | else |
364 | { |
365 | /* Insert NULL for dropped column */ |
366 | new_expr = (Node *) makeConst(INT4OID, |
367 | -1, |
368 | InvalidOid, |
369 | sizeof(int32), |
370 | (Datum) 0, |
371 | true, /* isnull */ |
372 | true /* byval */ ); |
373 | } |
374 | break; |
375 | default: |
376 | elog(ERROR, "unrecognized command_type: %d" , |
377 | (int) command_type); |
378 | new_expr = NULL; /* keep compiler quiet */ |
379 | break; |
380 | } |
381 | |
382 | new_tle = makeTargetEntry((Expr *) new_expr, |
383 | attrno, |
384 | pstrdup(NameStr(att_tup->attname)), |
385 | false); |
386 | } |
387 | |
388 | new_tlist = lappend(new_tlist, new_tle); |
389 | } |
390 | |
391 | /* |
392 | * The remaining tlist entries should be resjunk; append them all to the |
393 | * end of the new tlist, making sure they have resnos higher than the last |
394 | * real attribute. (Note: although the rewriter already did such |
395 | * renumbering, we have to do it again here in case we are doing an UPDATE |
396 | * in a table with dropped columns, or an inheritance child table with |
397 | * extra columns.) |
398 | */ |
399 | while (tlist_item) |
400 | { |
401 | TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item); |
402 | |
403 | if (!old_tle->resjunk) |
404 | elog(ERROR, "targetlist is not sorted correctly" ); |
405 | /* Get the resno right, but don't copy unnecessarily */ |
406 | if (old_tle->resno != attrno) |
407 | { |
408 | old_tle = flatCopyTargetEntry(old_tle); |
409 | old_tle->resno = attrno; |
410 | } |
411 | new_tlist = lappend(new_tlist, old_tle); |
412 | attrno++; |
413 | tlist_item = lnext(tlist_item); |
414 | } |
415 | |
416 | return new_tlist; |
417 | } |
418 | |
419 | |
420 | /* |
421 | * Locate PlanRowMark for given RT index, or return NULL if none |
422 | * |
423 | * This probably ought to be elsewhere, but there's no very good place |
424 | */ |
425 | PlanRowMark * |
426 | get_plan_rowmark(List *rowmarks, Index rtindex) |
427 | { |
428 | ListCell *l; |
429 | |
430 | foreach(l, rowmarks) |
431 | { |
432 | PlanRowMark *rc = (PlanRowMark *) lfirst(l); |
433 | |
434 | if (rc->rti == rtindex) |
435 | return rc; |
436 | } |
437 | return NULL; |
438 | } |
439 | |