diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index dc84f57506..1ee955ebe1 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.43 2005/11/29 01:25:49 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.44 2005/12/14 17:06:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -447,6 +447,30 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt)
 	return NULL;
 }
 
+/*
+ * Given a prepared statement, determine whether it will return tuples.
+ *
+ * Note: this is used rather than just testing the result of
+ * FetchPreparedStatementResultDesc() because that routine can fail if
+ * invoked in an aborted transaction.  This one is safe to use in any
+ * context.  Be sure to keep the two routines in sync!
+ */
+bool
+PreparedStatementReturnsTuples(PreparedStatement *stmt)
+{
+	switch (ChoosePortalStrategy(stmt->query_list))
+	{
+		case PORTAL_ONE_SELECT:
+		case PORTAL_UTIL_SELECT:
+			return true;
+
+		case PORTAL_MULTI_QUERY:
+			/* will not return tuples */
+			break;
+	}
+	return false;
+}
+
 /*
  * Given a prepared statement that returns tuples, extract the query
  * targetlist.	Returns NIL if the statement doesn't have a determinable
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3774c094b7..5c0730050d 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.470 2005/11/22 18:17:21 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.471 2005/12/14 17:06:27 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -1849,6 +1849,15 @@ exec_describe_statement_message(const char *stmt_name)
 	ListCell   *l;
 	StringInfoData buf;
 
+	/*
+	 * Start up a transaction command. (Note that this will normally change
+	 * current memory context.) Nothing happens if we are already in one.
+	 */
+	start_xact_command();
+
+	/* Switch back to message context */
+	MemoryContextSwitchTo(MessageContext);
+
 	/* Find prepared statement */
 	if (stmt_name[0] != '\0')
 		pstmt = FetchPreparedStatement(stmt_name, true);
@@ -1862,6 +1871,22 @@ exec_describe_statement_message(const char *stmt_name)
 					 errmsg("unnamed prepared statement does not exist")));
 	}
 
+	/*
+	 * If we are in aborted transaction state, we can't safely create a result
+	 * tupledesc, because that needs catalog accesses.  Hence, refuse to
+	 * Describe statements that return data.  (We shouldn't just refuse all
+	 * Describes, since that might break the ability of some clients to issue
+	 * COMMIT or ROLLBACK commands, if they use code that blindly Describes
+	 * whatever it does.)  We can Describe parameters without doing anything
+	 * dangerous, so we don't restrict that.
+	 */
+	if (IsAbortedTransactionBlockState() &&
+		PreparedStatementReturnsTuples(pstmt))
+		ereport(ERROR,
+				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+				 errmsg("current transaction is aborted, "
+						"commands ignored until end of transaction block")));
+
 	if (whereToSendOutput != DestRemote)
 		return;					/* can't actually do anything... */
 
@@ -1902,12 +1927,36 @@ exec_describe_portal_message(const char *portal_name)
 {
 	Portal		portal;
 
+	/*
+	 * Start up a transaction command. (Note that this will normally change
+	 * current memory context.) Nothing happens if we are already in one.
+	 */
+	start_xact_command();
+
+	/* Switch back to message context */
+	MemoryContextSwitchTo(MessageContext);
+
 	portal = GetPortalByName(portal_name);
 	if (!PortalIsValid(portal))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_CURSOR),
 				 errmsg("portal \"%s\" does not exist", portal_name)));
 
+	/*
+	 * If we are in aborted transaction state, we can't run
+	 * SendRowDescriptionMessage(), because that needs catalog accesses.
+	 * Hence, refuse to Describe portals that return data.  (We shouldn't just
+	 * refuse all Describes, since that might break the ability of some
+	 * clients to issue COMMIT or ROLLBACK commands, if they use code that
+	 * blindly Describes whatever it does.)
+	 */
+	if (IsAbortedTransactionBlockState() &&
+		portal->tupDesc)
+		ereport(ERROR,
+				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+				 errmsg("current transaction is aborted, "
+						"commands ignored until end of transaction block")));
+
 	if (whereToSendOutput != DestRemote)
 		return;					/* can't actually do anything... */
 
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index 4eafb91480..50767740cc 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.15 2005/11/29 01:25:50 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.16 2005/12/14 17:06:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,6 +60,7 @@ 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 bool PreparedStatementReturnsTuples(PreparedStatement *stmt);
 extern List *FetchPreparedStatementTargetList(PreparedStatement *stmt);
 
 #endif   /* PREPARE_H */