/*------------------------------------------------------------------------- * * tstoreReceiver.c * An implementation of DestReceiver that stores the result tuples in * a Tuplestore. * * Optionally, we can force detoasting (but not decompression) of out-of-line * toasted values. This is to support cursors WITH HOLD, which must retain * data even if the underlying table is dropped. * * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.19.2.2 2009/12/29 17:41:18 heikki Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/heapam.h" #include "access/tuptoaster.h" #include "executor/tstoreReceiver.h" typedef struct { DestReceiver pub; /* parameters: */ Tuplestorestate *tstore; /* where to put the data */ MemoryContext cxt; /* context containing tstore */ bool detoast; /* were we told to detoast? */ /* workspace: */ Datum *outvalues; /* values array for result tuple */ Datum *tofree; /* temp values to be pfree'd */ } TStoreState; static void tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self); static void tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self); /* * Prepare to receive tuples from executor. */ static void tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo) { TStoreState *myState = (TStoreState *) self; bool needtoast = false; Form_pg_attribute *attrs = typeinfo->attrs; int natts = typeinfo->natts; int i; /* Check if any columns require detoast work */ if (myState->detoast) { for (i = 0; i < natts; i++) { if (attrs[i]->attisdropped) continue; if (attrs[i]->attlen == -1) { needtoast = true; break; } } } /* Set up appropriate callback */ if (needtoast) { myState->pub.receiveSlot = tstoreReceiveSlot_detoast; /* Create workspace */ myState->outvalues = (Datum *) MemoryContextAlloc(myState->cxt, natts * sizeof(Datum)); myState->tofree = (Datum *) MemoryContextAlloc(myState->cxt, natts * sizeof(Datum)); } else { myState->pub.receiveSlot = tstoreReceiveSlot_notoast; myState->outvalues = NULL; myState->tofree = NULL; } } /* * Receive a tuple from the executor and store it in the tuplestore. * This is for the easy case where we don't have to detoast. */ static void tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self) { TStoreState *myState = (TStoreState *) self; tuplestore_puttupleslot(myState->tstore, slot); } /* * Receive a tuple from the executor and store it in the tuplestore. * This is for the case where we have to detoast any toasted values. */ static void tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self) { TStoreState *myState = (TStoreState *) self; TupleDesc typeinfo = slot->tts_tupleDescriptor; Form_pg_attribute *attrs = typeinfo->attrs; int natts = typeinfo->natts; int nfree; int i; HeapTuple tuple; MemoryContext oldcxt; bool *nulls; nulls = (bool *)MemoryContextAlloc(myState->cxt, natts * sizeof(bool)); /* Make sure the tuple is fully deconstructed */ slot_getallattrs(slot); /* * Fetch back any out-of-line datums. We build the new datums array in * myState->outvalues[] (but we can re-use the slot's isnull array). * Also, remember the fetched values to free afterwards. */ nfree = 0; for (i = 0; i < natts; i++) { Datum val; bool isnull; val = slot_getattr(slot,i+1,&isnull); if (!attrs[i]->attisdropped && attrs[i]->attlen == -1 && !slot_attisnull(slot,i+1)) { if (VARATT_IS_EXTERNAL(DatumGetPointer(val))) { val = PointerGetDatum(heap_tuple_fetch_attr( DatumGetPointer(val))); myState->tofree[nfree++] = val; } } myState->outvalues[i] = val; nulls[i] = isnull; } /* * Push the modified tuple into the tuplestore. */ tuple = heap_form_tuple(typeinfo, myState->outvalues, nulls); oldcxt = MemoryContextSwitchTo(myState->cxt); tuplestore_puttuple(myState->tstore, tuple); MemoryContextSwitchTo(oldcxt); heap_freetuple(tuple); /* And release any temporary detoasted values */ for (i = 0; i < nfree; i++) pfree(DatumGetPointer(myState->tofree[i])); } /* * Clean up at end of an executor run */ static void tstoreShutdownReceiver(DestReceiver *self) { TStoreState *myState = (TStoreState *) self; /* Release workspace if any */ if (myState->outvalues) pfree(myState->outvalues); myState->outvalues = NULL; if (myState->tofree) pfree(myState->tofree); myState->tofree = NULL; } /* * Destroy receiver when done with it */ static void tstoreDestroyReceiver(DestReceiver *self) { pfree(self); } /* * Initially create a DestReceiver object. */ DestReceiver * CreateTuplestoreDestReceiver(Tuplestorestate *tStore, MemoryContext tContext) { TStoreState *self = (TStoreState *) palloc0(sizeof(TStoreState)); self->pub.receiveSlot = tstoreReceiveSlot_notoast; self->pub.rStartup = tstoreStartupReceiver; self->pub.rShutdown = tstoreShutdownReceiver; self->pub.rDestroy = tstoreDestroyReceiver; self->pub.mydest = DestTuplestore; self->tstore = tStore; self->cxt = tContext; self->detoast = false; return (DestReceiver *) self; } /* * Set parameters for a TuplestoreDestReceiver */ void SetTuplestoreDestReceiverDeToast(DestReceiver *self, bool detoast) { TStoreState *myState = (TStoreState *) self; Assert(myState->pub.mydest == DestTuplestore); myState->detoast = detoast; }