Commit da4e00bf authored by Tom Lane's avatar Tom Lane

When in transaction-aborted state, reject Bind message for portals containing

anything but transaction-exiting commands (ROLLBACK etc).  We already rejected
Parse and Execute in such cases, so there seems little point in allowing Bind.
This prevents at least an Assert failure, and probably worse things, since
there's a lot of infrastructure that doesn't work when not in a live
transaction.  We can also simplify the Bind logic a bit by rejecting messages
with a nonzero number of parameters, instead of the former kluge to silently
substitute NULL for each parameter.  Per bug #2033 from Joel Stevenson.
parent 0dd92d56
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.468 2005/11/03 17:11:38 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.469 2005/11/10 00:31:34 tgl Exp $
* *
* NOTES * NOTES
* this is the "main" module of the postgres backend and * this is the "main" module of the postgres backend and
...@@ -159,6 +159,9 @@ static bool log_after_parse(List *raw_parsetree_list, ...@@ -159,6 +159,9 @@ static bool log_after_parse(List *raw_parsetree_list,
static List *pg_rewrite_queries(List *querytree_list); static List *pg_rewrite_queries(List *querytree_list);
static void start_xact_command(void); static void start_xact_command(void);
static void finish_xact_command(void); static void finish_xact_command(void);
static bool IsTransactionExitStmt(Node *parsetree);
static bool IsTransactionExitStmtList(List *parseTrees);
static bool IsTransactionStmtList(List *parseTrees);
static void SigHupHandler(SIGNAL_ARGS); static void SigHupHandler(SIGNAL_ARGS);
static void log_disconnections(int code, Datum arg); static void log_disconnections(int code, Datum arg);
...@@ -915,27 +918,12 @@ exec_simple_query(const char *query_string) ...@@ -915,27 +918,12 @@ exec_simple_query(const char *query_string)
* might be safe to allow some additional utility commands in this * might be safe to allow some additional utility commands in this
* state, but not many...) * state, but not many...)
*/ */
if (IsAbortedTransactionBlockState()) if (IsAbortedTransactionBlockState() &&
{ !IsTransactionExitStmt(parsetree))
bool allowit = false;
if (IsA(parsetree, TransactionStmt))
{
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
stmt->kind == TRANS_STMT_PREPARE ||
stmt->kind == TRANS_STMT_ROLLBACK ||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
allowit = true;
}
if (!allowit)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, " errmsg("current transaction is aborted, "
"commands ignored until end of transaction block"))); "commands ignored until end of transaction block")));
}
/* Make sure we are in a transaction command */ /* Make sure we are in a transaction command */
start_xact_command(); start_xact_command();
...@@ -1244,27 +1232,12 @@ exec_parse_message(const char *query_string, /* string to execute */ ...@@ -1244,27 +1232,12 @@ exec_parse_message(const char *query_string, /* string to execute */
* (It might be safe to allow some additional utility commands in this * (It might be safe to allow some additional utility commands in this
* state, but not many...) * state, but not many...)
*/ */
if (IsAbortedTransactionBlockState()) if (IsAbortedTransactionBlockState() &&
{ !IsTransactionExitStmt(parsetree))
bool allowit = false;
if (IsA(parsetree, TransactionStmt))
{
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
stmt->kind == TRANS_STMT_PREPARE ||
stmt->kind == TRANS_STMT_ROLLBACK ||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
allowit = true;
}
if (!allowit)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, " errmsg("current transaction is aborted, "
"commands ignored until end of transaction block"))); "commands ignored until end of transaction block")));
}
/* /*
* OK to analyze, rewrite, and plan this query. Note that the * OK to analyze, rewrite, and plan this query. Note that the
...@@ -1392,7 +1365,6 @@ exec_bind_message(StringInfo input_message) ...@@ -1392,7 +1365,6 @@ exec_bind_message(StringInfo input_message)
PreparedStatement *pstmt; PreparedStatement *pstmt;
Portal portal; Portal portal;
ParamListInfo params; ParamListInfo params;
bool isaborted = IsAbortedTransactionBlockState();
pgstat_report_activity("<BIND>"); pgstat_report_activity("<BIND>");
...@@ -1449,6 +1421,22 @@ exec_bind_message(StringInfo input_message) ...@@ -1449,6 +1421,22 @@ exec_bind_message(StringInfo input_message)
errmsg("bind message supplies %d parameters, but prepared statement \"%s\" requires %d", errmsg("bind message supplies %d parameters, but prepared statement \"%s\" requires %d",
numParams, stmt_name, list_length(pstmt->argtype_list)))); numParams, stmt_name, list_length(pstmt->argtype_list))));
/*
* If we are in aborted transaction state, the only portals we can
* actually run are those containing COMMIT or ROLLBACK commands.
* We disallow binding anything else to avoid problems with infrastructure
* that expects to run inside a valid transaction. We also disallow
* binding any parameters, since we can't risk calling user-defined
* I/O functions.
*/
if (IsAbortedTransactionBlockState() &&
(!IsTransactionExitStmtList(pstmt->query_list) ||
numParams != 0))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
"commands ignored until end of transaction block")));
/* /*
* Create the portal. Allow silent replacement of an existing portal only * Create the portal. Allow silent replacement of an existing portal only
* if the unnamed portal is specified. * if the unnamed portal is specified.
...@@ -1465,10 +1453,6 @@ exec_bind_message(StringInfo input_message) ...@@ -1465,10 +1453,6 @@ exec_bind_message(StringInfo input_message)
/* /*
* Fetch parameters, if any, and store in the portal's memory context. * Fetch parameters, if any, and store in the portal's memory context.
*
* In an aborted transaction, we can't risk calling user-defined functions,
* but we can't fail to Bind either, so bind all parameters to null
* values.
*/ */
if (numParams > 0) if (numParams > 0)
{ {
...@@ -1493,14 +1477,6 @@ exec_bind_message(StringInfo input_message) ...@@ -1493,14 +1477,6 @@ exec_bind_message(StringInfo input_message)
if (!isNull) if (!isNull)
{ {
const char *pvalue = pq_getmsgbytes(input_message, plength); const char *pvalue = pq_getmsgbytes(input_message, plength);
if (isaborted)
{
/* We don't bother to check the format in this case */
isNull = true;
}
else
{
int16 pformat; int16 pformat;
StringInfoData pbuf; StringInfoData pbuf;
char csave; char csave;
...@@ -1585,7 +1561,6 @@ exec_bind_message(StringInfo input_message) ...@@ -1585,7 +1561,6 @@ exec_bind_message(StringInfo input_message)
/* Restore message buffer contents */ /* Restore message buffer contents */
pbuf.data[plength] = csave; pbuf.data[plength] = csave;
} }
}
params[i].kind = PARAM_NUM; params[i].kind = PARAM_NUM;
params[i].id = i + 1; params[i].id = i + 1;
...@@ -1621,8 +1596,7 @@ exec_bind_message(StringInfo input_message) ...@@ -1621,8 +1596,7 @@ exec_bind_message(StringInfo input_message)
* statement context for planning is correct (see notes in * statement context for planning is correct (see notes in
* exec_parse_message). * exec_parse_message).
*/ */
if (pstmt->plan_list == NIL && pstmt->query_list != NIL && if (pstmt->plan_list == NIL && pstmt->query_list != NIL)
!isaborted)
{ {
MemoryContext oldContext = MemoryContextSwitchTo(pstmt->context); MemoryContext oldContext = MemoryContextSwitchTo(pstmt->context);
...@@ -1665,8 +1639,6 @@ exec_execute_message(const char *portal_name, long max_rows) ...@@ -1665,8 +1639,6 @@ exec_execute_message(const char *portal_name, long max_rows)
CommandDest dest; CommandDest dest;
DestReceiver *receiver; DestReceiver *receiver;
Portal portal; Portal portal;
bool is_trans_stmt = false;
bool is_trans_exit = false;
bool completed; bool completed;
char completionTag[COMPLETION_TAG_BUFSIZE]; char completionTag[COMPLETION_TAG_BUFSIZE];
struct timeval start_t, struct timeval start_t,
...@@ -1748,26 +1720,6 @@ exec_execute_message(const char *portal_name, long max_rows) ...@@ -1748,26 +1720,6 @@ exec_execute_message(const char *portal_name, long max_rows)
BeginCommand(portal->commandTag, dest); BeginCommand(portal->commandTag, dest);
/* Check for transaction-control commands */
if (list_length(portal->parseTrees) == 1)
{
Query *query = (Query *) linitial(portal->parseTrees);
if (query->commandType == CMD_UTILITY &&
query->utilityStmt != NULL &&
IsA(query->utilityStmt, TransactionStmt))
{
TransactionStmt *stmt = (TransactionStmt *) query->utilityStmt;
is_trans_stmt = true;
if (stmt->kind == TRANS_STMT_COMMIT ||
stmt->kind == TRANS_STMT_PREPARE ||
stmt->kind == TRANS_STMT_ROLLBACK ||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
is_trans_exit = true;
}
}
/* /*
* Create dest receiver in MessageContext (we don't want it in transaction * Create dest receiver in MessageContext (we don't want it in transaction
* context, because that may get deleted if portal contains VACUUM). * context, because that may get deleted if portal contains VACUUM).
...@@ -1784,14 +1736,12 @@ exec_execute_message(const char *portal_name, long max_rows) ...@@ -1784,14 +1736,12 @@ exec_execute_message(const char *portal_name, long max_rows)
* If we are in aborted transaction state, the only portals we can * If we are in aborted transaction state, the only portals we can
* actually run are those containing COMMIT or ROLLBACK commands. * actually run are those containing COMMIT or ROLLBACK commands.
*/ */
if (IsAbortedTransactionBlockState()) if (IsAbortedTransactionBlockState() &&
{ !IsTransactionExitStmtList(portal->parseTrees))
if (!is_trans_exit)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, " errmsg("current transaction is aborted, "
"commands ignored until end of transaction block"))); "commands ignored until end of transaction block")));
}
/* Check for cancel signal before we start execution */ /* Check for cancel signal before we start execution */
CHECK_FOR_INTERRUPTS(); CHECK_FOR_INTERRUPTS();
...@@ -1812,7 +1762,7 @@ exec_execute_message(const char *portal_name, long max_rows) ...@@ -1812,7 +1762,7 @@ exec_execute_message(const char *portal_name, long max_rows)
if (completed) if (completed)
{ {
if (is_trans_stmt) if (IsTransactionStmtList(portal->parseTrees))
{ {
/* /*
* If this was a transaction control statement, commit it. We * If this was a transaction control statement, commit it. We
...@@ -2024,6 +1974,56 @@ finish_xact_command(void) ...@@ -2024,6 +1974,56 @@ finish_xact_command(void)
} }
/*
* Convenience routines for checking whether a statement is one of the
* ones that we allow in transaction-aborted state.
*/
static bool
IsTransactionExitStmt(Node *parsetree)
{
if (parsetree && IsA(parsetree, TransactionStmt))
{
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
stmt->kind == TRANS_STMT_PREPARE ||
stmt->kind == TRANS_STMT_ROLLBACK ||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
return true;
}
return false;
}
static bool
IsTransactionExitStmtList(List *parseTrees)
{
if (list_length(parseTrees) == 1)
{
Query *query = (Query *) linitial(parseTrees);
if (query->commandType == CMD_UTILITY &&
IsTransactionExitStmt(query->utilityStmt))
return true;
}
return false;
}
static bool
IsTransactionStmtList(List *parseTrees)
{
if (list_length(parseTrees) == 1)
{
Query *query = (Query *) linitial(parseTrees);
if (query->commandType == CMD_UTILITY &&
query->utilityStmt && IsA(query->utilityStmt, TransactionStmt))
return true;
}
return false;
}
/* -------------------------------- /* --------------------------------
* signal handler routines used in PostgresMain() * signal handler routines used in PostgresMain()
* -------------------------------- * --------------------------------
......
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