summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/commands/prepare.c26
-rw-r--r--src/backend/tcop/postgres.c53
-rw-r--r--src/include/commands/prepare.h3
3 files changed, 78 insertions, 4 deletions
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index babae5e1483..b301a75b276 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -10,7 +10,7 @@
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.23.4.2 2004/12/13 00:17:52 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.23.4.3 2005/12/14 17:06:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -443,6 +443,30 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt)
}
/*
+ * 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;
+}
+
+/*
* 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 0526d3c8331..6f424f0976b 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.375.2.4 2005/11/10 00:31:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.375.2.5 2005/12/14 17:07:00 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@@ -1644,6 +1644,15 @@ exec_describe_statement_message(const char *stmt_name)
List *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);
@@ -1657,6 +1666,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 != Remote)
return; /* can't actually do anything... */
@@ -1703,12 +1728,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 != Remote)
return; /* can't actually do anything... */
@@ -2715,7 +2764,7 @@ PostgresMain(int argc, char *argv[], const char *username)
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
- puts("$Revision: 1.375.2.4 $ $Date: 2005/11/10 00:31:59 $\n");
+ puts("$Revision: 1.375.2.5 $ $Date: 2005/12/14 17:07:00 $\n");
}
/*
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index fdbea2c6083..5c1fcfaa02e 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
- * $Id: prepare.h,v 1.8 2003/08/08 21:42:40 momjian Exp $
+ * $Id: prepare.h,v 1.8.4.1 2005/12/14 17:07:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -58,5 +58,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 bool PreparedStatementReturnsTuples(PreparedStatement *stmt);
#endif /* PREPARE_H */