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