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