diff options
Diffstat (limited to 'src/backend/executor/tstoreReceiver.c')
-rw-r--r-- | src/backend/executor/tstoreReceiver.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c new file mode 100644 index 0000000..6c2dfbc --- /dev/null +++ b/src/backend/executor/tstoreReceiver.c @@ -0,0 +1,220 @@ +/*------------------------------------------------------------------------- + * + * 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-2020, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/executor/tstoreReceiver.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/detoast.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 bool tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self); +static bool 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; + int natts = typeinfo->natts; + int i; + + /* Check if any columns require detoast work */ + if (myState->detoast) + { + for (i = 0; i < natts; i++) + { + Form_pg_attribute attr = TupleDescAttr(typeinfo, i); + + if (attr->attisdropped) + continue; + if (attr->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 bool +tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self) +{ + TStoreState *myState = (TStoreState *) self; + + tuplestore_puttupleslot(myState->tstore, slot); + + return true; +} + +/* + * 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 bool +tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self) +{ + TStoreState *myState = (TStoreState *) self; + TupleDesc typeinfo = slot->tts_tupleDescriptor; + int natts = typeinfo->natts; + int nfree; + int i; + MemoryContext oldcxt; + + /* 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 = slot->tts_values[i]; + Form_pg_attribute attr = TupleDescAttr(typeinfo, i); + + if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i]) + { + if (VARATT_IS_EXTERNAL(DatumGetPointer(val))) + { + val = PointerGetDatum(detoast_external_attr((struct varlena *) + DatumGetPointer(val))); + myState->tofree[nfree++] = val; + } + } + + myState->outvalues[i] = val; + } + + /* + * Push the modified tuple into the tuplestore. + */ + oldcxt = MemoryContextSwitchTo(myState->cxt); + tuplestore_putvalues(myState->tstore, typeinfo, + myState->outvalues, slot->tts_isnull); + MemoryContextSwitchTo(oldcxt); + + /* And release any temporary detoasted values */ + for (i = 0; i < nfree; i++) + pfree(DatumGetPointer(myState->tofree[i])); + + return true; +} + +/* + * 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(void) +{ + TStoreState *self = (TStoreState *) palloc0(sizeof(TStoreState)); + + self->pub.receiveSlot = tstoreReceiveSlot_notoast; /* might change */ + self->pub.rStartup = tstoreStartupReceiver; + self->pub.rShutdown = tstoreShutdownReceiver; + self->pub.rDestroy = tstoreDestroyReceiver; + self->pub.mydest = DestTuplestore; + + /* private fields will be set by SetTuplestoreDestReceiverParams */ + + return (DestReceiver *) self; +} + +/* + * Set parameters for a TuplestoreDestReceiver + */ +void +SetTuplestoreDestReceiverParams(DestReceiver *self, + Tuplestorestate *tStore, + MemoryContext tContext, + bool detoast) +{ + TStoreState *myState = (TStoreState *) self; + + Assert(myState->pub.mydest == DestTuplestore); + myState->tstore = tStore; + myState->cxt = tContext; + myState->detoast = detoast; +} |