From: Andres Freund Date: Sat, 30 Oct 2010 08:36:41 +0000 (+0200) Subject: Enable rethrowing errors which occured while IDLE IN TRANSACTION state when explicitl... X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=8d39abbeea8362048eb437289fab718ea8ef23f3;p=users%2Fandresfreund%2Fpostgres.git Enable rethrowing errors which occured while IDLE IN TRANSACTION state when explicitly told so via a new error flag. This makes cancelling an idle transactions more visible to the cancelled connection so it can react more sensibly than just reacting to an ominous "in failed transaction" error. --- diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c index af58e4ea26..ed6f02ac9b 100644 --- a/src/backend/tcop/fastpath.c +++ b/src/backend/tcop/fastpath.c @@ -298,10 +298,7 @@ HandleFunctionRequest(StringInfo msgBuf) * won't lose sync with the frontend. */ if (IsAbortedTransactionBlockState()) - ereport(ERROR, - (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), - errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"))); + RaiseInFailedTransactionError(); /* * Now that we know we are in a valid transaction, set snapshot in case diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 505d1363a6..f1441018ea 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -183,6 +183,13 @@ static ProcSignalReason RecoveryConflictReason; */ static bool silent_error_while_idle = false; +/* + * Which error occured while we were idle? We want to rethrow it in + * the face of the next query - except if it is a ROLLBACK/COMMIT - it + * will silently be swallowed there... + */ +ErrorData *silent_error_while_idle_edata; + /* ---------------------------------------------------------------- * decls for routines only used in this file * ---------------------------------------------------------------- @@ -952,11 +959,7 @@ exec_simple_query(const char *query_string) */ if (IsAbortedTransactionBlockState() && !IsTransactionExitStmt(parsetree)) - ereport(ERROR, - (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), - errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"), - errdetail_abort())); + RaiseInFailedTransactionError(); /* Make sure we are in a transaction command */ start_xact_command(); @@ -1262,12 +1265,7 @@ exec_parse_message(const char *query_string, /* string to execute */ */ if (IsAbortedTransactionBlockState() && !IsTransactionExitStmt(raw_parse_tree)) - ereport(ERROR, - (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), - errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"), - errdetail_abort())); - + RaiseInFailedTransactionError(); /* * Set up a snapshot if parse analysis/planning will need one. */ @@ -1543,12 +1541,7 @@ exec_bind_message(StringInfo input_message) if (IsAbortedTransactionBlockState() && (!IsTransactionExitStmt(psrc->raw_parse_tree) || numParams != 0)) - ereport(ERROR, - (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), - errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"), - errdetail_abort())); - + RaiseInFailedTransactionError(); /* * Create the portal. Allow silent replacement of an existing portal only * if the unnamed portal is specified. @@ -1985,11 +1978,7 @@ exec_execute_message(const char *portal_name, long max_rows) */ if (IsAbortedTransactionBlockState() && !IsTransactionExitStmtList(portal->stmts)) - ereport(ERROR, - (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), - errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"), - errdetail_abort())); + RaiseInFailedTransactionError(); /* Check for cancel signal before we start execution */ CHECK_FOR_INTERRUPTS(); @@ -2353,11 +2342,7 @@ exec_describe_statement_message(const char *stmt_name) */ if (IsAbortedTransactionBlockState() && psrc->resultDesc) - ereport(ERROR, - (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), - errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"), - errdetail_abort())); + RaiseInFailedTransactionError(); if (whereToSendOutput != DestRemote) return; /* can't actually do anything... */ @@ -2434,11 +2419,7 @@ exec_describe_portal_message(const char *portal_name) */ if (IsAbortedTransactionBlockState() && portal->tupDesc) - ereport(ERROR, - (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), - errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"), - errdetail_abort())); + RaiseInFailedTransactionError(); if (whereToSendOutput != DestRemote) return; /* can't actually do anything... */ @@ -2571,6 +2552,18 @@ IsTransactionStmtList(List *parseTrees) return false; } +void +RaiseInFailedTransactionError(void){ + if(silent_error_while_idle_edata){ + ReThrowError(silent_error_while_idle_edata); + } + ereport(ERROR, + (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), + errmsg("current transaction is aborted, " + "commands ignored until end of transaction block"), + errdetail_abort())); +} + /* Release any existing unnamed prepared statement */ static void drop_unnamed_stmt(void) @@ -2967,7 +2960,7 @@ ProcessInterrupts(void) * because that would be unexpected as well. */ silent_error_while_idle = true; - error |= LOG_NO_CLIENT; + error |= LOG_NO_CLIENT|LOG_RE_THROW_AFTER_SYNC; } @@ -2985,7 +2978,7 @@ ProcessInterrupts(void) if (DoingCommandRead) { silent_error_while_idle = true; - error |= LOG_NO_CLIENT; + error |= LOG_NO_CLIENT|LOG_RE_THROW_AFTER_SYNC; } ImmediateInterruptOK = false; /* not idle anymore */ diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 6e174d86ec..d8895c626e 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -1173,6 +1173,29 @@ EmitErrorReport(void) if (edata->output_to_client && !(edata->eflags & LOG_NO_CLIENT)) send_message_to_frontend(edata); + if(silent_error_while_idle_edata){ + FreeErrorData(silent_error_while_idle_edata); + silent_error_while_idle_edata = 0; + } + + if (edata->eflags & LOG_RE_THROW_AFTER_SYNC){ + Assert(edata->elevel >= ERROR); + + /* The old context is already saved above and reset + * below... We cannot store the ErrorData in any other context + * than TopMemoryContext - they all may get reset inbetween + * two EmitErrorReport() calls. + * As there is no convenient point to reset the variable using + * any transaction bound variable is not possible without + * leaving a stray pointer. + */ + MemoryContextSwitchTo(TopMemoryContext); + silent_error_while_idle_edata = CopyErrorData(); + + /* We dont want to save/rethrow that error again */ + silent_error_while_idle_edata->eflags &= ~(LOG_RE_THROW_AFTER_SYNC|LOG_NO_CLIENT); + } + MemoryContextSwitchTo(oldcontext); recursion_depth--; } diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index f4026ecab1..b529ce10c8 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -45,6 +45,8 @@ typedef enum extern int log_statement; +extern ErrorData *silent_error_while_idle_edata; + extern List *pg_parse_and_rewrite(const char *query_string, Oid *paramTypes, int numParams); extern List *pg_parse_query(const char *query_string); @@ -81,5 +83,5 @@ extern void set_debug_options(int debug_flag, extern bool set_plan_disabling_options(const char *arg, GucContext context, GucSource source); extern const char *get_stats_option_name(const char *arg); - +extern void RaiseInFailedTransactionError(void); #endif /* TCOPPROT_H */ diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 1853e1dfeb..529265adbe 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -23,6 +23,8 @@ * if it would confuse the client at * that moment */ +#define LOG_RE_THROW_AFTER_SYNC (2<<29) + /* Error level codes */ #define DEBUG5 10 /* Debugging messages, in categories of * decreasing detail. */