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