Line data Source code
1 : /*------------------------------------------------------------------------- 2 : * 3 : * tstoreReceiver.c 4 : * An implementation of DestReceiver that stores the result tuples in 5 : * a Tuplestore. 6 : * 7 : * Optionally, we can force detoasting (but not decompression) of out-of-line 8 : * toasted values. This is to support cursors WITH HOLD, which must retain 9 : * data even if the underlying table is dropped. 10 : * 11 : * Also optionally, we can apply a tuple conversion map before storing. 12 : * 13 : * 14 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group 15 : * Portions Copyright (c) 1994, Regents of the University of California 16 : * 17 : * IDENTIFICATION 18 : * src/backend/executor/tstoreReceiver.c 19 : * 20 : *------------------------------------------------------------------------- 21 : */ 22 : 23 : #include "postgres.h" 24 : 25 : #include "access/detoast.h" 26 : #include "access/tupconvert.h" 27 : #include "executor/tstoreReceiver.h" 28 : #include "varatt.h" 29 : 30 : 31 : typedef struct 32 : { 33 : DestReceiver pub; 34 : /* parameters: */ 35 : Tuplestorestate *tstore; /* where to put the data */ 36 : MemoryContext cxt; /* context containing tstore */ 37 : bool detoast; /* were we told to detoast? */ 38 : TupleDesc target_tupdesc; /* target tupdesc, or NULL if none */ 39 : const char *map_failure_msg; /* tupdesc mapping failure message */ 40 : /* workspace: */ 41 : Datum *outvalues; /* values array for result tuple */ 42 : Datum *tofree; /* temp values to be pfree'd */ 43 : TupleConversionMap *tupmap; /* conversion map, if needed */ 44 : TupleTableSlot *mapslot; /* slot for mapped tuples */ 45 : } TStoreState; 46 : 47 : 48 : static bool tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self); 49 : static bool tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self); 50 : static bool tstoreReceiveSlot_tupmap(TupleTableSlot *slot, DestReceiver *self); 51 : 52 : 53 : /* 54 : * Prepare to receive tuples from executor. 55 : */ 56 : static void 57 52658 : tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo) 58 : { 59 52658 : TStoreState *myState = (TStoreState *) self; 60 52658 : bool needtoast = false; 61 52658 : int natts = typeinfo->natts; 62 : int i; 63 : 64 : /* Check if any columns require detoast work */ 65 52658 : if (myState->detoast) 66 : { 67 342 : for (i = 0; i < natts; i++) 68 : { 69 280 : CompactAttribute *attr = TupleDescCompactAttr(typeinfo, i); 70 : 71 280 : if (attr->attisdropped) 72 0 : continue; 73 280 : if (attr->attlen == -1) 74 : { 75 20 : needtoast = true; 76 20 : break; 77 : } 78 : } 79 : } 80 : 81 : /* Check if tuple conversion is needed */ 82 52658 : if (myState->target_tupdesc) 83 2682 : myState->tupmap = convert_tuples_by_position(typeinfo, 84 : myState->target_tupdesc, 85 : myState->map_failure_msg); 86 : else 87 49976 : myState->tupmap = NULL; 88 : 89 : /* Set up appropriate callback */ 90 52656 : if (needtoast) 91 : { 92 : Assert(!myState->tupmap); 93 20 : myState->pub.receiveSlot = tstoreReceiveSlot_detoast; 94 : /* Create workspace */ 95 20 : myState->outvalues = (Datum *) 96 20 : MemoryContextAlloc(myState->cxt, natts * sizeof(Datum)); 97 20 : myState->tofree = (Datum *) 98 20 : MemoryContextAlloc(myState->cxt, natts * sizeof(Datum)); 99 20 : myState->mapslot = NULL; 100 : } 101 52636 : else if (myState->tupmap) 102 : { 103 38 : myState->pub.receiveSlot = tstoreReceiveSlot_tupmap; 104 38 : myState->outvalues = NULL; 105 38 : myState->tofree = NULL; 106 38 : myState->mapslot = MakeSingleTupleTableSlot(myState->target_tupdesc, 107 : &TTSOpsVirtual); 108 : } 109 : else 110 : { 111 52598 : myState->pub.receiveSlot = tstoreReceiveSlot_notoast; 112 52598 : myState->outvalues = NULL; 113 52598 : myState->tofree = NULL; 114 52598 : myState->mapslot = NULL; 115 : } 116 52656 : } 117 : 118 : /* 119 : * Receive a tuple from the executor and store it in the tuplestore. 120 : * This is for the easy case where we don't have to detoast nor map anything. 121 : */ 122 : static bool 123 526846 : tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self) 124 : { 125 526846 : TStoreState *myState = (TStoreState *) self; 126 : 127 526846 : tuplestore_puttupleslot(myState->tstore, slot); 128 : 129 526846 : return true; 130 : } 131 : 132 : /* 133 : * Receive a tuple from the executor and store it in the tuplestore. 134 : * This is for the case where we have to detoast any toasted values. 135 : */ 136 : static bool 137 46 : tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self) 138 : { 139 46 : TStoreState *myState = (TStoreState *) self; 140 46 : TupleDesc typeinfo = slot->tts_tupleDescriptor; 141 46 : int natts = typeinfo->natts; 142 : int nfree; 143 : int i; 144 : MemoryContext oldcxt; 145 : 146 : /* Make sure the tuple is fully deconstructed */ 147 46 : slot_getallattrs(slot); 148 : 149 : /* 150 : * Fetch back any out-of-line datums. We build the new datums array in 151 : * myState->outvalues[] (but we can re-use the slot's isnull array). Also, 152 : * remember the fetched values to free afterwards. 153 : */ 154 46 : nfree = 0; 155 128 : for (i = 0; i < natts; i++) 156 : { 157 82 : Datum val = slot->tts_values[i]; 158 82 : CompactAttribute *attr = TupleDescCompactAttr(typeinfo, i); 159 : 160 82 : if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i]) 161 : { 162 40 : if (VARATT_IS_EXTERNAL(DatumGetPointer(val))) 163 : { 164 10 : val = PointerGetDatum(detoast_external_attr((struct varlena *) 165 10 : DatumGetPointer(val))); 166 10 : myState->tofree[nfree++] = val; 167 : } 168 : } 169 : 170 82 : myState->outvalues[i] = val; 171 : } 172 : 173 : /* 174 : * Push the modified tuple into the tuplestore. 175 : */ 176 46 : oldcxt = MemoryContextSwitchTo(myState->cxt); 177 46 : tuplestore_putvalues(myState->tstore, typeinfo, 178 46 : myState->outvalues, slot->tts_isnull); 179 46 : MemoryContextSwitchTo(oldcxt); 180 : 181 : /* And release any temporary detoasted values */ 182 56 : for (i = 0; i < nfree; i++) 183 10 : pfree(DatumGetPointer(myState->tofree[i])); 184 : 185 46 : return true; 186 : } 187 : 188 : /* 189 : * Receive a tuple from the executor and store it in the tuplestore. 190 : * This is for the case where we must apply a tuple conversion map. 191 : */ 192 : static bool 193 74 : tstoreReceiveSlot_tupmap(TupleTableSlot *slot, DestReceiver *self) 194 : { 195 74 : TStoreState *myState = (TStoreState *) self; 196 : 197 74 : execute_attr_map_slot(myState->tupmap->attrMap, slot, myState->mapslot); 198 74 : tuplestore_puttupleslot(myState->tstore, myState->mapslot); 199 : 200 74 : return true; 201 : } 202 : 203 : /* 204 : * Clean up at end of an executor run 205 : */ 206 : static void 207 52508 : tstoreShutdownReceiver(DestReceiver *self) 208 : { 209 52508 : TStoreState *myState = (TStoreState *) self; 210 : 211 : /* Release workspace if any */ 212 52508 : if (myState->outvalues) 213 20 : pfree(myState->outvalues); 214 52508 : myState->outvalues = NULL; 215 52508 : if (myState->tofree) 216 20 : pfree(myState->tofree); 217 52508 : myState->tofree = NULL; 218 52508 : if (myState->tupmap) 219 38 : free_conversion_map(myState->tupmap); 220 52508 : myState->tupmap = NULL; 221 52508 : if (myState->mapslot) 222 38 : ExecDropSingleTupleTableSlot(myState->mapslot); 223 52508 : myState->mapslot = NULL; 224 52508 : } 225 : 226 : /* 227 : * Destroy receiver when done with it 228 : */ 229 : static void 230 52490 : tstoreDestroyReceiver(DestReceiver *self) 231 : { 232 52490 : pfree(self); 233 52490 : } 234 : 235 : /* 236 : * Initially create a DestReceiver object. 237 : */ 238 : DestReceiver * 239 52924 : CreateTuplestoreDestReceiver(void) 240 : { 241 52924 : TStoreState *self = (TStoreState *) palloc0(sizeof(TStoreState)); 242 : 243 52924 : self->pub.receiveSlot = tstoreReceiveSlot_notoast; /* might change */ 244 52924 : self->pub.rStartup = tstoreStartupReceiver; 245 52924 : self->pub.rShutdown = tstoreShutdownReceiver; 246 52924 : self->pub.rDestroy = tstoreDestroyReceiver; 247 52924 : self->pub.mydest = DestTuplestore; 248 : 249 : /* private fields will be set by SetTuplestoreDestReceiverParams */ 250 : 251 52924 : return (DestReceiver *) self; 252 : } 253 : 254 : /* 255 : * Set parameters for a TuplestoreDestReceiver 256 : * 257 : * tStore: where to store the tuples 258 : * tContext: memory context containing tStore 259 : * detoast: forcibly detoast contained data? 260 : * target_tupdesc: if not NULL, forcibly convert tuples to this rowtype 261 : * map_failure_msg: error message to use if mapping to target_tupdesc fails 262 : * 263 : * We don't currently support both detoast and target_tupdesc at the same 264 : * time, just because no existing caller needs that combination. 265 : */ 266 : void 267 52924 : SetTuplestoreDestReceiverParams(DestReceiver *self, 268 : Tuplestorestate *tStore, 269 : MemoryContext tContext, 270 : bool detoast, 271 : TupleDesc target_tupdesc, 272 : const char *map_failure_msg) 273 : { 274 52924 : TStoreState *myState = (TStoreState *) self; 275 : 276 : Assert(!(detoast && target_tupdesc)); 277 : 278 : Assert(myState->pub.mydest == DestTuplestore); 279 52924 : myState->tstore = tStore; 280 52924 : myState->cxt = tContext; 281 52924 : myState->detoast = detoast; 282 52924 : myState->target_tupdesc = target_tupdesc; 283 52924 : myState->map_failure_msg = map_failure_msg; 284 52924 : }