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-2019, 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
27typedef 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
40static bool tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self);
41static bool tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self);
42
43
44/*
45 * Prepare to receive tuples from executor.
46 */
47static void
48tstoreStartupReceiver(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 */
94static bool
95tstoreReceiveSlot_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 */
108static bool
109tstoreReceiveSlot_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 */
163static void
164tstoreShutdownReceiver(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 */
180static void
181tstoreDestroyReceiver(DestReceiver *self)
182{
183 pfree(self);
184}
185
186/*
187 * Initially create a DestReceiver object.
188 */
189DestReceiver *
190CreateTuplestoreDestReceiver(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 */
208void
209SetTuplestoreDestReceiverParams(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