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 27458 : tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
58 : {
59 27458 : TStoreState *myState = (TStoreState *) self;
60 27458 : bool needtoast = false;
61 27458 : int natts = typeinfo->natts;
62 : int i;
63 :
64 : /* Check if any columns require detoast work */
65 27458 : if (myState->detoast)
66 : {
67 173 : for (i = 0; i < natts; i++)
68 : {
69 141 : CompactAttribute *attr = TupleDescCompactAttr(typeinfo, i);
70 :
71 141 : if (attr->attisdropped)
72 0 : continue;
73 141 : if (attr->attlen == -1)
74 : {
75 10 : needtoast = true;
76 10 : break;
77 : }
78 : }
79 : }
80 :
81 : /* Check if tuple conversion is needed */
82 27458 : 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 26117 : myState->tupmap = NULL;
88 :
89 : /* Set up appropriate callback */
90 27457 : if (needtoast)
91 : {
92 : Assert(!myState->tupmap);
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));
99 10 : myState->mapslot = NULL;
100 : }
101 27447 : 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 : {
111 27428 : myState->pub.receiveSlot = tstoreReceiveSlot_notoast;
112 27428 : myState->outvalues = NULL;
113 27428 : myState->tofree = NULL;
114 27428 : myState->mapslot = NULL;
115 : }
116 27457 : }
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 270551 : tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self)
124 : {
125 270551 : TStoreState *myState = (TStoreState *) self;
126 :
127 270551 : tuplestore_puttupleslot(myState->tstore, slot);
128 :
129 270551 : 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 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];
158 41 : CompactAttribute *attr = TupleDescCompactAttr(typeinfo, i);
159 :
160 41 : if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i])
161 : {
162 20 : if (VARATT_IS_EXTERNAL(DatumGetPointer(val)))
163 : {
164 5 : val = PointerGetDatum(detoast_external_attr((varlena *)
165 5 : DatumGetPointer(val)));
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 :
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
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
207 27368 : tstoreShutdownReceiver(DestReceiver *self)
208 : {
209 27368 : TStoreState *myState = (TStoreState *) self;
210 :
211 : /* Release workspace if any */
212 27368 : if (myState->outvalues)
213 10 : pfree(myState->outvalues);
214 27368 : myState->outvalues = NULL;
215 27368 : if (myState->tofree)
216 10 : pfree(myState->tofree);
217 27368 : myState->tofree = NULL;
218 27368 : if (myState->tupmap)
219 19 : free_conversion_map(myState->tupmap);
220 27368 : myState->tupmap = NULL;
221 27368 : if (myState->mapslot)
222 19 : ExecDropSingleTupleTableSlot(myState->mapslot);
223 27368 : myState->mapslot = NULL;
224 27368 : }
225 :
226 : /*
227 : * Destroy receiver when done with it
228 : */
229 : static void
230 27359 : tstoreDestroyReceiver(DestReceiver *self)
231 : {
232 27359 : pfree(self);
233 27359 : }
234 :
235 : /*
236 : * Initially create a DestReceiver object.
237 : */
238 : DestReceiver *
239 27616 : CreateTuplestoreDestReceiver(void)
240 : {
241 27616 : TStoreState *self = palloc0_object(TStoreState);
242 :
243 27616 : self->pub.receiveSlot = tstoreReceiveSlot_notoast; /* might change */
244 27616 : self->pub.rStartup = tstoreStartupReceiver;
245 27616 : self->pub.rShutdown = tstoreShutdownReceiver;
246 27616 : self->pub.rDestroy = tstoreDestroyReceiver;
247 27616 : self->pub.mydest = DestTuplestore;
248 :
249 : /* private fields will be set by SetTuplestoreDestReceiverParams */
250 :
251 27616 : 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 27616 : SetTuplestoreDestReceiverParams(DestReceiver *self,
268 : Tuplestorestate *tStore,
269 : MemoryContext tContext,
270 : bool detoast,
271 : TupleDesc target_tupdesc,
272 : const char *map_failure_msg)
273 : {
274 27616 : TStoreState *myState = (TStoreState *) self;
275 :
276 : Assert(!(detoast && target_tupdesc));
277 :
278 : Assert(myState->pub.mydest == DestTuplestore);
279 27616 : myState->tstore = tStore;
280 27616 : myState->cxt = tContext;
281 27616 : myState->detoast = detoast;
282 27616 : myState->target_tupdesc = target_tupdesc;
283 27616 : myState->map_failure_msg = map_failure_msg;
284 27616 : }
|