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 *
12 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
13 * Portions Copyright (c) 1994, Regents of the University of California
14 *
15 * IDENTIFICATION
16 * src/backend/executor/tstoreReceiver.c
17 *
18 *-------------------------------------------------------------------------
19 */
20
21 #include "postgres.h"
22
23 #include "access/tuptoaster.h"
24 #include "executor/tstoreReceiver.h"
25
26
27 typedef struct
28 {
29 DestReceiver pub;
30 /* parameters: */
31 Tuplestorestate *tstore; /* where to put the data */
32 MemoryContext cxt; /* context containing tstore */
33 bool detoast; /* were we told to detoast? */
34 /* workspace: */
35 Datum *outvalues; /* values array for result tuple */
36 Datum *tofree; /* temp values to be pfree'd */
37 } TStoreState;
38
39
40 static bool tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self);
41 static bool tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self);
42
43
44 /*
45 * Prepare to receive tuples from executor.
46 */
47 static void
tstoreStartupReceiver(DestReceiver * self,int operation,TupleDesc typeinfo)48 tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
49 {
50 TStoreState *myState = (TStoreState *) self;
51 bool needtoast = false;
52 int natts = typeinfo->natts;
53 int i;
54
55 /* Check if any columns require detoast work */
56 if (myState->detoast)
57 {
58 for (i = 0; i < natts; i++)
59 {
60 Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
61
62 if (attr->attisdropped)
63 continue;
64 if (attr->attlen == -1)
65 {
66 needtoast = true;
67 break;
68 }
69 }
70 }
71
72 /* Set up appropriate callback */
73 if (needtoast)
74 {
75 myState->pub.receiveSlot = tstoreReceiveSlot_detoast;
76 /* Create workspace */
77 myState->outvalues = (Datum *)
78 MemoryContextAlloc(myState->cxt, natts * sizeof(Datum));
79 myState->tofree = (Datum *)
80 MemoryContextAlloc(myState->cxt, natts * sizeof(Datum));
81 }
82 else
83 {
84 myState->pub.receiveSlot = tstoreReceiveSlot_notoast;
85 myState->outvalues = NULL;
86 myState->tofree = NULL;
87 }
88 }
89
90 /*
91 * Receive a tuple from the executor and store it in the tuplestore.
92 * This is for the easy case where we don't have to detoast.
93 */
94 static bool
tstoreReceiveSlot_notoast(TupleTableSlot * slot,DestReceiver * self)95 tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self)
96 {
97 TStoreState *myState = (TStoreState *) self;
98
99 tuplestore_puttupleslot(myState->tstore, slot);
100
101 return true;
102 }
103
104 /*
105 * Receive a tuple from the executor and store it in the tuplestore.
106 * This is for the case where we have to detoast any toasted values.
107 */
108 static bool
tstoreReceiveSlot_detoast(TupleTableSlot * slot,DestReceiver * self)109 tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
110 {
111 TStoreState *myState = (TStoreState *) self;
112 TupleDesc typeinfo = slot->tts_tupleDescriptor;
113 int natts = typeinfo->natts;
114 int nfree;
115 int i;
116 MemoryContext oldcxt;
117
118 /* Make sure the tuple is fully deconstructed */
119 slot_getallattrs(slot);
120
121 /*
122 * Fetch back any out-of-line datums. We build the new datums array in
123 * myState->outvalues[] (but we can re-use the slot's isnull array). Also,
124 * remember the fetched values to free afterwards.
125 */
126 nfree = 0;
127 for (i = 0; i < natts; i++)
128 {
129 Datum val = slot->tts_values[i];
130 Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
131
132 if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i])
133 {
134 if (VARATT_IS_EXTERNAL(DatumGetPointer(val)))
135 {
136 val = PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
137 DatumGetPointer(val)));
138 myState->tofree[nfree++] = val;
139 }
140 }
141
142 myState->outvalues[i] = val;
143 }
144
145 /*
146 * Push the modified tuple into the tuplestore.
147 */
148 oldcxt = MemoryContextSwitchTo(myState->cxt);
149 tuplestore_putvalues(myState->tstore, typeinfo,
150 myState->outvalues, slot->tts_isnull);
151 MemoryContextSwitchTo(oldcxt);
152
153 /* And release any temporary detoasted values */
154 for (i = 0; i < nfree; i++)
155 pfree(DatumGetPointer(myState->tofree[i]));
156
157 return true;
158 }
159
160 /*
161 * Clean up at end of an executor run
162 */
163 static void
tstoreShutdownReceiver(DestReceiver * self)164 tstoreShutdownReceiver(DestReceiver *self)
165 {
166 TStoreState *myState = (TStoreState *) self;
167
168 /* Release workspace if any */
169 if (myState->outvalues)
170 pfree(myState->outvalues);
171 myState->outvalues = NULL;
172 if (myState->tofree)
173 pfree(myState->tofree);
174 myState->tofree = NULL;
175 }
176
177 /*
178 * Destroy receiver when done with it
179 */
180 static void
tstoreDestroyReceiver(DestReceiver * self)181 tstoreDestroyReceiver(DestReceiver *self)
182 {
183 pfree(self);
184 }
185
186 /*
187 * Initially create a DestReceiver object.
188 */
189 DestReceiver *
CreateTuplestoreDestReceiver(void)190 CreateTuplestoreDestReceiver(void)
191 {
192 TStoreState *self = (TStoreState *) palloc0(sizeof(TStoreState));
193
194 self->pub.receiveSlot = tstoreReceiveSlot_notoast; /* might change */
195 self->pub.rStartup = tstoreStartupReceiver;
196 self->pub.rShutdown = tstoreShutdownReceiver;
197 self->pub.rDestroy = tstoreDestroyReceiver;
198 self->pub.mydest = DestTuplestore;
199
200 /* private fields will be set by SetTuplestoreDestReceiverParams */
201
202 return (DestReceiver *) self;
203 }
204
205 /*
206 * Set parameters for a TuplestoreDestReceiver
207 */
208 void
SetTuplestoreDestReceiverParams(DestReceiver * self,Tuplestorestate * tStore,MemoryContext tContext,bool detoast)209 SetTuplestoreDestReceiverParams(DestReceiver *self,
210 Tuplestorestate *tStore,
211 MemoryContext tContext,
212 bool detoast)
213 {
214 TStoreState *myState = (TStoreState *) self;
215
216 Assert(myState->pub.mydest == DestTuplestore);
217 myState->tstore = tStore;
218 myState->cxt = tContext;
219 myState->detoast = detoast;
220 }
221