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 *
preprocess_targetlist(PlannerInfo * root)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 *
expand_targetlist(List * tlist,int command_type,Index result_relation,Relation rel)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 *
get_plan_rowmark(List * rowmarks,Index rtindex)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