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-2025, 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 9624 : convert_tuples_by_position(TupleDesc indesc,
61 : TupleDesc outdesc,
62 : const char *msg)
63 : {
64 : TupleConversionMap *map;
65 : int n;
66 : AttrMap *attrMap;
67 :
68 : /* Verify compatibility and prepare attribute-number map */
69 9624 : attrMap = build_attrmap_by_position(indesc, outdesc, msg);
70 :
71 9590 : if (attrMap == NULL)
72 : {
73 : /* runtime conversion is not needed */
74 9494 : return NULL;
75 : }
76 :
77 : /* Prepare the map structure */
78 96 : map = palloc_object(TupleConversionMap);
79 96 : map->indesc = indesc;
80 96 : map->outdesc = outdesc;
81 96 : map->attrMap = attrMap;
82 : /* preallocate workspace for Datum arrays */
83 96 : n = outdesc->natts + 1; /* +1 for NULL */
84 96 : map->outvalues = palloc_array(Datum, n);
85 96 : map->outisnull = palloc_array(bool, n);
86 96 : n = indesc->natts + 1; /* +1 for NULL */
87 96 : map->invalues = palloc_array(Datum, n);
88 96 : map->inisnull = palloc_array(bool, n);
89 96 : map->invalues[0] = (Datum) 0; /* set up the NULL entry */
90 96 : map->inisnull[0] = true;
91 :
92 96 : return map;
93 : }
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 3822 : convert_tuples_by_name(TupleDesc indesc,
104 : TupleDesc outdesc)
105 : {
106 : AttrMap *attrMap;
107 :
108 : /* Verify compatibility and prepare attribute-number map */
109 3822 : attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false);
110 :
111 3822 : if (attrMap == NULL)
112 : {
113 : /* runtime conversion is not needed */
114 2984 : return NULL;
115 : }
116 :
117 838 : return convert_tuples_by_name_attrmap(indesc, outdesc, attrMap);
118 : }
119 :
120 : /*
121 : * Set up tuple conversion for input and output TupleDescs using the given
122 : * AttrMap.
123 : */
124 : TupleConversionMap *
125 2334 : convert_tuples_by_name_attrmap(TupleDesc indesc,
126 : TupleDesc outdesc,
127 : AttrMap *attrMap)
128 : {
129 2334 : int n = outdesc->natts;
130 : TupleConversionMap *map;
131 :
132 : Assert(attrMap != NULL);
133 :
134 : /* Prepare the map structure */
135 2334 : map = palloc_object(TupleConversionMap);
136 2334 : map->indesc = indesc;
137 2334 : map->outdesc = outdesc;
138 2334 : map->attrMap = attrMap;
139 : /* preallocate workspace for Datum arrays */
140 2334 : map->outvalues = palloc_array(Datum, n);
141 2334 : map->outisnull = palloc_array(bool, n);
142 2334 : n = indesc->natts + 1; /* +1 for NULL */
143 2334 : map->invalues = palloc_array(Datum, n);
144 2334 : map->inisnull = palloc_array(bool, n);
145 2334 : map->invalues[0] = (Datum) 0; /* set up the NULL entry */
146 2334 : map->inisnull[0] = true;
147 :
148 2334 : return map;
149 : }
150 :
151 : /*
152 : * Perform conversion of a tuple according to the map.
153 : */
154 : HeapTuple
155 107110 : execute_attr_map_tuple(HeapTuple tuple, TupleConversionMap *map)
156 : {
157 107110 : AttrMap *attrMap = map->attrMap;
158 107110 : Datum *invalues = map->invalues;
159 107110 : bool *inisnull = map->inisnull;
160 107110 : Datum *outvalues = map->outvalues;
161 107110 : bool *outisnull = map->outisnull;
162 : 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 107110 : heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1);
170 :
171 : /*
172 : * Transpose into proper fields of the new tuple.
173 : */
174 : Assert(attrMap->maplen == map->outdesc->natts);
175 426494 : for (i = 0; i < attrMap->maplen; i++)
176 : {
177 319384 : int j = attrMap->attnums[i];
178 :
179 319384 : outvalues[i] = invalues[j];
180 319384 : outisnull[i] = inisnull[j];
181 : }
182 :
183 : /*
184 : * Now form the new tuple.
185 : */
186 107110 : return heap_form_tuple(map->outdesc, outvalues, outisnull);
187 : }
188 :
189 : /*
190 : * Perform conversion of a tuple slot according to the map.
191 : */
192 : TupleTableSlot *
193 143842 : execute_attr_map_slot(AttrMap *attrMap,
194 : TupleTableSlot *in_slot,
195 : TupleTableSlot *out_slot)
196 : {
197 : Datum *invalues;
198 : bool *inisnull;
199 : Datum *outvalues;
200 : bool *outisnull;
201 : int outnatts;
202 : int i;
203 :
204 : /* Sanity checks */
205 : Assert(in_slot->tts_tupleDescriptor != NULL &&
206 : out_slot->tts_tupleDescriptor != NULL);
207 : Assert(in_slot->tts_values != NULL && out_slot->tts_values != NULL);
208 :
209 143842 : outnatts = out_slot->tts_tupleDescriptor->natts;
210 :
211 : /* Extract all the values of the in slot. */
212 143842 : slot_getallattrs(in_slot);
213 :
214 : /* Before doing the mapping, clear any old contents from the out slot */
215 143842 : ExecClearTuple(out_slot);
216 :
217 143842 : invalues = in_slot->tts_values;
218 143842 : inisnull = in_slot->tts_isnull;
219 143842 : outvalues = out_slot->tts_values;
220 143842 : outisnull = out_slot->tts_isnull;
221 :
222 : /* Transpose into proper fields of the out slot. */
223 579962 : for (i = 0; i < outnatts; i++)
224 : {
225 436120 : int j = attrMap->attnums[i] - 1;
226 :
227 : /* attrMap->attnums[i] == 0 means it's a NULL datum. */
228 436120 : if (j == -1)
229 : {
230 2614 : outvalues[i] = (Datum) 0;
231 2614 : outisnull[i] = true;
232 : }
233 : else
234 : {
235 433506 : outvalues[i] = invalues[j];
236 433506 : outisnull[i] = inisnull[j];
237 : }
238 : }
239 :
240 143842 : ExecStoreVirtualTuple(out_slot);
241 :
242 143842 : return out_slot;
243 : }
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 494 : execute_attr_map_cols(AttrMap *attrMap, Bitmapset *in_cols)
254 : {
255 : Bitmapset *out_cols;
256 : int out_attnum;
257 :
258 : /* fast path for the common trivial case */
259 494 : if (in_cols == NULL)
260 4 : return NULL;
261 :
262 : /*
263 : * For each output column, check which input column it corresponds to.
264 : */
265 490 : out_cols = NULL;
266 :
267 490 : for (out_attnum = FirstLowInvalidHeapAttributeNumber;
268 6248 : out_attnum <= attrMap->maplen;
269 5758 : out_attnum++)
270 : {
271 : int in_attnum;
272 :
273 5758 : if (out_attnum < 0)
274 : {
275 : /* System column. No mapping. */
276 3430 : in_attnum = out_attnum;
277 : }
278 2328 : else if (out_attnum == 0)
279 490 : continue;
280 : else
281 : {
282 : /* normal user column */
283 1838 : in_attnum = attrMap->attnums[out_attnum - 1];
284 :
285 1838 : if (in_attnum == 0)
286 142 : continue;
287 : }
288 :
289 5126 : if (bms_is_member(in_attnum - FirstLowInvalidHeapAttributeNumber, in_cols))
290 532 : out_cols = bms_add_member(out_cols, out_attnum - FirstLowInvalidHeapAttributeNumber);
291 : }
292 :
293 490 : return out_cols;
294 : }
295 :
296 : /*
297 : * Free a TupleConversionMap structure.
298 : */
299 : void
300 170 : free_conversion_map(TupleConversionMap *map)
301 : {
302 : /* indesc and outdesc are not ours to free */
303 170 : free_attrmap(map->attrMap);
304 170 : pfree(map->invalues);
305 170 : pfree(map->inisnull);
306 170 : pfree(map->outvalues);
307 170 : pfree(map->outisnull);
308 170 : pfree(map);
309 170 : }
|