diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index 78939e2bd054fe8481a05c1b262b011fe1447edf..9080d047fc2b5e63daf883935ef8f05493c0e423 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.90 2005/05/01 18:56:17 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.91 2005/06/22 17:45:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,6 +19,7 @@
 #include "access/printtup.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
+#include "tcop/pquery.h"
 #include "utils/lsyscache.h"
 #include "utils/portal.h"
 
@@ -130,16 +131,9 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	 * descriptions, then we send back the tuple descriptor of the tuples.
 	 */
 	if (operation == CMD_SELECT && myState->sendDescrip)
-	{
-		List	   *targetlist;
-
-		if (portal->strategy == PORTAL_ONE_SELECT)
-			targetlist = ((Query *) linitial(portal->parseTrees))->targetList;
-		else
-			targetlist = NIL;
-
-		SendRowDescriptionMessage(typeinfo, targetlist, portal->formats);
-	}
+		SendRowDescriptionMessage(typeinfo,
+								  FetchPortalTargetList(portal),
+								  portal->formats);
 
 	/* ----------------
 	 * We could set up the derived attr info at this time, but we postpone it
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index be3416bd3e5bbdc32ba8569ba562a9f1869add19..dec3d249dfa89a62044b0666622aca500c626153 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -10,7 +10,7 @@
  * Copyright (c) 2002-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.39 2005/06/03 23:05:28 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.40 2005/06/22 17:45:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -445,6 +445,58 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt)
 	return NULL;
 }
 
+/*
+ * Given a prepared statement that returns tuples, extract the query
+ * targetlist.  Returns NIL if the statement doesn't have a determinable
+ * targetlist.
+ *
+ * Note: do not modify the result.
+ *
+ * XXX be careful to keep this in sync with FetchPortalTargetList,
+ * and with UtilityReturnsTuples.
+ */
+List *
+FetchPreparedStatementTargetList(PreparedStatement *stmt)
+{
+	PortalStrategy strategy = ChoosePortalStrategy(stmt->query_list);
+
+	if (strategy == PORTAL_ONE_SELECT)
+		return ((Query *) linitial(stmt->query_list))->targetList;
+	if (strategy == PORTAL_UTIL_SELECT)
+	{
+		Node *utilityStmt;
+
+		utilityStmt = ((Query *) linitial(stmt->query_list))->utilityStmt;
+		switch (nodeTag(utilityStmt))
+		{
+			case T_FetchStmt:
+			{
+				FetchStmt  *substmt = (FetchStmt *) utilityStmt;
+				Portal		subportal;
+
+				Assert(!substmt->ismove);
+				subportal = GetPortalByName(substmt->portalname);
+				Assert(PortalIsValid(subportal));
+				return FetchPortalTargetList(subportal);
+			}
+
+			case T_ExecuteStmt:
+			{
+				ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt;
+				PreparedStatement *entry;
+
+				Assert(!substmt->into);
+				entry = FetchPreparedStatement(substmt->name, true);
+				return FetchPreparedStatementTargetList(entry);
+			}
+
+			default:
+				break;
+		}
+	}
+	return NIL;
+}
+
 /*
  * Implements the 'DEALLOCATE' utility statement: deletes the
  * specified plan from storage.
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 454bc2577e1cbf2074e0742e007f5ed2d7567074..a676edd9a971b3de3f7e3704a701dbe38319ec81 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.449 2005/06/17 22:32:46 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.450 2005/06/22 17:45:45 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -1928,15 +1928,9 @@ exec_describe_statement_message(const char *stmt_name)
 	 */
 	tupdesc = FetchPreparedStatementResultDesc(pstmt);
 	if (tupdesc)
-	{
-		List	   *targetlist;
-
-		if (ChoosePortalStrategy(pstmt->query_list) == PORTAL_ONE_SELECT)
-			targetlist = ((Query *) linitial(pstmt->query_list))->targetList;
-		else
-			targetlist = NIL;
-		SendRowDescriptionMessage(tupdesc, targetlist, NULL);
-	}
+		SendRowDescriptionMessage(tupdesc,
+								  FetchPreparedStatementTargetList(pstmt),
+								  NULL);
 	else
 		pq_putemptymessage('n');	/* NoData */
 
@@ -1962,16 +1956,9 @@ exec_describe_portal_message(const char *portal_name)
 		return;					/* can't actually do anything... */
 
 	if (portal->tupDesc)
-	{
-		List	   *targetlist;
-
-		if (portal->strategy == PORTAL_ONE_SELECT)
-			targetlist = ((Query *) linitial(portal->parseTrees))->targetList;
-		else
-			targetlist = NIL;
-		SendRowDescriptionMessage(portal->tupDesc, targetlist,
+		SendRowDescriptionMessage(portal->tupDesc,
+								  FetchPortalTargetList(portal),
 								  portal->formats);
-	}
 	else
 		pq_putemptymessage('n');	/* NoData */
 }
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 51f3df8e56b2aaa1c2a136742edbaef5bebd7b3e..75eb75f6de7c0ca39e98bf0cb39629455e3e47a2 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,13 +8,14 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.93 2005/03/25 21:57:58 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.94 2005/06/22 17:45:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
+#include "commands/prepare.h"
 #include "commands/trigger.h"
 #include "executor/executor.h"
 #include "miscadmin.h"
@@ -252,6 +253,56 @@ ChoosePortalStrategy(List *parseTrees)
 	return strategy;
 }
 
+/*
+ * FetchPortalTargetList
+ *		Given a portal that returns tuples, extract the query targetlist.
+ *		Returns NIL if the portal doesn't have a determinable targetlist.
+ *
+ * Note: do not modify the result.
+ *
+ * XXX be careful to keep this in sync with FetchPreparedStatementTargetList,
+ * and with UtilityReturnsTuples.
+ */
+List *
+FetchPortalTargetList(Portal portal)
+{
+	if (portal->strategy == PORTAL_ONE_SELECT)
+		return ((Query *) linitial(portal->parseTrees))->targetList;
+	if (portal->strategy == PORTAL_UTIL_SELECT)
+	{
+		Node *utilityStmt;
+
+		utilityStmt = ((Query *) linitial(portal->parseTrees))->utilityStmt;
+		switch (nodeTag(utilityStmt))
+		{
+			case T_FetchStmt:
+			{
+				FetchStmt  *substmt = (FetchStmt *) utilityStmt;
+				Portal		subportal;
+
+				Assert(!substmt->ismove);
+				subportal = GetPortalByName(substmt->portalname);
+				Assert(PortalIsValid(subportal));
+				return FetchPortalTargetList(subportal);
+			}
+
+			case T_ExecuteStmt:
+			{
+				ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt;
+				PreparedStatement *entry;
+
+				Assert(!substmt->into);
+				entry = FetchPreparedStatement(substmt->name, true);
+				return FetchPreparedStatementTargetList(entry);
+			}
+
+			default:
+				break;
+		}
+	}
+	return NIL;
+}
+
 /*
  * PortalStart
  *		Prepare a portal for execution.
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index 4a8cde52354d5be670512d519c2436b7725f8889..b6c315bd42de8a60c9d0001007f866ba31f43f3b 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 2002-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.13 2005/01/01 05:43:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.14 2005/06/22 17:45:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,5 +59,6 @@ extern PreparedStatement *FetchPreparedStatement(const char *stmt_name,
 extern void DropPreparedStatement(const char *stmt_name, bool showError);
 extern List *FetchPreparedStatementParams(const char *stmt_name);
 extern TupleDesc FetchPreparedStatementResultDesc(PreparedStatement *stmt);
+extern List *FetchPreparedStatementTargetList(PreparedStatement *stmt);
 
 #endif   /* PREPARE_H */
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index c3c87baa3f9faa82bb3c66f5f94e4101897a01d9..da2d29aea8927e07a286f16e3a9da6371591d8b3 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.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/tcop/pquery.h,v 1.34 2004/12/31 22:03:44 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/tcop/pquery.h,v 1.35 2005/06/22 17:45:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,8 @@ extern DLLIMPORT Portal ActivePortal;
 
 extern PortalStrategy ChoosePortalStrategy(List *parseTrees);
 
+extern List *FetchPortalTargetList(Portal portal);
+
 extern void PortalStart(Portal portal, ParamListInfo params,
 						Snapshot snapshot);