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-2020, 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 * 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 * 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 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 * 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 * 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 + 1; 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 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