diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 45776277d8815d83d08716c3b4fcb68c9f5aadc9..9b74f1bdc1a5866e638539321984c2c9e85821cd 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.76 2008/11/30 20:51:25 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.77 2008/12/01 17:06:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -351,11 +351,15 @@ 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);
 		SetTuplestoreDestReceiverParams(queryDesc->dest,
 										portal->holdStore,
-										portal->holdContext);
+										portal->holdContext,
+										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 cb78c80b919a7f38c0a05f007f1b10b577cbcb74..9993f856ffad3dbb3efcb4da339fe5f6fca26cff 100644
--- a/src/backend/executor/tstoreReceiver.c
+++ b/src/backend/executor/tstoreReceiver.c
@@ -1,46 +1,97 @@
 /*-------------------------------------------------------------------------
  *
  * 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-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.20 2008/11/30 20:51:25 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.21 2008/12/01 17:06:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.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 +101,77 @@ 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;
+	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((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]));
+}
+
 /*
  * 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;
 }
 
 /*
@@ -76,7 +191,7 @@ CreateTuplestoreDestReceiver(void)
 {
 	TStoreState *self = (TStoreState *) palloc0(sizeof(TStoreState));
 
-	self->pub.receiveSlot = tstoreReceiveSlot;
+	self->pub.receiveSlot = tstoreReceiveSlot_notoast;	/* might change */
 	self->pub.rStartup = tstoreStartupReceiver;
 	self->pub.rShutdown = tstoreShutdownReceiver;
 	self->pub.rDestroy = tstoreDestroyReceiver;
@@ -93,11 +208,13 @@ CreateTuplestoreDestReceiver(void)
 void
 SetTuplestoreDestReceiverParams(DestReceiver *self,
 								Tuplestorestate *tStore,
-								MemoryContext tContext)
+								MemoryContext tContext,
+								bool detoast)
 {
 	TStoreState *myState = (TStoreState *) self;
 
 	Assert(myState->pub.mydest == DestTuplestore);
 	myState->tstore = tStore;
 	myState->cxt = tContext;
+	myState->detoast = detoast;
 }
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 2a9764ac8b1ba7e297594245ace0da0cdc5283c2..c2a161b0f7f23ae5cbe95ce6d8a276c70137caf1 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.126 2008/11/30 20:51:25 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.127 2008/12/01 17:06:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1036,7 +1036,8 @@ FillPortalStore(Portal portal, bool isTopLevel)
 	treceiver = CreateDestReceiver(DestTuplestore);
 	SetTuplestoreDestReceiverParams(treceiver,
 									portal->holdStore,
-									portal->holdContext);
+									portal->holdContext,
+									false);
 
 	completionTag[0] = '\0';
 
diff --git a/src/include/executor/tstoreReceiver.h b/src/include/executor/tstoreReceiver.h
index c304c1fce906d4b9445e8fb27f705ed87e55b937..b2d3890f46a25e52ffc08227fa693fe00fd27d0d 100644
--- a/src/include/executor/tstoreReceiver.h
+++ b/src/include/executor/tstoreReceiver.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/tstoreReceiver.h,v 1.11 2008/11/30 20:51:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/tstoreReceiver.h,v 1.12 2008/12/01 17:06:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,7 @@ extern DestReceiver *CreateTuplestoreDestReceiver(void);
 
 extern void SetTuplestoreDestReceiverParams(DestReceiver *self,
 											Tuplestorestate *tStore,
-											MemoryContext tContext);
+											MemoryContext tContext,
+											bool detoast);
 
 #endif   /* TSTORE_RECEIVER_H */