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