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