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-2024, 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
7647 tgl@sss.pgh.pa.us 56 :CBC 22403 : tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
57 : : {
5613 58 : 22403 : TStoreState *myState = (TStoreState *) self;
59 : 22403 : bool needtoast = false;
60 : 22403 : int natts = typeinfo->natts;
61 : : int i;
62 : :
63 : : /* Check if any columns require detoast work */
64 [ + + ]: 22403 : if (myState->detoast)
65 : : {
66 [ + + ]: 171 : for (i = 0; i < natts; i++)
67 : : {
2429 andres@anarazel.de 68 : 140 : Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
69 : :
70 [ - + ]: 140 : if (attr->attisdropped)
5613 tgl@sss.pgh.pa.us 71 :UBC 0 : continue;
2429 andres@anarazel.de 72 [ + + ]:CBC 140 : if (attr->attlen == -1)
73 : : {
5613 tgl@sss.pgh.pa.us 74 : 10 : needtoast = true;
75 : 10 : break;
76 : : }
77 : : }
78 : : }
79 : :
80 : : /* Check if tuple conversion is needed */
1402 81 [ + + ]: 22403 : 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 : 21076 : myState->tupmap = NULL;
87 : :
88 : : /* Set up appropriate callback */
5613 89 [ + + ]: 22403 : if (needtoast)
90 : : {
1402 91 [ - + ]: 10 : Assert(!myState->tupmap);
5613 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));
1402 98 : 10 : myState->mapslot = NULL;
99 : : }
100 [ + + ]: 22393 : 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 : : {
5613 110 : 22375 : myState->pub.receiveSlot = tstoreReceiveSlot_notoast;
111 : 22375 : myState->outvalues = NULL;
112 : 22375 : myState->tofree = NULL;
1402 113 : 22375 : myState->mapslot = NULL;
114 : : }
7689 bruce@momjian.us 115 : 22403 : }
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
5613 tgl@sss.pgh.pa.us 122 : 235539 : tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self)
123 : : {
7689 bruce@momjian.us 124 : 235539 : TStoreState *myState = (TStoreState *) self;
125 : :
6501 tgl@sss.pgh.pa.us 126 : 235539 : tuplestore_puttupleslot(myState->tstore, slot);
127 : :
2869 rhaas@postgresql.org 128 : 235539 : 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
5613 tgl@sss.pgh.pa.us 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];
2429 andres@anarazel.de 157 : 41 : Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
158 : :
159 [ + - + + : 41 : if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i])
+ + ]
160 : : {
5613 tgl@sss.pgh.pa.us 161 [ + + ]: 20 : if (VARATT_IS_EXTERNAL(DatumGetPointer(val)))
162 : : {
1654 rhaas@postgresql.org 163 : 5 : val = PointerGetDatum(detoast_external_attr((struct varlena *)
2489 tgl@sss.pgh.pa.us 164 : 5 : DatumGetPointer(val)));
5613 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,
5613 tgl@sss.pgh.pa.us 177 :GIC 23 : myState->outvalues, slot->tts_isnull);
5613 tgl@sss.pgh.pa.us 178 :CBC 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 : :
2869 rhaas@postgresql.org 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
1402 tgl@sss.pgh.pa.us 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
7649 206 : 22335 : tstoreShutdownReceiver(DestReceiver *self)
207 : : {
5613 208 : 22335 : TStoreState *myState = (TStoreState *) self;
209 : :
210 : : /* Release workspace if any */
211 [ + + ]: 22335 : if (myState->outvalues)
212 : 10 : pfree(myState->outvalues);
213 : 22335 : myState->outvalues = NULL;
214 [ + + ]: 22335 : if (myState->tofree)
215 : 10 : pfree(myState->tofree);
216 : 22335 : myState->tofree = NULL;
1402 217 [ + + ]: 22335 : if (myState->tupmap)
218 : 18 : free_conversion_map(myState->tupmap);
219 : 22335 : myState->tupmap = NULL;
220 [ + + ]: 22335 : if (myState->mapslot)
221 : 18 : ExecDropSingleTupleTableSlot(myState->mapslot);
222 : 22335 : myState->mapslot = NULL;
7689 bruce@momjian.us 223 : 22335 : }
224 : :
225 : : /*
226 : : * Destroy receiver when done with it
227 : : */
228 : : static void
7649 tgl@sss.pgh.pa.us 229 : 22335 : tstoreDestroyReceiver(DestReceiver *self)
230 : : {
231 : 22335 : pfree(self);
232 : 22335 : }
233 : :
234 : : /*
235 : : * Initially create a DestReceiver object.
236 : : */
237 : : DestReceiver *
5614 238 : 22522 : CreateTuplestoreDestReceiver(void)
239 : : {
240 : 22522 : TStoreState *self = (TStoreState *) palloc0(sizeof(TStoreState));
241 : :
5613 242 : 22522 : self->pub.receiveSlot = tstoreReceiveSlot_notoast; /* might change */
7557 243 : 22522 : self->pub.rStartup = tstoreStartupReceiver;
244 : 22522 : self->pub.rShutdown = tstoreShutdownReceiver;
245 : 22522 : self->pub.rDestroy = tstoreDestroyReceiver;
6737 alvherre@alvh.no-ip. 246 : 22522 : self->pub.mydest = DestTuplestore;
247 : :
248 : : /* private fields will be set by SetTuplestoreDestReceiverParams */
249 : :
7689 bruce@momjian.us 250 : 22522 : 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
5614 tgl@sss.pgh.pa.us 266 : 22522 : SetTuplestoreDestReceiverParams(DestReceiver *self,
267 : : Tuplestorestate *tStore,
268 : : MemoryContext tContext,
269 : : bool detoast,
270 : : TupleDesc target_tupdesc,
271 : : const char *map_failure_msg)
272 : : {
273 : 22522 : TStoreState *myState = (TStoreState *) self;
274 : :
1402 275 [ + + - + ]: 22522 : Assert(!(detoast && target_tupdesc));
276 : :
5614 277 [ - + ]: 22522 : Assert(myState->pub.mydest == DestTuplestore);
278 : 22522 : myState->tstore = tStore;
279 : 22522 : myState->cxt = tContext;
5613 280 : 22522 : myState->detoast = detoast;
1402 281 : 22522 : myState->target_tupdesc = target_tupdesc;
282 : 22522 : myState->map_failure_msg = map_failure_msg;
5614 283 : 22522 : }
|