Age Owner Branch data TLA 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
8208 tgl@sss.pgh.pa.us 57 :CBC 26016 : tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
58 : : {
6174 59 : 26016 : TStoreState *myState = (TStoreState *) self;
60 : 26016 : bool needtoast = false;
61 : 26016 : int natts = typeinfo->natts;
62 : : int i;
63 : :
64 : : /* Check if any columns require detoast work */
65 [ + + ]: 26016 : if (myState->detoast)
66 : : {
67 [ + + ]: 171 : for (i = 0; i < natts; i++)
68 : : {
311 drowley@postgresql.o 69 : 140 : CompactAttribute *attr = TupleDescCompactAttr(typeinfo, i);
70 : :
2990 andres@anarazel.de 71 [ - + ]: 140 : if (attr->attisdropped)
6174 tgl@sss.pgh.pa.us 72 :UBC 0 : continue;
2990 andres@anarazel.de 73 [ + + ]:CBC 140 : if (attr->attlen == -1)
74 : : {
6174 tgl@sss.pgh.pa.us 75 : 10 : needtoast = true;
76 : 10 : break;
77 : : }
78 : : }
79 : : }
80 : :
81 : : /* Check if tuple conversion is needed */
1963 82 [ + + ]: 26016 : if (myState->target_tupdesc)
83 : 1341 : myState->tupmap = convert_tuples_by_position(typeinfo,
84 : : myState->target_tupdesc,
85 : : myState->map_failure_msg);
86 : : else
87 : 24675 : myState->tupmap = NULL;
88 : :
89 : : /* Set up appropriate callback */
6174 90 [ + + ]: 26015 : if (needtoast)
91 : : {
1963 92 [ - + ]: 10 : Assert(!myState->tupmap);
6174 93 : 10 : myState->pub.receiveSlot = tstoreReceiveSlot_detoast;
94 : : /* Create workspace */
95 : 10 : myState->outvalues = (Datum *)
96 : 10 : MemoryContextAlloc(myState->cxt, natts * sizeof(Datum));
97 : 10 : myState->tofree = (Datum *)
98 : 10 : MemoryContextAlloc(myState->cxt, natts * sizeof(Datum));
1963 99 : 10 : myState->mapslot = NULL;
100 : : }
101 [ + + ]: 26005 : else if (myState->tupmap)
102 : : {
103 : 19 : myState->pub.receiveSlot = tstoreReceiveSlot_tupmap;
104 : 19 : myState->outvalues = NULL;
105 : 19 : myState->tofree = NULL;
106 : 19 : myState->mapslot = MakeSingleTupleTableSlot(myState->target_tupdesc,
107 : : &TTSOpsVirtual);
108 : : }
109 : : else
110 : : {
6174 111 : 25986 : myState->pub.receiveSlot = tstoreReceiveSlot_notoast;
112 : 25986 : myState->outvalues = NULL;
113 : 25986 : myState->tofree = NULL;
1963 114 : 25986 : myState->mapslot = NULL;
115 : : }
8250 bruce@momjian.us 116 : 26015 : }
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
6174 tgl@sss.pgh.pa.us 123 : 263125 : tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self)
124 : : {
8250 bruce@momjian.us 125 : 263125 : TStoreState *myState = (TStoreState *) self;
126 : :
7062 tgl@sss.pgh.pa.us 127 : 263125 : tuplestore_puttupleslot(myState->tstore, slot);
128 : :
3430 rhaas@postgresql.org 129 : 263125 : 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
6174 tgl@sss.pgh.pa.us 137 : 23 : tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
138 : : {
139 : 23 : TStoreState *myState = (TStoreState *) self;
140 : 23 : TupleDesc typeinfo = slot->tts_tupleDescriptor;
141 : 23 : int natts = typeinfo->natts;
142 : : int nfree;
143 : : int i;
144 : : MemoryContext oldcxt;
145 : :
146 : : /* Make sure the tuple is fully deconstructed */
147 : 23 : 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 : 23 : nfree = 0;
155 [ + + ]: 64 : for (i = 0; i < natts; i++)
156 : : {
157 : 41 : Datum val = slot->tts_values[i];
311 drowley@postgresql.o 158 : 41 : CompactAttribute *attr = TupleDescCompactAttr(typeinfo, i);
159 : :
2990 andres@anarazel.de 160 [ + - + + : 41 : if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i])
+ + ]
161 : : {
6174 tgl@sss.pgh.pa.us 162 [ + + ]: 20 : if (VARATT_IS_EXTERNAL(DatumGetPointer(val)))
163 : : {
2215 rhaas@postgresql.org 164 : 5 : val = PointerGetDatum(detoast_external_attr((struct varlena *)
3050 tgl@sss.pgh.pa.us 165 : 5 : DatumGetPointer(val)));
6174 166 : 5 : myState->tofree[nfree++] = val;
167 : : }
168 : : }
169 : :
170 : 41 : myState->outvalues[i] = val;
171 : : }
172 : :
173 : : /*
174 : : * Push the modified tuple into the tuplestore.
175 : : */
176 : 23 : oldcxt = MemoryContextSwitchTo(myState->cxt);
177 : 23 : tuplestore_putvalues(myState->tstore, typeinfo,
178 : 23 : myState->outvalues, slot->tts_isnull);
179 : 23 : MemoryContextSwitchTo(oldcxt);
180 : :
181 : : /* And release any temporary detoasted values */
182 [ + + ]: 28 : for (i = 0; i < nfree; i++)
183 : 5 : pfree(DatumGetPointer(myState->tofree[i]));
184 : :
3430 rhaas@postgresql.org 185 : 23 : 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
1963 tgl@sss.pgh.pa.us 193 : 37 : tstoreReceiveSlot_tupmap(TupleTableSlot *slot, DestReceiver *self)
194 : : {
195 : 37 : TStoreState *myState = (TStoreState *) self;
196 : :
197 : 37 : execute_attr_map_slot(myState->tupmap->attrMap, slot, myState->mapslot);
198 : 37 : tuplestore_puttupleslot(myState->tstore, myState->mapslot);
199 : :
200 : 37 : return true;
201 : : }
202 : :
203 : : /*
204 : : * Clean up at end of an executor run
205 : : */
206 : : static void
8210 207 : 25940 : tstoreShutdownReceiver(DestReceiver *self)
208 : : {
6174 209 : 25940 : TStoreState *myState = (TStoreState *) self;
210 : :
211 : : /* Release workspace if any */
212 [ + + ]: 25940 : if (myState->outvalues)
213 : 10 : pfree(myState->outvalues);
214 : 25940 : myState->outvalues = NULL;
215 [ + + ]: 25940 : if (myState->tofree)
216 : 10 : pfree(myState->tofree);
217 : 25940 : myState->tofree = NULL;
1963 218 [ + + ]: 25940 : if (myState->tupmap)
219 : 19 : free_conversion_map(myState->tupmap);
220 : 25940 : myState->tupmap = NULL;
221 [ + + ]: 25940 : if (myState->mapslot)
222 : 19 : ExecDropSingleTupleTableSlot(myState->mapslot);
223 : 25940 : myState->mapslot = NULL;
8250 bruce@momjian.us 224 : 25940 : }
225 : :
226 : : /*
227 : : * Destroy receiver when done with it
228 : : */
229 : : static void
8210 tgl@sss.pgh.pa.us 230 : 25931 : tstoreDestroyReceiver(DestReceiver *self)
231 : : {
232 : 25931 : pfree(self);
233 : 25931 : }
234 : :
235 : : /*
236 : : * Initially create a DestReceiver object.
237 : : */
238 : : DestReceiver *
6175 239 : 26149 : CreateTuplestoreDestReceiver(void)
240 : : {
241 : 26149 : TStoreState *self = (TStoreState *) palloc0(sizeof(TStoreState));
242 : :
6174 243 : 26149 : self->pub.receiveSlot = tstoreReceiveSlot_notoast; /* might change */
8118 244 : 26149 : self->pub.rStartup = tstoreStartupReceiver;
245 : 26149 : self->pub.rShutdown = tstoreShutdownReceiver;
246 : 26149 : self->pub.rDestroy = tstoreDestroyReceiver;
7298 alvherre@alvh.no-ip. 247 : 26149 : self->pub.mydest = DestTuplestore;
248 : :
249 : : /* private fields will be set by SetTuplestoreDestReceiverParams */
250 : :
8250 bruce@momjian.us 251 : 26149 : 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
6175 tgl@sss.pgh.pa.us 267 : 26149 : SetTuplestoreDestReceiverParams(DestReceiver *self,
268 : : Tuplestorestate *tStore,
269 : : MemoryContext tContext,
270 : : bool detoast,
271 : : TupleDesc target_tupdesc,
272 : : const char *map_failure_msg)
273 : : {
274 : 26149 : TStoreState *myState = (TStoreState *) self;
275 : :
1963 276 [ + + - + ]: 26149 : Assert(!(detoast && target_tupdesc));
277 : :
6175 278 [ - + ]: 26149 : Assert(myState->pub.mydest == DestTuplestore);
279 : 26149 : myState->tstore = tStore;
280 : 26149 : myState->cxt = tContext;
6174 281 : 26149 : myState->detoast = detoast;
1963 282 : 26149 : myState->target_tupdesc = target_tupdesc;
283 : 26149 : myState->map_failure_msg = map_failure_msg;
6175 284 : 26149 : }
|