1 /*-------------------------------------------------------------------------
2  *
3  * tupconvert.c
4  *	  Tuple conversion support.
5  *
6  * These functions provide conversion between rowtypes that are logically
7  * equivalent but might have columns in a different order or different sets
8  * of dropped columns.  There is some overlap of functionality with the
9  * executor's "junkfilter" routines, but these functions work on bare
10  * HeapTuples rather than TupleTableSlots.
11  *
12  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
13  * Portions Copyright (c) 1994, Regents of the University of California
14  *
15  *
16  * IDENTIFICATION
17  *	  src/backend/access/common/tupconvert.c
18  *
19  *-------------------------------------------------------------------------
20  */
21 #include "postgres.h"
22 
23 #include "access/htup_details.h"
24 #include "access/sysattr.h"
25 #include "access/tupconvert.h"
26 #include "utils/builtins.h"
27 
28 
29 /*
30  * The conversion setup routines have the following common API:
31  *
32  * The setup routine checks whether the given source and destination tuple
33  * descriptors are logically compatible.  If not, it throws an error.
34  * If so, it returns NULL if they are physically compatible (ie, no conversion
35  * is needed), else a TupleConversionMap that can be used by do_convert_tuple
36  * to perform the conversion.
37  *
38  * The TupleConversionMap, if needed, is palloc'd in the caller's memory
39  * context.  Also, the given tuple descriptors are referenced by the map,
40  * so they must survive as long as the map is needed.
41  *
42  * The caller must supply a suitable primary error message to be used if
43  * a compatibility error is thrown.  Recommended coding practice is to use
44  * gettext_noop() on this string, so that it is translatable but won't
45  * actually be translated unless the error gets thrown.
46  *
47  *
48  * Implementation notes:
49  *
50  * The key component of a TupleConversionMap is an attrMap[] array with
51  * one entry per output column.  This entry contains the 1-based index of
52  * the corresponding input column, or zero to force a NULL value (for
53  * a dropped output column).  The TupleConversionMap also contains workspace
54  * arrays.
55  */
56 
57 
58 /*
59  * Set up for tuple conversion, matching input and output columns by
60  * position.  (Dropped columns are ignored in both input and output.)
61  *
62  * Note: the errdetail messages speak of indesc as the "returned" rowtype,
63  * outdesc as the "expected" rowtype.  This is okay for current uses but
64  * might need generalization in future.
65  */
66 TupleConversionMap *
convert_tuples_by_position(TupleDesc indesc,TupleDesc outdesc,const char * msg)67 convert_tuples_by_position(TupleDesc indesc,
68 						   TupleDesc outdesc,
69 						   const char *msg)
70 {
71 	TupleConversionMap *map;
72 	AttrNumber *attrMap;
73 	int			nincols;
74 	int			noutcols;
75 	int			n;
76 	int			i;
77 	int			j;
78 	bool		same;
79 
80 	/* Verify compatibility and prepare attribute-number map */
81 	n = outdesc->natts;
82 	attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
83 	j = 0;						/* j is next physical input attribute */
84 	nincols = noutcols = 0;		/* these count non-dropped attributes */
85 	same = true;
86 	for (i = 0; i < n; i++)
87 	{
88 		Form_pg_attribute att = TupleDescAttr(outdesc, i);
89 		Oid			atttypid;
90 		int32		atttypmod;
91 
92 		if (att->attisdropped)
93 			continue;			/* attrMap[i] is already 0 */
94 		noutcols++;
95 		atttypid = att->atttypid;
96 		atttypmod = att->atttypmod;
97 		for (; j < indesc->natts; j++)
98 		{
99 			att = TupleDescAttr(indesc, j);
100 			if (att->attisdropped)
101 				continue;
102 			nincols++;
103 			/* Found matching column, check type */
104 			if (atttypid != att->atttypid ||
105 				(atttypmod != att->atttypmod && atttypmod >= 0))
106 				ereport(ERROR,
107 						(errcode(ERRCODE_DATATYPE_MISMATCH),
108 						 errmsg_internal("%s", _(msg)),
109 						 errdetail("Returned type %s does not match expected type %s in column %d.",
110 								   format_type_with_typemod(att->atttypid,
111 															att->atttypmod),
112 								   format_type_with_typemod(atttypid,
113 															atttypmod),
114 								   noutcols)));
115 			attrMap[i] = (AttrNumber) (j + 1);
116 			j++;
117 			break;
118 		}
119 		if (attrMap[i] == 0)
120 			same = false;		/* we'll complain below */
121 	}
122 
123 	/* Check for unused input columns */
124 	for (; j < indesc->natts; j++)
125 	{
126 		if (TupleDescAttr(indesc, j)->attisdropped)
127 			continue;
128 		nincols++;
129 		same = false;			/* we'll complain below */
130 	}
131 
132 	/* Report column count mismatch using the non-dropped-column counts */
133 	if (!same)
134 		ereport(ERROR,
135 				(errcode(ERRCODE_DATATYPE_MISMATCH),
136 				 errmsg_internal("%s", _(msg)),
137 				 errdetail("Number of returned columns (%d) does not match "
138 						   "expected column count (%d).",
139 						   nincols, noutcols)));
140 
141 	/*
142 	 * Check to see if the map is one-to-one, in which case we need not do a
143 	 * tuple conversion.  We must also insist that both tupdescs either
144 	 * specify or don't specify an OID column, else we need a conversion to
145 	 * add/remove space for that.  (For some callers, presence or absence of
146 	 * an OID column perhaps would not really matter, but let's be safe.)
147 	 */
148 	if (indesc->natts == outdesc->natts &&
149 		indesc->tdhasoid == outdesc->tdhasoid)
150 	{
151 		for (i = 0; i < n; i++)
152 		{
153 			Form_pg_attribute inatt = TupleDescAttr(indesc, i);
154 			Form_pg_attribute outatt = TupleDescAttr(outdesc, i);
155 
156 			/*
157 			 * If the input column has a missing attribute, we need a
158 			 * conversion.
159 			 */
160 			if (inatt->atthasmissing)
161 			{
162 				same = false;
163 				break;
164 			}
165 
166 			if (attrMap[i] == (i + 1))
167 				continue;
168 
169 			/*
170 			 * If it's a dropped column and the corresponding input column is
171 			 * also dropped, we needn't convert.  However, attlen and attalign
172 			 * must agree.
173 			 */
174 			if (attrMap[i] == 0 &&
175 				inatt->attisdropped &&
176 				inatt->attlen == outatt->attlen &&
177 				inatt->attalign == outatt->attalign)
178 				continue;
179 
180 			same = false;
181 			break;
182 		}
183 	}
184 	else
185 		same = false;
186 
187 	if (same)
188 	{
189 		/* Runtime conversion is not needed */
190 		pfree(attrMap);
191 		return NULL;
192 	}
193 
194 	/* Prepare the map structure */
195 	map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
196 	map->indesc = indesc;
197 	map->outdesc = outdesc;
198 	map->attrMap = attrMap;
199 	/* preallocate workspace for Datum arrays */
200 	map->outvalues = (Datum *) palloc(n * sizeof(Datum));
201 	map->outisnull = (bool *) palloc(n * sizeof(bool));
202 	n = indesc->natts + 1;		/* +1 for NULL */
203 	map->invalues = (Datum *) palloc(n * sizeof(Datum));
204 	map->inisnull = (bool *) palloc(n * sizeof(bool));
205 	map->invalues[0] = (Datum) 0;	/* set up the NULL entry */
206 	map->inisnull[0] = true;
207 
208 	return map;
209 }
210 
211 /*
212  * Set up for tuple conversion, matching input and output columns by name.
213  * (Dropped columns are ignored in both input and output.)	This is intended
214  * for use when the rowtypes are related by inheritance, so we expect an exact
215  * match of both type and typmod.  The error messages will be a bit unhelpful
216  * unless both rowtypes are named composite types.
217  */
218 TupleConversionMap *
convert_tuples_by_name(TupleDesc indesc,TupleDesc outdesc,const char * msg)219 convert_tuples_by_name(TupleDesc indesc,
220 					   TupleDesc outdesc,
221 					   const char *msg)
222 {
223 	TupleConversionMap *map;
224 	AttrNumber *attrMap;
225 	int			n = outdesc->natts;
226 	int			i;
227 	bool		same;
228 
229 	/* Verify compatibility and prepare attribute-number map */
230 	attrMap = convert_tuples_by_name_map(indesc, outdesc, msg);
231 
232 	/*
233 	 * Check to see if the map is one-to-one, in which case we need not do a
234 	 * tuple conversion.  We must also insist that both tupdescs either
235 	 * specify or don't specify an OID column, else we need a conversion to
236 	 * add/remove space for that.  (For some callers, presence or absence of
237 	 * an OID column perhaps would not really matter, but let's be safe.)
238 	 */
239 	if (indesc->natts == outdesc->natts &&
240 		indesc->tdhasoid == outdesc->tdhasoid)
241 	{
242 		same = true;
243 		for (i = 0; i < n; i++)
244 		{
245 			Form_pg_attribute inatt = TupleDescAttr(indesc, i);
246 			Form_pg_attribute outatt = TupleDescAttr(outdesc, i);
247 
248 			/*
249 			 * If the input column has a missing attribute, we need a
250 			 * conversion.
251 			 */
252 			if (inatt->atthasmissing)
253 			{
254 				same = false;
255 				break;
256 			}
257 
258 			if (attrMap[i] == (i + 1))
259 				continue;
260 
261 			/*
262 			 * If it's a dropped column and the corresponding input column is
263 			 * also dropped, we needn't convert.  However, attlen and attalign
264 			 * must agree.
265 			 */
266 			if (attrMap[i] == 0 &&
267 				inatt->attisdropped &&
268 				inatt->attlen == outatt->attlen &&
269 				inatt->attalign == outatt->attalign)
270 				continue;
271 
272 			same = false;
273 			break;
274 		}
275 	}
276 	else
277 		same = false;
278 
279 	if (same)
280 	{
281 		/* Runtime conversion is not needed */
282 		pfree(attrMap);
283 		return NULL;
284 	}
285 
286 	/* Prepare the map structure */
287 	map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
288 	map->indesc = indesc;
289 	map->outdesc = outdesc;
290 	map->attrMap = attrMap;
291 	/* preallocate workspace for Datum arrays */
292 	map->outvalues = (Datum *) palloc(n * sizeof(Datum));
293 	map->outisnull = (bool *) palloc(n * sizeof(bool));
294 	n = indesc->natts + 1;		/* +1 for NULL */
295 	map->invalues = (Datum *) palloc(n * sizeof(Datum));
296 	map->inisnull = (bool *) palloc(n * sizeof(bool));
297 	map->invalues[0] = (Datum) 0;	/* set up the NULL entry */
298 	map->inisnull[0] = true;
299 
300 	return map;
301 }
302 
303 /*
304  * Return a palloc'd bare attribute map for tuple conversion, matching input
305  * and output columns by name.  (Dropped columns are ignored in both input and
306  * output.)  This is normally a subroutine for convert_tuples_by_name, but can
307  * be used standalone.
308  */
309 AttrNumber *
convert_tuples_by_name_map(TupleDesc indesc,TupleDesc outdesc,const char * msg)310 convert_tuples_by_name_map(TupleDesc indesc,
311 						   TupleDesc outdesc,
312 						   const char *msg)
313 {
314 	AttrNumber *attrMap;
315 	int			n;
316 	int			i;
317 
318 	n = outdesc->natts;
319 	attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
320 	for (i = 0; i < n; i++)
321 	{
322 		Form_pg_attribute outatt = TupleDescAttr(outdesc, i);
323 		char	   *attname;
324 		Oid			atttypid;
325 		int32		atttypmod;
326 		int			j;
327 
328 		if (outatt->attisdropped)
329 			continue;			/* attrMap[i] is already 0 */
330 		attname = NameStr(outatt->attname);
331 		atttypid = outatt->atttypid;
332 		atttypmod = outatt->atttypmod;
333 		for (j = 0; j < indesc->natts; j++)
334 		{
335 			Form_pg_attribute inatt = TupleDescAttr(indesc, j);
336 
337 			if (inatt->attisdropped)
338 				continue;
339 			if (strcmp(attname, NameStr(inatt->attname)) == 0)
340 			{
341 				/* Found it, check type */
342 				if (atttypid != inatt->atttypid || atttypmod != inatt->atttypmod)
343 					ereport(ERROR,
344 							(errcode(ERRCODE_DATATYPE_MISMATCH),
345 							 errmsg_internal("%s", _(msg)),
346 							 errdetail("Attribute \"%s\" of type %s does not match corresponding attribute of type %s.",
347 									   attname,
348 									   format_type_be(outdesc->tdtypeid),
349 									   format_type_be(indesc->tdtypeid))));
350 				attrMap[i] = (AttrNumber) (j + 1);
351 				break;
352 			}
353 		}
354 		if (attrMap[i] == 0)
355 			ereport(ERROR,
356 					(errcode(ERRCODE_DATATYPE_MISMATCH),
357 					 errmsg_internal("%s", _(msg)),
358 					 errdetail("Attribute \"%s\" of type %s does not exist in type %s.",
359 							   attname,
360 							   format_type_be(outdesc->tdtypeid),
361 							   format_type_be(indesc->tdtypeid))));
362 	}
363 
364 	return attrMap;
365 }
366 
367 /*
368  * Perform conversion of a tuple according to the map.
369  */
370 HeapTuple
do_convert_tuple(HeapTuple tuple,TupleConversionMap * map)371 do_convert_tuple(HeapTuple tuple, TupleConversionMap *map)
372 {
373 	AttrNumber *attrMap = map->attrMap;
374 	Datum	   *invalues = map->invalues;
375 	bool	   *inisnull = map->inisnull;
376 	Datum	   *outvalues = map->outvalues;
377 	bool	   *outisnull = map->outisnull;
378 	int			outnatts = map->outdesc->natts;
379 	int			i;
380 
381 	/*
382 	 * Extract all the values of the old tuple, offsetting the arrays so that
383 	 * invalues[0] is left NULL and invalues[1] is the first source attribute;
384 	 * this exactly matches the numbering convention in attrMap.
385 	 */
386 	heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1);
387 
388 	/*
389 	 * Transpose into proper fields of the new tuple.
390 	 */
391 	for (i = 0; i < outnatts; i++)
392 	{
393 		int			j = attrMap[i];
394 
395 		outvalues[i] = invalues[j];
396 		outisnull[i] = inisnull[j];
397 	}
398 
399 	/*
400 	 * Now form the new tuple.
401 	 */
402 	return heap_form_tuple(map->outdesc, outvalues, outisnull);
403 }
404 
405 /*
406  * Perform conversion of bitmap of columns according to the map.
407  *
408  * The input and output bitmaps are offset by
409  * FirstLowInvalidHeapAttributeNumber to accommodate system cols, like the
410  * column-bitmaps in RangeTblEntry.
411  */
412 Bitmapset *
execute_attr_map_cols(Bitmapset * in_cols,TupleConversionMap * map)413 execute_attr_map_cols(Bitmapset *in_cols, TupleConversionMap *map)
414 {
415 	AttrNumber *attrMap = map->attrMap;
416 	int			maplen = map->outdesc->natts;
417 	Bitmapset  *out_cols;
418 	int			out_attnum;
419 
420 	/* fast path for the common trivial case */
421 	if (in_cols == NULL)
422 		return NULL;
423 
424 	/*
425 	 * For each output column, check which input column it corresponds to.
426 	 */
427 	out_cols = NULL;
428 
429 	for (out_attnum = FirstLowInvalidHeapAttributeNumber + 1;
430 		 out_attnum <= maplen;
431 		 out_attnum++)
432 	{
433 		int			in_attnum;
434 
435 		if (out_attnum < 0)
436 		{
437 			/* System column. No mapping. */
438 			in_attnum = out_attnum;
439 		}
440 		else if (out_attnum == 0)
441 			continue;
442 		else
443 		{
444 			/* normal user column */
445 			in_attnum = attrMap[out_attnum - 1];
446 
447 			if (in_attnum == 0)
448 				continue;
449 		}
450 
451 		if (bms_is_member(in_attnum - FirstLowInvalidHeapAttributeNumber, in_cols))
452 			out_cols = bms_add_member(out_cols, out_attnum - FirstLowInvalidHeapAttributeNumber);
453 	}
454 
455 	return out_cols;
456 }
457 
458 /*
459  * Free a TupleConversionMap structure.
460  */
461 void
free_conversion_map(TupleConversionMap * map)462 free_conversion_map(TupleConversionMap *map)
463 {
464 	/* indesc and outdesc are not ours to free */
465 	pfree(map->attrMap);
466 	pfree(map->invalues);
467 	pfree(map->inisnull);
468 	pfree(map->outvalues);
469 	pfree(map->outisnull);
470 	pfree(map);
471 }
472