11/*-------------------------------------------------------------------------
22 *
33 * tstoreReceiver.c
4- * an implementation of DestReceiver that stores the result tuples in
5- * a Tuplestore
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.
610 *
711 *
812 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
913 * Portions Copyright (c) 1994, Regents of the University of California
1014 *
1115 * IDENTIFICATION
12- * $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.20 2008/11/30 20:51:25 tgl Exp $
16+ * $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.21 2008/12/01 17:06:21 tgl Exp $
1317 *
1418 *-------------------------------------------------------------------------
1519 */
1620
1721#include "postgres.h"
1822
23+ #include "access/tuptoaster.h"
1924#include "executor/tstoreReceiver.h"
2025
2126
2227typedef struct
2328{
2429 DestReceiver pub ;
25- Tuplestorestate * tstore ;
26- MemoryContext cxt ;
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 */
2737} TStoreState ;
2838
2939
40+ static void tstoreReceiveSlot_notoast (TupleTableSlot * slot , DestReceiver * self );
41+ static void tstoreReceiveSlot_detoast (TupleTableSlot * slot , DestReceiver * self );
42+
43+
3044/*
3145 * Prepare to receive tuples from executor.
3246 */
3347static void
3448tstoreStartupReceiver (DestReceiver * self , int operation , TupleDesc typeinfo )
3549{
36- /* do nothing */
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+ }
3787}
3888
3989/*
4090 * 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.
4192 */
4293static void
43- tstoreReceiveSlot (TupleTableSlot * slot , DestReceiver * self )
94+ tstoreReceiveSlot_notoast (TupleTableSlot * slot , DestReceiver * self )
4495{
4596 TStoreState * myState = (TStoreState * ) self ;
4697 MemoryContext oldcxt = MemoryContextSwitchTo (myState -> cxt );
@@ -50,13 +101,77 @@ tstoreReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
50101 MemoryContextSwitchTo (oldcxt );
51102}
52103
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 void
109+ tstoreReceiveSlot_detoast (TupleTableSlot * slot , DestReceiver * self )
110+ {
111+ TStoreState * myState = (TStoreState * ) self ;
112+ TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
113+ Form_pg_attribute * attrs = typeinfo -> attrs ;
114+ int natts = typeinfo -> natts ;
115+ int nfree ;
116+ int i ;
117+ MemoryContext oldcxt ;
118+
119+ /* Make sure the tuple is fully deconstructed */
120+ slot_getallattrs (slot );
121+
122+ /*
123+ * Fetch back any out-of-line datums. We build the new datums array in
124+ * myState->outvalues[] (but we can re-use the slot's isnull array).
125+ * Also, remember the fetched values to free afterwards.
126+ */
127+ nfree = 0 ;
128+ for (i = 0 ; i < natts ; i ++ )
129+ {
130+ Datum val = slot -> tts_values [i ];
131+
132+ if (!attrs [i ]-> attisdropped &&
133+ attrs [i ]-> attlen == -1 &&
134+ !slot -> tts_isnull [i ])
135+ {
136+ if (VARATT_IS_EXTERNAL (DatumGetPointer (val )))
137+ {
138+ val = PointerGetDatum (heap_tuple_fetch_attr ((struct varlena * )
139+ DatumGetPointer (val )));
140+ myState -> tofree [nfree ++ ] = val ;
141+ }
142+ }
143+
144+ myState -> outvalues [i ] = val ;
145+ }
146+
147+ /*
148+ * Push the modified tuple into the tuplestore.
149+ */
150+ oldcxt = MemoryContextSwitchTo (myState -> cxt );
151+ tuplestore_putvalues (myState -> tstore , typeinfo ,
152+ myState -> outvalues , slot -> tts_isnull );
153+ MemoryContextSwitchTo (oldcxt );
154+
155+ /* And release any temporary detoasted values */
156+ for (i = 0 ; i < nfree ; i ++ )
157+ pfree (DatumGetPointer (myState -> tofree [i ]));
158+ }
159+
53160/*
54161 * Clean up at end of an executor run
55162 */
56163static void
57164tstoreShutdownReceiver (DestReceiver * self )
58165{
59- /* do nothing */
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 ;
60175}
61176
62177/*
@@ -76,7 +191,7 @@ CreateTuplestoreDestReceiver(void)
76191{
77192 TStoreState * self = (TStoreState * ) palloc0 (sizeof (TStoreState ));
78193
79- self -> pub .receiveSlot = tstoreReceiveSlot ;
194+ self -> pub .receiveSlot = tstoreReceiveSlot_notoast ; /* might change */
80195 self -> pub .rStartup = tstoreStartupReceiver ;
81196 self -> pub .rShutdown = tstoreShutdownReceiver ;
82197 self -> pub .rDestroy = tstoreDestroyReceiver ;
@@ -93,11 +208,13 @@ CreateTuplestoreDestReceiver(void)
93208void
94209SetTuplestoreDestReceiverParams (DestReceiver * self ,
95210 Tuplestorestate * tStore ,
96- MemoryContext tContext )
211+ MemoryContext tContext ,
212+ bool detoast )
97213{
98214 TStoreState * myState = (TStoreState * ) self ;
99215
100216 Assert (myState -> pub .mydest == DestTuplestore );
101217 myState -> tstore = tStore ;
102218 myState -> cxt = tContext ;
219+ myState -> detoast = detoast ;
103220}
0 commit comments