Age Owner Branch data TLA 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 *
5927 tgl@sss.pgh.pa.us 60 :CBC 4812 : 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 */
2141 michael@paquier.xyz 69 : 4812 : attrMap = build_attrmap_by_position(indesc, outdesc, msg);
70 : :
71 [ + + ]: 4795 : if (attrMap == NULL)
72 : : {
73 : : /* runtime conversion is not needed */
5927 tgl@sss.pgh.pa.us 74 : 4747 : return NULL;
75 : : }
76 : :
77 : : /* Prepare the map structure */
78 : 48 : map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
79 : 48 : map->indesc = indesc;
80 : 48 : map->outdesc = outdesc;
81 : 48 : map->attrMap = attrMap;
82 : : /* preallocate workspace for Datum arrays */
2141 michael@paquier.xyz 83 : 48 : n = outdesc->natts + 1; /* +1 for NULL */
5927 tgl@sss.pgh.pa.us 84 : 48 : map->outvalues = (Datum *) palloc(n * sizeof(Datum));
85 : 48 : map->outisnull = (bool *) palloc(n * sizeof(bool));
5723 bruce@momjian.us 86 : 48 : n = indesc->natts + 1; /* +1 for NULL */
5927 tgl@sss.pgh.pa.us 87 : 48 : map->invalues = (Datum *) palloc(n * sizeof(Datum));
88 : 48 : map->inisnull = (bool *) palloc(n * sizeof(bool));
3051 89 : 48 : map->invalues[0] = (Datum) 0; /* set up the NULL entry */
5927 90 : 48 : map->inisnull[0] = true;
91 : :
92 : 48 : 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 : 1909 : convert_tuples_by_name(TupleDesc indesc,
104 : : TupleDesc outdesc)
105 : : {
106 : : AttrMap *attrMap;
107 : :
108 : : /* Verify compatibility and prepare attribute-number map */
1064 alvherre@alvh.no-ip. 109 : 1909 : attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false);
110 : :
2583 andres@anarazel.de 111 [ + + ]: 1909 : if (attrMap == NULL)
112 : : {
113 : : /* runtime conversion is not needed */
5927 tgl@sss.pgh.pa.us 114 : 1491 : return NULL;
115 : : }
116 : :
1061 alvherre@alvh.no-ip. 117 : 418 : 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 : 1162 : convert_tuples_by_name_attrmap(TupleDesc indesc,
126 : : TupleDesc outdesc,
127 : : AttrMap *attrMap)
128 : : {
129 : 1162 : int n = outdesc->natts;
130 : : TupleConversionMap *map;
131 : :
132 [ - + ]: 1162 : Assert(attrMap != NULL);
133 : :
134 : : /* Prepare the map structure */
5927 tgl@sss.pgh.pa.us 135 : 1162 : map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
136 : 1162 : map->indesc = indesc;
137 : 1162 : map->outdesc = outdesc;
138 : 1162 : map->attrMap = attrMap;
139 : : /* preallocate workspace for Datum arrays */
140 : 1162 : map->outvalues = (Datum *) palloc(n * sizeof(Datum));
141 : 1162 : map->outisnull = (bool *) palloc(n * sizeof(bool));
5723 bruce@momjian.us 142 : 1162 : n = indesc->natts + 1; /* +1 for NULL */
5927 tgl@sss.pgh.pa.us 143 : 1162 : map->invalues = (Datum *) palloc(n * sizeof(Datum));
144 : 1162 : map->inisnull = (bool *) palloc(n * sizeof(bool));
3051 145 : 1162 : map->invalues[0] = (Datum) 0; /* set up the NULL entry */
5927 146 : 1162 : map->inisnull[0] = true;
147 : :
148 : 1162 : return map;
149 : : }
150 : :
151 : : /*
152 : : * Perform conversion of a tuple according to the map.
153 : : */
154 : : HeapTuple
2583 andres@anarazel.de 155 : 53707 : execute_attr_map_tuple(HeapTuple tuple, TupleConversionMap *map)
156 : : {
2141 michael@paquier.xyz 157 : 53707 : AttrMap *attrMap = map->attrMap;
5927 tgl@sss.pgh.pa.us 158 : 53707 : Datum *invalues = map->invalues;
159 : 53707 : bool *inisnull = map->inisnull;
160 : 53707 : Datum *outvalues = map->outvalues;
161 : 53707 : 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 : 53707 : heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1);
170 : :
171 : : /*
172 : : * Transpose into proper fields of the new tuple.
173 : : */
2141 michael@paquier.xyz 174 [ - + ]: 53707 : Assert(attrMap->maplen == map->outdesc->natts);
175 [ + + ]: 213703 : for (i = 0; i < attrMap->maplen; i++)
176 : : {
177 : 159996 : int j = attrMap->attnums[i];
178 : :
5927 tgl@sss.pgh.pa.us 179 : 159996 : outvalues[i] = invalues[j];
180 : 159996 : outisnull[i] = inisnull[j];
181 : : }
182 : :
183 : : /*
184 : : * Now form the new tuple.
185 : : */
186 : 53707 : 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 *
2141 michael@paquier.xyz 193 : 71912 : 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 */
2583 andres@anarazel.de 205 [ + - - + ]: 71912 : Assert(in_slot->tts_tupleDescriptor != NULL &&
206 : : out_slot->tts_tupleDescriptor != NULL);
207 [ + - - + ]: 71912 : Assert(in_slot->tts_values != NULL && out_slot->tts_values != NULL);
208 : :
209 : 71912 : outnatts = out_slot->tts_tupleDescriptor->natts;
210 : :
211 : : /* Extract all the values of the in slot. */
212 : 71912 : slot_getallattrs(in_slot);
213 : :
214 : : /* Before doing the mapping, clear any old contents from the out slot */
215 : 71912 : ExecClearTuple(out_slot);
216 : :
217 : 71912 : invalues = in_slot->tts_values;
218 : 71912 : inisnull = in_slot->tts_isnull;
219 : 71912 : outvalues = out_slot->tts_values;
220 : 71912 : outisnull = out_slot->tts_isnull;
221 : :
222 : : /* Transpose into proper fields of the out slot. */
223 [ + + ]: 289954 : for (i = 0; i < outnatts; i++)
224 : : {
2141 michael@paquier.xyz 225 : 218042 : int j = attrMap->attnums[i] - 1;
226 : :
227 : : /* attrMap->attnums[i] == 0 means it's a NULL datum. */
2583 andres@anarazel.de 228 [ + + ]: 218042 : if (j == -1)
229 : : {
230 : 1307 : outvalues[i] = (Datum) 0;
231 : 1307 : outisnull[i] = true;
232 : : }
233 : : else
234 : : {
235 : 216735 : outvalues[i] = invalues[j];
236 : 216735 : outisnull[i] = inisnull[j];
237 : : }
238 : : }
239 : :
240 : 71912 : ExecStoreVirtualTuple(out_slot);
241 : :
242 : 71912 : 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 *
1723 heikki.linnakangas@i 253 : 247 : 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 [ + + ]: 247 : if (in_cols == NULL)
260 : 2 : return NULL;
261 : :
262 : : /*
263 : : * For each output column, check which input column it corresponds to.
264 : : */
265 : 245 : out_cols = NULL;
266 : :
267 : 245 : for (out_attnum = FirstLowInvalidHeapAttributeNumber;
268 [ + + ]: 3124 : out_attnum <= attrMap->maplen;
269 : 2879 : out_attnum++)
270 : : {
271 : : int in_attnum;
272 : :
273 [ + + ]: 2879 : if (out_attnum < 0)
274 : : {
275 : : /* System column. No mapping. */
276 : 1715 : in_attnum = out_attnum;
277 : : }
278 [ + + ]: 1164 : else if (out_attnum == 0)
279 : 245 : continue;
280 : : else
281 : : {
282 : : /* normal user column */
283 : 919 : in_attnum = attrMap->attnums[out_attnum - 1];
284 : :
285 [ + + ]: 919 : if (in_attnum == 0)
286 : 71 : continue;
287 : : }
288 : :
289 [ + + ]: 2563 : if (bms_is_member(in_attnum - FirstLowInvalidHeapAttributeNumber, in_cols))
290 : 266 : out_cols = bms_add_member(out_cols, out_attnum - FirstLowInvalidHeapAttributeNumber);
291 : : }
292 : :
293 : 245 : return out_cols;
294 : : }
295 : :
296 : : /*
297 : : * Free a TupleConversionMap structure.
298 : : */
299 : : void
5927 tgl@sss.pgh.pa.us 300 : 87 : free_conversion_map(TupleConversionMap *map)
301 : : {
302 : : /* indesc and outdesc are not ours to free */
2141 michael@paquier.xyz 303 : 87 : free_attrmap(map->attrMap);
5927 tgl@sss.pgh.pa.us 304 : 87 : pfree(map->invalues);
305 : 87 : pfree(map->inisnull);
306 : 87 : pfree(map->outvalues);
307 : 87 : pfree(map->outisnull);
308 : 87 : pfree(map);
309 : 87 : }
|