PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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
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-2017, 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/tupconvert.h"
25 #include "utils/builtins.h"
26 
27 
28 /*
29  * The conversion setup routines have the following common API:
30  *
31  * The setup routine checks whether the given source and destination tuple
32  * descriptors are logically compatible. If not, it throws an error.
33  * If so, it returns NULL if they are physically compatible (ie, no conversion
34  * is needed), else a TupleConversionMap that can be used by do_convert_tuple
35  * to perform the conversion.
36  *
37  * The TupleConversionMap, if needed, is palloc'd in the caller's memory
38  * context. Also, the given tuple descriptors are referenced by the map,
39  * so they must survive as long as the map is needed.
40  *
41  * The caller must supply a suitable primary error message to be used if
42  * a compatibility error is thrown. Recommended coding practice is to use
43  * gettext_noop() on this string, so that it is translatable but won't
44  * actually be translated unless the error gets thrown.
45  *
46  *
47  * Implementation notes:
48  *
49  * The key component of a TupleConversionMap is an attrMap[] array with
50  * one entry per output column. This entry contains the 1-based index of
51  * the corresponding input column, or zero to force a NULL value (for
52  * a dropped output column). The TupleConversionMap also contains workspace
53  * arrays.
54  */
55 
56 
57 /*
58  * Set up for tuple conversion, matching input and output columns by
59  * position. (Dropped columns are ignored in both input and output.)
60  *
61  * Note: the errdetail messages speak of indesc as the "returned" rowtype,
62  * outdesc as the "expected" rowtype. This is okay for current uses but
63  * might need generalization in future.
64  */
67  TupleDesc outdesc,
68  const char *msg)
69 {
70  TupleConversionMap *map;
71  AttrNumber *attrMap;
72  int nincols;
73  int noutcols;
74  int n;
75  int i;
76  int j;
77  bool same;
78 
79  /* Verify compatibility and prepare attribute-number map */
80  n = outdesc->natts;
81  attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
82  j = 0; /* j is next physical input attribute */
83  nincols = noutcols = 0; /* these count non-dropped attributes */
84  same = true;
85  for (i = 0; i < n; i++)
86  {
87  Form_pg_attribute att = outdesc->attrs[i];
88  Oid atttypid;
89  int32 atttypmod;
90 
91  if (att->attisdropped)
92  continue; /* attrMap[i] is already 0 */
93  noutcols++;
94  atttypid = att->atttypid;
95  atttypmod = att->atttypmod;
96  for (; j < indesc->natts; j++)
97  {
98  att = indesc->attrs[j];
99  if (att->attisdropped)
100  continue;
101  nincols++;
102  /* Found matching column, check type */
103  if (atttypid != att->atttypid ||
104  (atttypmod != att->atttypmod && atttypmod >= 0))
105  ereport(ERROR,
106  (errcode(ERRCODE_DATATYPE_MISMATCH),
107  errmsg_internal("%s", _(msg)),
108  errdetail("Returned type %s does not match expected type %s in column %d.",
109  format_type_with_typemod(att->atttypid,
110  att->atttypmod),
111  format_type_with_typemod(atttypid,
112  atttypmod),
113  noutcols)));
114  attrMap[i] = (AttrNumber) (j + 1);
115  j++;
116  break;
117  }
118  if (attrMap[i] == 0)
119  same = false; /* we'll complain below */
120  }
121 
122  /* Check for unused input columns */
123  for (; j < indesc->natts; j++)
124  {
125  if (indesc->attrs[j]->attisdropped)
126  continue;
127  nincols++;
128  same = false; /* we'll complain below */
129  }
130 
131  /* Report column count mismatch using the non-dropped-column counts */
132  if (!same)
133  ereport(ERROR,
134  (errcode(ERRCODE_DATATYPE_MISMATCH),
135  errmsg_internal("%s", _(msg)),
136  errdetail("Number of returned columns (%d) does not match "
137  "expected column count (%d).",
138  nincols, noutcols)));
139 
140  /*
141  * Check to see if the map is one-to-one, in which case we need not do a
142  * tuple conversion. We must also insist that both tupdescs either
143  * specify or don't specify an OID column, else we need a conversion to
144  * add/remove space for that. (For some callers, presence or absence of
145  * an OID column perhaps would not really matter, but let's be safe.)
146  */
147  if (indesc->natts == outdesc->natts &&
148  indesc->tdhasoid == outdesc->tdhasoid)
149  {
150  for (i = 0; i < n; i++)
151  {
152  if (attrMap[i] == (i + 1))
153  continue;
154 
155  /*
156  * If it's a dropped column and the corresponding input column is
157  * also dropped, we needn't convert. However, attlen and attalign
158  * must agree.
159  */
160  if (attrMap[i] == 0 &&
161  indesc->attrs[i]->attisdropped &&
162  indesc->attrs[i]->attlen == outdesc->attrs[i]->attlen &&
163  indesc->attrs[i]->attalign == outdesc->attrs[i]->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  const char *msg)
208 {
209  TupleConversionMap *map;
210  AttrNumber *attrMap;
211  int n = outdesc->natts;
212  int i;
213  bool same;
214 
215  /* Verify compatibility and prepare attribute-number map */
216  attrMap = convert_tuples_by_name_map(indesc, outdesc, msg);
217 
218  /*
219  * Check to see if the map is one-to-one, in which case we need not do a
220  * tuple conversion. We must also insist that both tupdescs either
221  * specify or don't specify an OID column, else we need a conversion to
222  * add/remove space for that. (For some callers, presence or absence of
223  * an OID column perhaps would not really matter, but let's be safe.)
224  */
225  if (indesc->natts == outdesc->natts &&
226  indesc->tdhasoid == outdesc->tdhasoid)
227  {
228  same = true;
229  for (i = 0; i < n; i++)
230  {
231  if (attrMap[i] == (i + 1))
232  continue;
233 
234  /*
235  * If it's a dropped column and the corresponding input column is
236  * also dropped, we needn't convert. However, attlen and attalign
237  * must agree.
238  */
239  if (attrMap[i] == 0 &&
240  indesc->attrs[i]->attisdropped &&
241  indesc->attrs[i]->attlen == outdesc->attrs[i]->attlen &&
242  indesc->attrs[i]->attalign == outdesc->attrs[i]->attalign)
243  continue;
244 
245  same = false;
246  break;
247  }
248  }
249  else
250  same = false;
251 
252  if (same)
253  {
254  /* Runtime conversion is not needed */
255  pfree(attrMap);
256  return NULL;
257  }
258 
259  /* Prepare the map structure */
260  map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
261  map->indesc = indesc;
262  map->outdesc = outdesc;
263  map->attrMap = attrMap;
264  /* preallocate workspace for Datum arrays */
265  map->outvalues = (Datum *) palloc(n * sizeof(Datum));
266  map->outisnull = (bool *) palloc(n * sizeof(bool));
267  n = indesc->natts + 1; /* +1 for NULL */
268  map->invalues = (Datum *) palloc(n * sizeof(Datum));
269  map->inisnull = (bool *) palloc(n * sizeof(bool));
270  map->invalues[0] = (Datum) 0; /* set up the NULL entry */
271  map->inisnull[0] = true;
272 
273  return map;
274 }
275 
276 /*
277  * Return a palloc'd bare attribute map for tuple conversion, matching input
278  * and output columns by name. (Dropped columns are ignored in both input and
279  * output.) This is normally a subroutine for convert_tuples_by_name, but can
280  * be used standalone.
281  */
282 AttrNumber *
284  TupleDesc outdesc,
285  const char *msg)
286 {
287  AttrNumber *attrMap;
288  int n;
289  int i;
290 
291  n = outdesc->natts;
292  attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
293  for (i = 0; i < n; i++)
294  {
295  Form_pg_attribute att = outdesc->attrs[i];
296  char *attname;
297  Oid atttypid;
298  int32 atttypmod;
299  int j;
300 
301  if (att->attisdropped)
302  continue; /* attrMap[i] is already 0 */
303  attname = NameStr(att->attname);
304  atttypid = att->atttypid;
305  atttypmod = att->atttypmod;
306  for (j = 0; j < indesc->natts; j++)
307  {
308  att = indesc->attrs[j];
309  if (att->attisdropped)
310  continue;
311  if (strcmp(attname, NameStr(att->attname)) == 0)
312  {
313  /* Found it, check type */
314  if (atttypid != att->atttypid || atttypmod != att->atttypmod)
315  ereport(ERROR,
316  (errcode(ERRCODE_DATATYPE_MISMATCH),
317  errmsg_internal("%s", _(msg)),
318  errdetail("Attribute \"%s\" of type %s does not match corresponding attribute of type %s.",
319  attname,
320  format_type_be(outdesc->tdtypeid),
321  format_type_be(indesc->tdtypeid))));
322  attrMap[i] = (AttrNumber) (j + 1);
323  break;
324  }
325  }
326  if (attrMap[i] == 0)
327  ereport(ERROR,
328  (errcode(ERRCODE_DATATYPE_MISMATCH),
329  errmsg_internal("%s", _(msg)),
330  errdetail("Attribute \"%s\" of type %s does not exist in type %s.",
331  attname,
332  format_type_be(outdesc->tdtypeid),
333  format_type_be(indesc->tdtypeid))));
334  }
335 
336  return attrMap;
337 }
338 
339 /*
340  * Perform conversion of a tuple according to the map.
341  */
342 HeapTuple
344 {
345  AttrNumber *attrMap = map->attrMap;
346  Datum *invalues = map->invalues;
347  bool *inisnull = map->inisnull;
348  Datum *outvalues = map->outvalues;
349  bool *outisnull = map->outisnull;
350  int outnatts = map->outdesc->natts;
351  int i;
352 
353  /*
354  * Extract all the values of the old tuple, offsetting the arrays so that
355  * invalues[0] is left NULL and invalues[1] is the first source attribute;
356  * this exactly matches the numbering convention in attrMap.
357  */
358  heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1);
359 
360  /*
361  * Transpose into proper fields of the new tuple.
362  */
363  for (i = 0; i < outnatts; i++)
364  {
365  int j = attrMap[i];
366 
367  outvalues[i] = invalues[j];
368  outisnull[i] = inisnull[j];
369  }
370 
371  /*
372  * Now form the new tuple.
373  */
374  return heap_form_tuple(map->outdesc, outvalues, outisnull);
375 }
376 
377 /*
378  * Free a TupleConversionMap structure.
379  */
380 void
382 {
383  /* indesc and outdesc are not ours to free */
384  pfree(map->attrMap);
385  pfree(map->invalues);
386  pfree(map->inisnull);
387  pfree(map->outvalues);
388  pfree(map->outisnull);
389  pfree(map);
390 }
AttrNumber * attrMap
Definition: tupconvert.h:25
TupleDesc indesc
Definition: tupconvert.h:23
Oid tdtypeid
Definition: tupdesc.h:77
TupleDesc outdesc
Definition: tupconvert.h:24
bool tdhasoid
Definition: tupdesc.h:79
TupleConversionMap * convert_tuples_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg)
Definition: tupconvert.c:66
Form_pg_attribute * attrs
Definition: tupdesc.h:74
int errcode(int sqlerrcode)
Definition: elog.c:575
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:692
unsigned int Oid
Definition: postgres_ext.h:31
int natts
Definition: tupdesc.h:73
signed int int32
Definition: c.h:256
void pfree(void *pointer)
Definition: mcxt.c:950
#define ERROR
Definition: elog.h:43
int errdetail(const char *fmt,...)
Definition: elog.c:873
void free_conversion_map(TupleConversionMap *map)
Definition: tupconvert.c:381
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
#define ereport(elevel, rest)
Definition: elog.h:122
TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc, const char *msg)
Definition: tupconvert.c:205
AttrNumber * convert_tuples_by_name_map(TupleDesc indesc, TupleDesc outdesc, const char *msg)
Definition: tupconvert.c:283
char * format_type_with_typemod(Oid type_oid, int32 typemod)
Definition: format_type.c:113
void * palloc0(Size size)
Definition: mcxt.c:878
uintptr_t Datum
Definition: postgres.h:372
int errmsg_internal(const char *fmt,...)
Definition: elog.c:827
#define NULL
Definition: c.h:229
HeapTuple do_convert_tuple(HeapTuple tuple, TupleConversionMap *map)
Definition: tupconvert.c:343
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:933
void * palloc(Size size)
Definition: mcxt.c:849
int i
#define NameStr(name)
Definition: c.h:499
int16 AttrNumber
Definition: attnum.h:21
#define _(x)
Definition: elog.c:84