| 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 * | 
| 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; | 
| 148 | 			Form_pg_attribute outatt; | 
| 149 |  | 
| 150 | 			if (attrMap[i] == (i + 1)) | 
| 151 | 				continue; | 
| 152 |  | 
| 153 | 			/* | 
| 154 | 			 * If it's a dropped column and the corresponding input column is | 
| 155 | 			 * also dropped, we needn't convert.  However, attlen and attalign | 
| 156 | 			 * must agree. | 
| 157 | 			 */ | 
| 158 | 			inatt = TupleDescAttr(indesc, i); | 
| 159 | 			outatt = TupleDescAttr(outdesc, i); | 
| 160 | 			if (attrMap[i] == 0 && | 
| 161 | 				inatt->attisdropped && | 
| 162 | 				inatt->attlen == outatt->attlen && | 
| 163 | 				inatt->attalign == outatt->attalign) | 
| 164 | 				continue; | 
| 165 |  | 
| 166 | 			same = false; | 
| 167 | 			break; | 
| 168 | 		} | 
| 169 | 	} | 
| 170 | 	else | 
| 171 | 		same = false; | 
| 172 |  | 
| 173 | 	if (same) | 
| 174 | 	{ | 
| 175 | 		/* Runtime conversion is not needed */ | 
| 176 | 		pfree(attrMap); | 
| 177 | 		return NULL; | 
| 178 | 	} | 
| 179 |  | 
| 180 | 	/* Prepare the map structure */ | 
| 181 | 	map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap)); | 
| 182 | 	map->indesc = indesc; | 
| 183 | 	map->outdesc = outdesc; | 
| 184 | 	map->attrMap = attrMap; | 
| 185 | 	/* preallocate workspace for Datum arrays */ | 
| 186 | 	map->outvalues = (Datum *) palloc(n * sizeof(Datum)); | 
| 187 | 	map->outisnull = (bool *) palloc(n * sizeof(bool)); | 
| 188 | 	n = indesc->natts + 1;		/* +1 for NULL */ | 
| 189 | 	map->invalues = (Datum *) palloc(n * sizeof(Datum)); | 
| 190 | 	map->inisnull = (bool *) palloc(n * sizeof(bool)); | 
| 191 | 	map->invalues[0] = (Datum) 0;	/* set up the NULL entry */ | 
| 192 | 	map->inisnull[0] = true; | 
| 193 |  | 
| 194 | 	return map; | 
| 195 | } | 
| 196 |  | 
| 197 | /* | 
| 198 |  * Set up for tuple conversion, matching input and output columns by name. | 
| 199 |  * (Dropped columns are ignored in both input and output.)	This is intended | 
| 200 |  * for use when the rowtypes are related by inheritance, so we expect an exact | 
| 201 |  * match of both type and typmod.  The error messages will be a bit unhelpful | 
| 202 |  * unless both rowtypes are named composite types. | 
| 203 |  */ | 
| 204 | TupleConversionMap * | 
| 205 | convert_tuples_by_name(TupleDesc indesc, | 
| 206 | 					   TupleDesc outdesc, | 
| 207 | 					   const char *msg) | 
| 208 | { | 
| 209 | 	TupleConversionMap *map; | 
| 210 | 	AttrNumber *attrMap; | 
| 211 | 	int			n = outdesc->natts; | 
| 212 |  | 
| 213 | 	/* Verify compatibility and prepare attribute-number map */ | 
| 214 | 	attrMap = convert_tuples_by_name_map_if_req(indesc, outdesc, msg); | 
| 215 |  | 
| 216 | 	if (attrMap == NULL) | 
| 217 | 	{ | 
| 218 | 		/* runtime conversion is not needed */ | 
| 219 | 		return NULL; | 
| 220 | 	} | 
| 221 |  | 
| 222 | 	/* Prepare the map structure */ | 
| 223 | 	map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap)); | 
| 224 | 	map->indesc = indesc; | 
| 225 | 	map->outdesc = outdesc; | 
| 226 | 	map->attrMap = attrMap; | 
| 227 | 	/* preallocate workspace for Datum arrays */ | 
| 228 | 	map->outvalues = (Datum *) palloc(n * sizeof(Datum)); | 
| 229 | 	map->outisnull = (bool *) palloc(n * sizeof(bool)); | 
| 230 | 	n = indesc->natts + 1;		/* +1 for NULL */ | 
| 231 | 	map->invalues = (Datum *) palloc(n * sizeof(Datum)); | 
| 232 | 	map->inisnull = (bool *) palloc(n * sizeof(bool)); | 
| 233 | 	map->invalues[0] = (Datum) 0;	/* set up the NULL entry */ | 
| 234 | 	map->inisnull[0] = true; | 
| 235 |  | 
| 236 | 	return map; | 
| 237 | } | 
| 238 |  | 
| 239 | /* | 
| 240 |  * Return a palloc'd bare attribute map for tuple conversion, matching input | 
| 241 |  * and output columns by name.  (Dropped columns are ignored in both input and | 
| 242 |  * output.)  This is normally a subroutine for convert_tuples_by_name, but can | 
| 243 |  * be used standalone. | 
| 244 |  */ | 
| 245 | AttrNumber * | 
| 246 | convert_tuples_by_name_map(TupleDesc indesc, | 
| 247 | 						   TupleDesc outdesc, | 
| 248 | 						   const char *msg) | 
| 249 | { | 
| 250 | 	AttrNumber *attrMap; | 
| 251 | 	int			outnatts; | 
| 252 | 	int			innatts; | 
| 253 | 	int			i; | 
| 254 | 	int			nextindesc = -1; | 
| 255 |  | 
| 256 | 	outnatts = outdesc->natts; | 
| 257 | 	innatts = indesc->natts; | 
| 258 |  | 
| 259 | 	attrMap = (AttrNumber *) palloc0(outnatts * sizeof(AttrNumber)); | 
| 260 | 	for (i = 0; i < outnatts; i++) | 
| 261 | 	{ | 
| 262 | 		Form_pg_attribute outatt = TupleDescAttr(outdesc, i); | 
| 263 | 		char	   *attname; | 
| 264 | 		Oid			atttypid; | 
| 265 | 		int32		atttypmod; | 
| 266 | 		int			j; | 
| 267 |  | 
| 268 | 		if (outatt->attisdropped) | 
| 269 | 			continue;			/* attrMap[i] is already 0 */ | 
| 270 | 		attname = NameStr(outatt->attname); | 
| 271 | 		atttypid = outatt->atttypid; | 
| 272 | 		atttypmod = outatt->atttypmod; | 
| 273 |  | 
| 274 | 		/* | 
| 275 | 		 * Now search for an attribute with the same name in the indesc. It | 
| 276 | 		 * seems likely that a partitioned table will have the attributes in | 
| 277 | 		 * the same order as the partition, so the search below is optimized | 
| 278 | 		 * for that case.  It is possible that columns are dropped in one of | 
| 279 | 		 * the relations, but not the other, so we use the 'nextindesc' | 
| 280 | 		 * counter to track the starting point of the search.  If the inner | 
| 281 | 		 * loop encounters dropped columns then it will have to skip over | 
| 282 | 		 * them, but it should leave 'nextindesc' at the correct position for | 
| 283 | 		 * the next outer loop. | 
| 284 | 		 */ | 
| 285 | 		for (j = 0; j < innatts; j++) | 
| 286 | 		{ | 
| 287 | 			Form_pg_attribute inatt; | 
| 288 |  | 
| 289 | 			nextindesc++; | 
| 290 | 			if (nextindesc >= innatts) | 
| 291 | 				nextindesc = 0; | 
| 292 |  | 
| 293 | 			inatt = TupleDescAttr(indesc, nextindesc); | 
| 294 | 			if (inatt->attisdropped) | 
| 295 | 				continue; | 
| 296 | 			if (strcmp(attname, NameStr(inatt->attname)) == 0) | 
| 297 | 			{ | 
| 298 | 				/* Found it, check type */ | 
| 299 | 				if (atttypid != inatt->atttypid || atttypmod != inatt->atttypmod) | 
| 300 | 					ereport(ERROR, | 
| 301 | 							(errcode(ERRCODE_DATATYPE_MISMATCH), | 
| 302 | 							 errmsg_internal("%s" , _(msg)), | 
| 303 | 							 errdetail("Attribute \"%s\" of type %s does not match corresponding attribute of type %s." , | 
| 304 | 									   attname, | 
| 305 | 									   format_type_be(outdesc->tdtypeid), | 
| 306 | 									   format_type_be(indesc->tdtypeid)))); | 
| 307 | 				attrMap[i] = inatt->attnum; | 
| 308 | 				break; | 
| 309 | 			} | 
| 310 | 		} | 
| 311 | 		if (attrMap[i] == 0) | 
| 312 | 			ereport(ERROR, | 
| 313 | 					(errcode(ERRCODE_DATATYPE_MISMATCH), | 
| 314 | 					 errmsg_internal("%s" , _(msg)), | 
| 315 | 					 errdetail("Attribute \"%s\" of type %s does not exist in type %s." , | 
| 316 | 							   attname, | 
| 317 | 							   format_type_be(outdesc->tdtypeid), | 
| 318 | 							   format_type_be(indesc->tdtypeid)))); | 
| 319 | 	} | 
| 320 | 	return attrMap; | 
| 321 | } | 
| 322 |  | 
| 323 | /* | 
| 324 |  * Returns mapping created by convert_tuples_by_name_map, or NULL if no | 
| 325 |  * conversion not required. This is a convenience routine for | 
| 326 |  * convert_tuples_by_name() and other functions. | 
| 327 |  */ | 
| 328 | AttrNumber * | 
| 329 | convert_tuples_by_name_map_if_req(TupleDesc indesc, | 
| 330 | 								  TupleDesc outdesc, | 
| 331 | 								  const char *msg) | 
| 332 | { | 
| 333 | 	AttrNumber *attrMap; | 
| 334 | 	int			n = outdesc->natts; | 
| 335 | 	int			i; | 
| 336 | 	bool		same; | 
| 337 |  | 
| 338 | 	/* Verify compatibility and prepare attribute-number map */ | 
| 339 | 	attrMap = convert_tuples_by_name_map(indesc, outdesc, msg); | 
| 340 |  | 
| 341 | 	/* | 
| 342 | 	 * Check to see if the map is one-to-one, in which case we need not do a | 
| 343 | 	 * tuple conversion. | 
| 344 | 	 */ | 
| 345 | 	if (indesc->natts == outdesc->natts) | 
| 346 | 	{ | 
| 347 | 		same = true; | 
| 348 | 		for (i = 0; i < n; i++) | 
| 349 | 		{ | 
| 350 | 			Form_pg_attribute inatt; | 
| 351 | 			Form_pg_attribute outatt; | 
| 352 |  | 
| 353 | 			if (attrMap[i] == (i + 1)) | 
| 354 | 				continue; | 
| 355 |  | 
| 356 | 			/* | 
| 357 | 			 * If it's a dropped column and the corresponding input column is | 
| 358 | 			 * also dropped, we needn't convert.  However, attlen and attalign | 
| 359 | 			 * must agree. | 
| 360 | 			 */ | 
| 361 | 			inatt = TupleDescAttr(indesc, i); | 
| 362 | 			outatt = TupleDescAttr(outdesc, i); | 
| 363 | 			if (attrMap[i] == 0 && | 
| 364 | 				inatt->attisdropped && | 
| 365 | 				inatt->attlen == outatt->attlen && | 
| 366 | 				inatt->attalign == outatt->attalign) | 
| 367 | 				continue; | 
| 368 |  | 
| 369 | 			same = false; | 
| 370 | 			break; | 
| 371 | 		} | 
| 372 | 	} | 
| 373 | 	else | 
| 374 | 		same = false; | 
| 375 |  | 
| 376 | 	if (same) | 
| 377 | 	{ | 
| 378 | 		/* Runtime conversion is not needed */ | 
| 379 | 		pfree(attrMap); | 
| 380 | 		return NULL; | 
| 381 | 	} | 
| 382 | 	else | 
| 383 | 		return attrMap; | 
| 384 | } | 
| 385 |  | 
| 386 | /* | 
| 387 |  * Perform conversion of a tuple according to the map. | 
| 388 |  */ | 
| 389 | HeapTuple | 
| 390 | execute_attr_map_tuple(HeapTuple tuple, TupleConversionMap *map) | 
| 391 | { | 
| 392 | 	AttrNumber *attrMap = map->attrMap; | 
| 393 | 	Datum	   *invalues = map->invalues; | 
| 394 | 	bool	   *inisnull = map->inisnull; | 
| 395 | 	Datum	   *outvalues = map->outvalues; | 
| 396 | 	bool	   *outisnull = map->outisnull; | 
| 397 | 	int			outnatts = map->outdesc->natts; | 
| 398 | 	int			i; | 
| 399 |  | 
| 400 | 	/* | 
| 401 | 	 * Extract all the values of the old tuple, offsetting the arrays so that | 
| 402 | 	 * invalues[0] is left NULL and invalues[1] is the first source attribute; | 
| 403 | 	 * this exactly matches the numbering convention in attrMap. | 
| 404 | 	 */ | 
| 405 | 	heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1); | 
| 406 |  | 
| 407 | 	/* | 
| 408 | 	 * Transpose into proper fields of the new tuple. | 
| 409 | 	 */ | 
| 410 | 	for (i = 0; i < outnatts; i++) | 
| 411 | 	{ | 
| 412 | 		int			j = attrMap[i]; | 
| 413 |  | 
| 414 | 		outvalues[i] = invalues[j]; | 
| 415 | 		outisnull[i] = inisnull[j]; | 
| 416 | 	} | 
| 417 |  | 
| 418 | 	/* | 
| 419 | 	 * Now form the new tuple. | 
| 420 | 	 */ | 
| 421 | 	return heap_form_tuple(map->outdesc, outvalues, outisnull); | 
| 422 | } | 
| 423 |  | 
| 424 | /* | 
| 425 |  * Perform conversion of a tuple slot according to the map. | 
| 426 |  */ | 
| 427 | TupleTableSlot * | 
| 428 | execute_attr_map_slot(AttrNumber *attrMap, | 
| 429 | 					  TupleTableSlot *in_slot, | 
| 430 | 					  TupleTableSlot *out_slot) | 
| 431 | { | 
| 432 | 	Datum	   *invalues; | 
| 433 | 	bool	   *inisnull; | 
| 434 | 	Datum	   *outvalues; | 
| 435 | 	bool	   *outisnull; | 
| 436 | 	int			outnatts; | 
| 437 | 	int			i; | 
| 438 |  | 
| 439 | 	/* Sanity checks */ | 
| 440 | 	Assert(in_slot->tts_tupleDescriptor != NULL && | 
| 441 | 		   out_slot->tts_tupleDescriptor != NULL); | 
| 442 | 	Assert(in_slot->tts_values != NULL && out_slot->tts_values != NULL); | 
| 443 |  | 
| 444 | 	outnatts = out_slot->tts_tupleDescriptor->natts; | 
| 445 |  | 
| 446 | 	/* Extract all the values of the in slot. */ | 
| 447 | 	slot_getallattrs(in_slot); | 
| 448 |  | 
| 449 | 	/* Before doing the mapping, clear any old contents from the out slot */ | 
| 450 | 	ExecClearTuple(out_slot); | 
| 451 |  | 
| 452 | 	invalues = in_slot->tts_values; | 
| 453 | 	inisnull = in_slot->tts_isnull; | 
| 454 | 	outvalues = out_slot->tts_values; | 
| 455 | 	outisnull = out_slot->tts_isnull; | 
| 456 |  | 
| 457 | 	/* Transpose into proper fields of the out slot. */ | 
| 458 | 	for (i = 0; i < outnatts; i++) | 
| 459 | 	{ | 
| 460 | 		int			j = attrMap[i] - 1; | 
| 461 |  | 
| 462 | 		/* attrMap[i] == 0 means it's a NULL datum. */ | 
| 463 | 		if (j == -1) | 
| 464 | 		{ | 
| 465 | 			outvalues[i] = (Datum) 0; | 
| 466 | 			outisnull[i] = true; | 
| 467 | 		} | 
| 468 | 		else | 
| 469 | 		{ | 
| 470 | 			outvalues[i] = invalues[j]; | 
| 471 | 			outisnull[i] = inisnull[j]; | 
| 472 | 		} | 
| 473 | 	} | 
| 474 |  | 
| 475 | 	ExecStoreVirtualTuple(out_slot); | 
| 476 |  | 
| 477 | 	return out_slot; | 
| 478 | } | 
| 479 |  | 
| 480 | /* | 
| 481 |  * Free a TupleConversionMap structure. | 
| 482 |  */ | 
| 483 | void | 
| 484 | free_conversion_map(TupleConversionMap *map) | 
| 485 | { | 
| 486 | 	/* indesc and outdesc are not ours to free */ | 
| 487 | 	pfree(map->attrMap); | 
| 488 | 	pfree(map->invalues); | 
| 489 | 	pfree(map->inisnull); | 
| 490 | 	pfree(map->outvalues); | 
| 491 | 	pfree(map->outisnull); | 
| 492 | 	pfree(map); | 
| 493 | } | 
| 494 |  |