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