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