diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index 39b73c7a609..f02ecec3f8f 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.44.2.1 2007/02/06 22:49:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.44.2.2 2008/12/01 17:06:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,6 +25,7 @@ #include "commands/portalcmds.h" #include "executor/executor.h" +#include "executor/tstoreReceiver.h" #include "optimizer/planner.h" #include "rewrite/rewriteHandler.h" #include "tcop/pquery.h" @@ -368,8 +369,12 @@ PersistHoldablePortal(Portal portal) */ ExecutorRewind(queryDesc); - /* Change the destination to output to the tuplestore */ + /* + * Change the destination to output to the tuplestore. Note we + * tell the tuplestore receiver to detoast all data passed through it. + */ queryDesc->dest = CreateDestReceiver(DestTuplestore, portal); + SetTuplestoreDestReceiverDeToast(queryDesc->dest, true); /* Fetch the result set into the tuplestore */ ExecutorRun(queryDesc, ForwardScanDirection, 0L); diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c index 90c34aeba6f..fb2aa843fd6 100644 --- a/src/backend/executor/tstoreReceiver.c +++ b/src/backend/executor/tstoreReceiver.c @@ -1,46 +1,98 @@ /*------------------------------------------------------------------------- * * tstoreReceiver.c - * an implementation of DestReceiver that stores the result tuples in - * a Tuplestore + * 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-2005, PostgreSQL Global Development Group + * 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.15 2005/11/03 17:11:36 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.15.2.1 2008/12/01 17:06:41 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "access/heapam.h" +#include "access/tuptoaster.h" #include "executor/tstoreReceiver.h" typedef struct { DestReceiver pub; - Tuplestorestate *tstore; - MemoryContext cxt; + /* 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) { - /* do nothing */ + 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(TupleTableSlot *slot, DestReceiver *self) +tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self) { TStoreState *myState = (TStoreState *) self; MemoryContext oldcxt = MemoryContextSwitchTo(myState->cxt); @@ -50,13 +102,80 @@ tstoreReceiveSlot(TupleTableSlot *slot, DestReceiver *self) MemoryContextSwitchTo(oldcxt); } +/* + * 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; + + /* 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]; + + if (!attrs[i]->attisdropped && + attrs[i]->attlen == -1 && + !slot->tts_isnull[i]) + { + if (VARATT_IS_EXTERNAL(DatumGetPointer(val))) + { + val = PointerGetDatum(heap_tuple_fetch_attr((varattrib *) + DatumGetPointer(val))); + myState->tofree[nfree++] = val; + } + } + + myState->outvalues[i] = val; + } + + /* + * Push the modified tuple into the tuplestore. + */ + tuple = heap_form_tuple(typeinfo, + myState->outvalues, slot->tts_isnull); + 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) { - /* do nothing */ + 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; } /* @@ -75,9 +194,9 @@ DestReceiver * CreateTuplestoreDestReceiver(Tuplestorestate *tStore, MemoryContext tContext) { - TStoreState *self = (TStoreState *) palloc(sizeof(TStoreState)); + TStoreState *self = (TStoreState *) palloc0(sizeof(TStoreState)); - self->pub.receiveSlot = tstoreReceiveSlot; + self->pub.receiveSlot = tstoreReceiveSlot_notoast; self->pub.rStartup = tstoreStartupReceiver; self->pub.rShutdown = tstoreShutdownReceiver; self->pub.rDestroy = tstoreDestroyReceiver; @@ -85,6 +204,20 @@ CreateTuplestoreDestReceiver(Tuplestorestate *tStore, 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; +} diff --git a/src/include/executor/tstoreReceiver.h b/src/include/executor/tstoreReceiver.h index 51be739ff16..fbef6dd3295 100644 --- a/src/include/executor/tstoreReceiver.h +++ b/src/include/executor/tstoreReceiver.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/executor/tstoreReceiver.h,v 1.7 2004/12/31 22:03:29 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/executor/tstoreReceiver.h,v 1.7.6.1 2008/12/01 17:06:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,4 +22,7 @@ extern DestReceiver *CreateTuplestoreDestReceiver(Tuplestorestate *tStore, MemoryContext tContext); +extern void SetTuplestoreDestReceiverDeToast(DestReceiver *self, + bool detoast); + #endif /* TSTORE_RECEIVER_H */