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