PostgreSQL Source Code  git master
tupconvert.c
Go to the documentation of this file.
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  */
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  */
206  TupleDesc outdesc)
207 {
208  TupleConversionMap *map;
209  AttrNumber *attrMap;
210  int n = outdesc->natts;
211 
212  /* Verify compatibility and prepare attribute-number map */
213  attrMap = convert_tuples_by_name_map_if_req(indesc, outdesc);
214 
215  if (attrMap == NULL)
216  {
217  /* runtime conversion is not needed */
218  return NULL;
219  }
220 
221  /* Prepare the map structure */
222  map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
223  map->indesc = indesc;
224  map->outdesc = outdesc;
225  map->attrMap = attrMap;
226  /* preallocate workspace for Datum arrays */
227  map->outvalues = (Datum *) palloc(n * sizeof(Datum));
228  map->outisnull = (bool *) palloc(n * sizeof(bool));
229  n = indesc->natts + 1; /* +1 for NULL */
230  map->invalues = (Datum *) palloc(n * sizeof(Datum));
231  map->inisnull = (bool *) palloc(n * sizeof(bool));
232  map->invalues[0] = (Datum) 0; /* set up the NULL entry */
233  map->inisnull[0] = true;
234 
235  return map;
236 }
237 
238 /*
239  * Return a palloc'd bare attribute map for tuple conversion, matching input
240  * and output columns by name. (Dropped columns are ignored in both input and
241  * output.) This is normally a subroutine for convert_tuples_by_name, but can
242  * be used standalone.
243  */
244 AttrNumber *
246  TupleDesc outdesc)
247 {
248  AttrNumber *attrMap;
249  int outnatts;
250  int innatts;
251  int i;
252  int nextindesc = -1;
253 
254  outnatts = outdesc->natts;
255  innatts = indesc->natts;
256 
257  attrMap = (AttrNumber *) palloc0(outnatts * sizeof(AttrNumber));
258  for (i = 0; i < outnatts; i++)
259  {
260  Form_pg_attribute outatt = TupleDescAttr(outdesc, i);
261  char *attname;
262  Oid atttypid;
263  int32 atttypmod;
264  int j;
265 
266  if (outatt->attisdropped)
267  continue; /* attrMap[i] is already 0 */
268  attname = NameStr(outatt->attname);
269  atttypid = outatt->atttypid;
270  atttypmod = outatt->atttypmod;
271 
272  /*
273  * Now search for an attribute with the same name in the indesc. It
274  * seems likely that a partitioned table will have the attributes in
275  * the same order as the partition, so the search below is optimized
276  * for that case. It is possible that columns are dropped in one of
277  * the relations, but not the other, so we use the 'nextindesc'
278  * counter to track the starting point of the search. If the inner
279  * loop encounters dropped columns then it will have to skip over
280  * them, but it should leave 'nextindesc' at the correct position for
281  * the next outer loop.
282  */
283  for (j = 0; j < innatts; j++)
284  {
285  Form_pg_attribute inatt;
286 
287  nextindesc++;
288  if (nextindesc >= innatts)
289  nextindesc = 0;
290 
291  inatt = TupleDescAttr(indesc, nextindesc);
292  if (inatt->attisdropped)
293  continue;
294  if (strcmp(attname, NameStr(inatt->attname)) == 0)
295  {
296  /* Found it, check type */
297  if (atttypid != inatt->atttypid || atttypmod != inatt->atttypmod)
298  ereport(ERROR,
299  (errcode(ERRCODE_DATATYPE_MISMATCH),
300  errmsg("could not convert row type"),
301  errdetail("Attribute \"%s\" of type %s does not match corresponding attribute of type %s.",
302  attname,
303  format_type_be(outdesc->tdtypeid),
304  format_type_be(indesc->tdtypeid))));
305  attrMap[i] = inatt->attnum;
306  break;
307  }
308  }
309  if (attrMap[i] == 0)
310  ereport(ERROR,
311  (errcode(ERRCODE_DATATYPE_MISMATCH),
312  errmsg("could not convert row type"),
313  errdetail("Attribute \"%s\" of type %s does not exist in type %s.",
314  attname,
315  format_type_be(outdesc->tdtypeid),
316  format_type_be(indesc->tdtypeid))));
317  }
318  return attrMap;
319 }
320 
321 /*
322  * Returns mapping created by convert_tuples_by_name_map, or NULL if no
323  * conversion not required. This is a convenience routine for
324  * convert_tuples_by_name() and other functions.
325  */
326 AttrNumber *
328  TupleDesc outdesc)
329 {
330  AttrNumber *attrMap;
331  int n = outdesc->natts;
332  int i;
333  bool same;
334 
335  /* Verify compatibility and prepare attribute-number map */
336  attrMap = convert_tuples_by_name_map(indesc, outdesc);
337 
338  /*
339  * Check to see if the map is one-to-one, in which case we need not do a
340  * tuple conversion.
341  */
342  if (indesc->natts == outdesc->natts)
343  {
344  same = true;
345  for (i = 0; i < n; i++)
346  {
347  Form_pg_attribute inatt;
348  Form_pg_attribute outatt;
349 
350  if (attrMap[i] == (i + 1))
351  continue;
352 
353  /*
354  * If it's a dropped column and the corresponding input column is
355  * also dropped, we needn't convert. However, attlen and attalign
356  * must agree.
357  */
358  inatt = TupleDescAttr(indesc, i);
359  outatt = TupleDescAttr(outdesc, i);
360  if (attrMap[i] == 0 &&
361  inatt->attisdropped &&
362  inatt->attlen == outatt->attlen &&
363  inatt->attalign == outatt->attalign)
364  continue;
365 
366  same = false;
367  break;
368  }
369  }
370  else
371  same = false;
372 
373  if (same)
374  {
375  /* Runtime conversion is not needed */
376  pfree(attrMap);
377  return NULL;
378  }
379  else
380  return attrMap;
381 }
382 
383 /*
384  * Perform conversion of a tuple according to the map.
385  */
386 HeapTuple
388 {
389  AttrNumber *attrMap = map->attrMap;
390  Datum *invalues = map->invalues;
391  bool *inisnull = map->inisnull;
392  Datum *outvalues = map->outvalues;
393  bool *outisnull = map->outisnull;
394  int outnatts = map->outdesc->natts;
395  int i;
396 
397  /*
398  * Extract all the values of the old tuple, offsetting the arrays so that
399  * invalues[0] is left NULL and invalues[1] is the first source attribute;
400  * this exactly matches the numbering convention in attrMap.
401  */
402  heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1);
403 
404  /*
405  * Transpose into proper fields of the new tuple.
406  */
407  for (i = 0; i < outnatts; i++)
408  {
409  int j = attrMap[i];
410 
411  outvalues[i] = invalues[j];
412  outisnull[i] = inisnull[j];
413  }
414 
415  /*
416  * Now form the new tuple.
417  */
418  return heap_form_tuple(map->outdesc, outvalues, outisnull);
419 }
420 
421 /*
422  * Perform conversion of a tuple slot according to the map.
423  */
426  TupleTableSlot *in_slot,
427  TupleTableSlot *out_slot)
428 {
429  Datum *invalues;
430  bool *inisnull;
431  Datum *outvalues;
432  bool *outisnull;
433  int outnatts;
434  int i;
435 
436  /* Sanity checks */
437  Assert(in_slot->tts_tupleDescriptor != NULL &&
438  out_slot->tts_tupleDescriptor != NULL);
439  Assert(in_slot->tts_values != NULL && out_slot->tts_values != NULL);
440 
441  outnatts = out_slot->tts_tupleDescriptor->natts;
442 
443  /* Extract all the values of the in slot. */
444  slot_getallattrs(in_slot);
445 
446  /* Before doing the mapping, clear any old contents from the out slot */
447  ExecClearTuple(out_slot);
448 
449  invalues = in_slot->tts_values;
450  inisnull = in_slot->tts_isnull;
451  outvalues = out_slot->tts_values;
452  outisnull = out_slot->tts_isnull;
453 
454  /* Transpose into proper fields of the out slot. */
455  for (i = 0; i < outnatts; i++)
456  {
457  int j = attrMap[i] - 1;
458 
459  /* attrMap[i] == 0 means it's a NULL datum. */
460  if (j == -1)
461  {
462  outvalues[i] = (Datum) 0;
463  outisnull[i] = true;
464  }
465  else
466  {
467  outvalues[i] = invalues[j];
468  outisnull[i] = inisnull[j];
469  }
470  }
471 
472  ExecStoreVirtualTuple(out_slot);
473 
474  return out_slot;
475 }
476 
477 /*
478  * Free a TupleConversionMap structure.
479  */
480 void
482 {
483  /* indesc and outdesc are not ours to free */
484  pfree(map->attrMap);
485  pfree(map->invalues);
486  pfree(map->inisnull);
487  pfree(map->outvalues);
488  pfree(map->outisnull);
489  pfree(map);
490 }
AttrNumber * attrMap
Definition: tupconvert.h:26
TupleDesc indesc
Definition: tupconvert.h:24
TupleDesc outdesc
Definition: tupconvert.h:25
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:426
TupleConversionMap * convert_tuples_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg)
Definition: tupconvert.c:65
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
int errcode(int sqlerrcode)
Definition: elog.c:570
TupleTableSlot * execute_attr_map_slot(AttrNumber *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:425
char * format_type_be(Oid type_oid)
Definition: format_type.c:326
Datum * tts_values
Definition: tuptable.h:126
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
unsigned int Oid
Definition: postgres_ext.h:31
signed int int32
Definition: c.h:346
void pfree(void *pointer)
Definition: mcxt.c:1031
#define ERROR
Definition: elog.h:43
NameData attname
Definition: pg_attribute.h:40
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:355
TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc)
Definition: tupconvert.c:205
bool * tts_isnull
Definition: tuptable.h:128
int errdetail(const char *fmt,...)
Definition: elog.c:860
void free_conversion_map(TupleConversionMap *map)
Definition: tupconvert.c:481
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
HeapTuple execute_attr_map_tuple(HeapTuple tuple, TupleConversionMap *map)
Definition: tupconvert.c:387
Oid atttypid
Definition: pg_attribute.h:49
#define ereport(elevel, rest)
Definition: elog.h:141
AttrNumber * convert_tuples_by_name_map_if_req(TupleDesc indesc, TupleDesc outdesc)
Definition: tupconvert.c:327
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:124
char * format_type_with_typemod(Oid type_oid, int32 typemod)
Definition: format_type.c:345
void * palloc0(Size size)
Definition: mcxt.c:955
uintptr_t Datum
Definition: postgres.h:367
int errmsg_internal(const char *fmt,...)
Definition: elog.c:814
#define Assert(condition)
Definition: c.h:732
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1249
Oid tdtypeid
Definition: tupdesc.h:82
void * palloc(Size size)
Definition: mcxt.c:924
int errmsg(const char *fmt,...)
Definition: elog.c:784
int i
#define NameStr(name)
Definition: c.h:609
AttrNumber * convert_tuples_by_name_map(TupleDesc indesc, TupleDesc outdesc)
Definition: tupconvert.c:245
int16 AttrNumber
Definition: attnum.h:21
#define _(x)
Definition: elog.c:84
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1517