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