Enable rethrowing errors which occured while IDLE IN TRANSACTION state when explicitl... feature/idle-query-cancellation
authorAndres Freund <[email protected]>
Sat, 30 Oct 2010 08:36:41 +0000 (10:36 +0200)
committerAndres Freund <[email protected]>
Sat, 30 Oct 2010 08:42:23 +0000 (10:42 +0200)
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.

src/backend/tcop/fastpath.c
src/backend/tcop/postgres.c
src/backend/utils/error/elog.c
src/include/tcop/tcopprot.h
src/include/utils/elog.h

index af58e4ea26eaff3d2edf50852d977621d5b90252..ed6f02ac9b6a9a8ae5bf6e8f7937140660e0a8a5 100644 (file)
@@ -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
index 505d1363a6004954740c935bef5d6e37c2482dc5..f1441018ea149018fe7111bfeec9fa81447833f2 100644 (file)
@@ -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 */
index 6e174d86ec6e219be8354b27e4685d5283e3de19..d8895c626e4561bb00b75b1acc6f110173d9ddb3 100644 (file)
@@ -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--;
 }
index f4026ecab120837566da0a93b82f3921f639de58..b529ce10c8144afd5a3bdf6888edb4506109c3df 100644 (file)
@@ -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 */
index 1853e1dfeb5ebb425d8e9fbf73d9f1bd06130b41..529265adbe681c401d7fe235ccfc26dd0954baba 100644 (file)
@@ -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. */