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.
This commit is contained in:
parent
426292663a
commit
4ce6be4f5e
@ -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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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 @@
|
|||||||
*
|
*
|
||||||
* 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,
|
|||||||
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 */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user