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-2021, 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/tupconvert.h"
22 #include "executor/tuptable.h"
23
24
25 /*
26 * The conversion setup routines have the following common API:
27 *
28 * The setup routine checks using attmap.c whether the given source and
29 * destination tuple descriptors are logically compatible. If not, it throws
30 * an error. If so, it returns NULL if they are physically compatible (ie, no
31 * conversion is needed), else a TupleConversionMap that can be used by
32 * execute_attr_map_tuple or execute_attr_map_slot to perform the conversion.
33 *
34 * The TupleConversionMap, if needed, is palloc'd in the caller's memory
35 * context. Also, the given tuple descriptors are referenced by the map,
36 * so they must survive as long as the map is needed.
37 *
38 * The caller must supply a suitable primary error message to be used if
39 * a compatibility error is thrown. Recommended coding practice is to use
40 * gettext_noop() on this string, so that it is translatable but won't
41 * actually be translated unless the error gets thrown.
42 *
43 *
44 * Implementation notes:
45 *
46 * The key component of a TupleConversionMap is an attrMap[] array with
47 * one entry per output column. This entry contains the 1-based index of
48 * the corresponding input column, or zero to force a NULL value (for
49 * a dropped output column). The TupleConversionMap also contains workspace
50 * arrays.
51 */
52
53
54 /*
55 * Set up for tuple conversion, matching input and output columns by
56 * position. (Dropped columns are ignored in both input and output.)
57 */
58 TupleConversionMap *
convert_tuples_by_position(TupleDesc indesc,TupleDesc outdesc,const char * msg)59 convert_tuples_by_position(TupleDesc indesc,
60 TupleDesc outdesc,
61 const char *msg)
62 {
63 TupleConversionMap *map;
64 int n;
65 AttrMap *attrMap;
66
67 /* Verify compatibility and prepare attribute-number map */
68 attrMap = build_attrmap_by_position(indesc, outdesc, msg);
69
70 if (attrMap == NULL)
71 {
72 /* runtime conversion is not needed */
73 return NULL;
74 }
75
76 /* Prepare the map structure */
77 map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
78 map->indesc = indesc;
79 map->outdesc = outdesc;
80 map->attrMap = attrMap;
81 /* preallocate workspace for Datum arrays */
82 n = outdesc->natts + 1; /* +1 for NULL */
83 map->outvalues = (Datum *) palloc(n * sizeof(Datum));
84 map->outisnull = (bool *) palloc(n * sizeof(bool));
85 n = indesc->natts + 1; /* +1 for NULL */
86 map->invalues = (Datum *) palloc(n * sizeof(Datum));
87 map->inisnull = (bool *) palloc(n * sizeof(bool));
88 map->invalues[0] = (Datum) 0; /* set up the NULL entry */
89 map->inisnull[0] = true;
90
91 return map;
92 }
93
94 /*
95 * Set up for tuple conversion, matching input and output columns by name.
96 * (Dropped columns are ignored in both input and output.) This is intended
97 * for use when the rowtypes are related by inheritance, so we expect an exact
98 * match of both type and typmod. The error messages will be a bit unhelpful
99 * unless both rowtypes are named composite types.
100 */
101 TupleConversionMap *
convert_tuples_by_name(TupleDesc indesc,TupleDesc outdesc)102 convert_tuples_by_name(TupleDesc indesc,
103 TupleDesc outdesc)
104 {
105 TupleConversionMap *map;
106 AttrMap *attrMap;
107 int n = outdesc->natts;
108
109 /* Verify compatibility and prepare attribute-number map */
110 attrMap = build_attrmap_by_name_if_req(indesc, outdesc);
111
112 if (attrMap == NULL)
113 {
114 /* runtime conversion is not needed */
115 return NULL;
116 }
117
118 /* Prepare the map structure */
119 map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
120 map->indesc = indesc;
121 map->outdesc = outdesc;
122 map->attrMap = attrMap;
123 /* preallocate workspace for Datum arrays */
124 map->outvalues = (Datum *) palloc(n * sizeof(Datum));
125 map->outisnull = (bool *) palloc(n * sizeof(bool));
126 n = indesc->natts + 1; /* +1 for NULL */
127 map->invalues = (Datum *) palloc(n * sizeof(Datum));
128 map->inisnull = (bool *) palloc(n * sizeof(bool));
129 map->invalues[0] = (Datum) 0; /* set up the NULL entry */
130 map->inisnull[0] = true;
131
132 return map;
133 }
134
135 /*
136 * Perform conversion of a tuple according to the map.
137 */
138 HeapTuple
execute_attr_map_tuple(HeapTuple tuple,TupleConversionMap * map)139 execute_attr_map_tuple(HeapTuple tuple, TupleConversionMap *map)
140 {
141 AttrMap *attrMap = map->attrMap;
142 Datum *invalues = map->invalues;
143 bool *inisnull = map->inisnull;
144 Datum *outvalues = map->outvalues;
145 bool *outisnull = map->outisnull;
146 int i;
147
148 /*
149 * Extract all the values of the old tuple, offsetting the arrays so that
150 * invalues[0] is left NULL and invalues[1] is the first source attribute;
151 * this exactly matches the numbering convention in attrMap.
152 */
153 heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1);
154
155 /*
156 * Transpose into proper fields of the new tuple.
157 */
158 Assert(attrMap->maplen == map->outdesc->natts);
159 for (i = 0; i < attrMap->maplen; i++)
160 {
161 int j = attrMap->attnums[i];
162
163 outvalues[i] = invalues[j];
164 outisnull[i] = inisnull[j];
165 }
166
167 /*
168 * Now form the new tuple.
169 */
170 return heap_form_tuple(map->outdesc, outvalues, outisnull);
171 }
172
173 /*
174 * Perform conversion of a tuple slot according to the map.
175 */
176 TupleTableSlot *
execute_attr_map_slot(AttrMap * attrMap,TupleTableSlot * in_slot,TupleTableSlot * out_slot)177 execute_attr_map_slot(AttrMap *attrMap,
178 TupleTableSlot *in_slot,
179 TupleTableSlot *out_slot)
180 {
181 Datum *invalues;
182 bool *inisnull;
183 Datum *outvalues;
184 bool *outisnull;
185 int outnatts;
186 int i;
187
188 /* Sanity checks */
189 Assert(in_slot->tts_tupleDescriptor != NULL &&
190 out_slot->tts_tupleDescriptor != NULL);
191 Assert(in_slot->tts_values != NULL && out_slot->tts_values != NULL);
192
193 outnatts = out_slot->tts_tupleDescriptor->natts;
194
195 /* Extract all the values of the in slot. */
196 slot_getallattrs(in_slot);
197
198 /* Before doing the mapping, clear any old contents from the out slot */
199 ExecClearTuple(out_slot);
200
201 invalues = in_slot->tts_values;
202 inisnull = in_slot->tts_isnull;
203 outvalues = out_slot->tts_values;
204 outisnull = out_slot->tts_isnull;
205
206 /* Transpose into proper fields of the out slot. */
207 for (i = 0; i < outnatts; i++)
208 {
209 int j = attrMap->attnums[i] - 1;
210
211 /* attrMap->attnums[i] == 0 means it's a NULL datum. */
212 if (j == -1)
213 {
214 outvalues[i] = (Datum) 0;
215 outisnull[i] = true;
216 }
217 else
218 {
219 outvalues[i] = invalues[j];
220 outisnull[i] = inisnull[j];
221 }
222 }
223
224 ExecStoreVirtualTuple(out_slot);
225
226 return out_slot;
227 }
228
229 /*
230 * Perform conversion of bitmap of columns according to the map.
231 *
232 * The input and output bitmaps are offset by
233 * FirstLowInvalidHeapAttributeNumber to accommodate system cols, like the
234 * column-bitmaps in RangeTblEntry.
235 */
236 Bitmapset *
execute_attr_map_cols(AttrMap * attrMap,Bitmapset * in_cols)237 execute_attr_map_cols(AttrMap *attrMap, Bitmapset *in_cols)
238 {
239 Bitmapset *out_cols;
240 int out_attnum;
241
242 /* fast path for the common trivial case */
243 if (in_cols == NULL)
244 return NULL;
245
246 /*
247 * For each output column, check which input column it corresponds to.
248 */
249 out_cols = NULL;
250
251 for (out_attnum = FirstLowInvalidHeapAttributeNumber;
252 out_attnum <= attrMap->maplen;
253 out_attnum++)
254 {
255 int in_attnum;
256
257 if (out_attnum < 0)
258 {
259 /* System column. No mapping. */
260 in_attnum = out_attnum;
261 }
262 else if (out_attnum == 0)
263 continue;
264 else
265 {
266 /* normal user column */
267 in_attnum = attrMap->attnums[out_attnum - 1];
268
269 if (in_attnum == 0)
270 continue;
271 }
272
273 if (bms_is_member(in_attnum - FirstLowInvalidHeapAttributeNumber, in_cols))
274 out_cols = bms_add_member(out_cols, out_attnum - FirstLowInvalidHeapAttributeNumber);
275 }
276
277 return out_cols;
278 }
279
280 /*
281 * Free a TupleConversionMap structure.
282 */
283 void
free_conversion_map(TupleConversionMap * map)284 free_conversion_map(TupleConversionMap *map)
285 {
286 /* indesc and outdesc are not ours to free */
287 free_attrmap(map->attrMap);
288 pfree(map->invalues);
289 pfree(map->inisnull);
290 pfree(map->outvalues);
291 pfree(map->outisnull);
292 pfree(map);
293 }
294