Commit 4ce6be4f authored by Tom Lane's avatar Tom Lane

Defend against crash while processing Describe Statement or Describe Portal

messages, when client attempts to execute these outside a transaction (start
one) or in a failed transaction (reject message, except for COMMIT/ROLLBACK
statements which we can handle).  Per report from Francisco Figueiredo Jr.
parent 42629266
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Copyright (c) 2002-2005, PostgreSQL Global Development Group * Copyright (c) 2002-2005, PostgreSQL Global Development Group
* *
* IDENTIFICATION * 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) ...@@ -447,6 +447,30 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt)
return NULL; 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 * Given a prepared statement that returns tuples, extract the query
* targetlist. Returns NIL if the statement doesn't have a determinable * targetlist. Returns NIL if the statement doesn't have a determinable
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* this is the "main" module of the postgres backend and * this is the "main" module of the postgres backend and
...@@ -1849,6 +1849,15 @@ exec_describe_statement_message(const char *stmt_name) ...@@ -1849,6 +1849,15 @@ exec_describe_statement_message(const char *stmt_name)
ListCell *l; ListCell *l;
StringInfoData buf; 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 */ /* Find prepared statement */
if (stmt_name[0] != '\0') if (stmt_name[0] != '\0')
pstmt = FetchPreparedStatement(stmt_name, true); pstmt = FetchPreparedStatement(stmt_name, true);
...@@ -1862,6 +1871,22 @@ exec_describe_statement_message(const char *stmt_name) ...@@ -1862,6 +1871,22 @@ exec_describe_statement_message(const char *stmt_name)
errmsg("unnamed prepared statement does not exist"))); 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) if (whereToSendOutput != DestRemote)
return; /* can't actually do anything... */ return; /* can't actually do anything... */
...@@ -1902,12 +1927,36 @@ exec_describe_portal_message(const char *portal_name) ...@@ -1902,12 +1927,36 @@ exec_describe_portal_message(const char *portal_name)
{ {
Portal portal; 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); portal = GetPortalByName(portal_name);
if (!PortalIsValid(portal)) if (!PortalIsValid(portal))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_CURSOR), (errcode(ERRCODE_UNDEFINED_CURSOR),
errmsg("portal \"%s\" does not exist", portal_name))); 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) if (whereToSendOutput != DestRemote)
return; /* can't actually do anything... */ return; /* can't actually do anything... */
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 2002-2005, PostgreSQL Global Development Group * 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, ...@@ -60,6 +60,7 @@ extern PreparedStatement *FetchPreparedStatement(const char *stmt_name,
extern void DropPreparedStatement(const char *stmt_name, bool showError); extern void DropPreparedStatement(const char *stmt_name, bool showError);
extern List *FetchPreparedStatementParams(const char *stmt_name); extern List *FetchPreparedStatementParams(const char *stmt_name);
extern TupleDesc FetchPreparedStatementResultDesc(PreparedStatement *stmt); extern TupleDesc FetchPreparedStatementResultDesc(PreparedStatement *stmt);
extern bool PreparedStatementReturnsTuples(PreparedStatement *stmt);
extern List *FetchPreparedStatementTargetList(PreparedStatement *stmt); extern List *FetchPreparedStatementTargetList(PreparedStatement *stmt);
#endif /* PREPARE_H */ #endif /* PREPARE_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment