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-2017, 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 Form_pg_attribute *attrs = typeinfo->attrs;
53 int natts = typeinfo->natts;
54 int i;
55
56 /* Check if any columns require detoast work */
57 if (myState->detoast)
58 {
59 for (i = 0; i < natts; i++)
60 {
61 if (attrs[i]->attisdropped)
62 continue;
63 if (attrs[i]->attlen == -1)
64 {
65 needtoast = true;
66 break;
67 }
68 }
69 }
70
71 /* Set up appropriate callback */
72 if (needtoast)
73 {
74 myState->pub.receiveSlot = tstoreReceiveSlot_detoast;
75 /* Create workspace */
76 myState->outvalues = (Datum *)
77 MemoryContextAlloc(myState->cxt, natts * sizeof(Datum));
78 myState->tofree = (Datum *)
79 MemoryContextAlloc(myState->cxt, natts * sizeof(Datum));
80 }
81 else
82 {
83 myState->pub.receiveSlot = tstoreReceiveSlot_notoast;
84 myState->outvalues = NULL;
85 myState->tofree = NULL;
86 }
87 }
88
89 /*
90 * Receive a tuple from the executor and store it in the tuplestore.
91 * This is for the easy case where we don't have to detoast.
92 */
93 static bool
tstoreReceiveSlot_notoast(TupleTableSlot * slot,DestReceiver * self)94 tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self)
95 {
96 TStoreState *myState = (TStoreState *) self;
97
98 tuplestore_puttupleslot(myState->tstore, slot);
99
100 return true;
101 }
102
103 /*
104 * Receive a tuple from the executor and store it in the tuplestore.
105 * This is for the case where we have to detoast any toasted values.
106 */
107 static bool
tstoreReceiveSlot_detoast(TupleTableSlot * slot,DestReceiver * self)108 tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
109 {
110 TStoreState *myState = (TStoreState *) self;
111 TupleDesc typeinfo = slot->tts_tupleDescriptor;
112 Form_pg_attribute *attrs = typeinfo->attrs;
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
131 if (!attrs[i]->attisdropped &&
132 attrs[i]->attlen == -1 &&
133 !slot->tts_isnull[i])
134 {
135 if (VARATT_IS_EXTERNAL(DatumGetPointer(val)))
136 {
137 val = PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
138 DatumGetPointer(val)));
139 myState->tofree[nfree++] = val;
140 }
141 }
142
143 myState->outvalues[i] = val;
144 }
145
146 /*
147 * Push the modified tuple into the tuplestore.
148 */
149 oldcxt = MemoryContextSwitchTo(myState->cxt);
150 tuplestore_putvalues(myState->tstore, typeinfo,
151 myState->outvalues, slot->tts_isnull);
152 MemoryContextSwitchTo(oldcxt);
153
154 /* And release any temporary detoasted values */
155 for (i = 0; i < nfree; i++)
156 pfree(DatumGetPointer(myState->tofree[i]));
157
158 return true;
159 }
160
161 /*
162 * Clean up at end of an executor run
163 */
164 static void
tstoreShutdownReceiver(DestReceiver * self)165 tstoreShutdownReceiver(DestReceiver *self)
166 {
167 TStoreState *myState = (TStoreState *) self;
168
169 /* Release workspace if any */
170 if (myState->outvalues)
171 pfree(myState->outvalues);
172 myState->outvalues = NULL;
173 if (myState->tofree)
174 pfree(myState->tofree);
175 myState->tofree = NULL;
176 }
177
178 /*
179 * Destroy receiver when done with it
180 */
181 static void
tstoreDestroyReceiver(DestReceiver * self)182 tstoreDestroyReceiver(DestReceiver *self)
183 {
184 pfree(self);
185 }
186
187 /*
188 * Initially create a DestReceiver object.
189 */
190 DestReceiver *
CreateTuplestoreDestReceiver(void)191 CreateTuplestoreDestReceiver(void)
192 {
193 TStoreState *self = (TStoreState *) palloc0(sizeof(TStoreState));
194
195 self->pub.receiveSlot = tstoreReceiveSlot_notoast; /* might change */
196 self->pub.rStartup = tstoreStartupReceiver;
197 self->pub.rShutdown = tstoreShutdownReceiver;
198 self->pub.rDestroy = tstoreDestroyReceiver;
199 self->pub.mydest = DestTuplestore;
200
201 /* private fields will be set by SetTuplestoreDestReceiverParams */
202
203 return (DestReceiver *) self;
204 }
205
206 /*
207 * Set parameters for a TuplestoreDestReceiver
208 */
209 void
SetTuplestoreDestReceiverParams(DestReceiver * self,Tuplestorestate * tStore,MemoryContext tContext,bool detoast)210 SetTuplestoreDestReceiverParams(DestReceiver *self,
211 Tuplestorestate *tStore,
212 MemoryContext tContext,
213 bool detoast)
214 {
215 TStoreState *myState = (TStoreState *) self;
216
217 Assert(myState->pub.mydest == DestTuplestore);
218 myState->tstore = tStore;
219 myState->cxt = tContext;
220 myState->detoast = detoast;
221 }
222