diff options
Diffstat (limited to 'src/backend/tcop/postgres.c')
-rw-r--r-- | src/backend/tcop/postgres.c | 201 |
1 files changed, 146 insertions, 55 deletions
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index ad8678e2200..8d55e3575a0 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.582 2010/01/15 09:19:04 heikki Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.583 2010/01/16 10:05:50 sriggs Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -63,7 +63,6 @@ #include "storage/proc.h" #include "storage/procsignal.h" #include "storage/sinval.h" -#include "storage/standby.h" #include "tcop/fastpath.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" @@ -173,6 +172,8 @@ static int UseNewLine = 1; /* Use newlines query delimiters (the default) */ static int UseNewLine = 0; /* Use EOF as query delimiters */ #endif /* TCOP_DONTUSENEWLINE */ +/* whether we were cancelled during recovery by conflict processing or not */ +static bool RecoveryConflictPending = false; /* ---------------------------------------------------------------- * decls for routines only used in this file @@ -186,6 +187,7 @@ static List *pg_rewrite_query(Query *query); static bool check_log_statement(List *stmt_list); static int errdetail_execute(List *raw_parsetree_list); static int errdetail_params(ParamListInfo params); +static int errdetail_abort(void); static void start_xact_command(void); static void finish_xact_command(void); static bool IsTransactionExitStmt(Node *parsetree); @@ -944,7 +946,8 @@ exec_simple_query(const char *query_string) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"))); + "commands ignored until end of transaction block"), + errdetail_abort())); /* Make sure we are in a transaction command */ start_xact_command(); @@ -1253,7 +1256,8 @@ exec_parse_message(const char *query_string, /* string to execute */ ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"))); + "commands ignored until end of transaction block"), + errdetail_abort())); /* * Set up a snapshot if parse analysis/planning will need one. @@ -1533,7 +1537,8 @@ exec_bind_message(StringInfo input_message) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"))); + "commands ignored until end of transaction block"), + errdetail_abort())); /* * Create the portal. Allow silent replacement of an existing portal only @@ -1974,7 +1979,8 @@ exec_execute_message(const char *portal_name, long max_rows) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"))); + "commands ignored until end of transaction block"), + errdetail_abort())); /* Check for cancel signal before we start execution */ CHECK_FOR_INTERRUPTS(); @@ -2235,6 +2241,20 @@ errdetail_params(ParamListInfo params) } /* + * errdetail_abort + * + * Add an errdetail() line showing abort reason, if any. + */ +static int +errdetail_abort(void) +{ + if (MyProc->recoveryConflictPending) + errdetail("abort reason: recovery conflict"); + + return 0; +} + +/* * exec_describe_statement_message * * Process a "Describe" message for a prepared statement @@ -2291,7 +2311,8 @@ exec_describe_statement_message(const char *stmt_name) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"))); + "commands ignored until end of transaction block"), + errdetail_abort())); if (whereToSendOutput != DestRemote) return; /* can't actually do anything... */ @@ -2371,7 +2392,8 @@ exec_describe_portal_message(const char *portal_name) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " - "commands ignored until end of transaction block"))); + "commands ignored until end of transaction block"), + errdetail_abort())); if (whereToSendOutput != DestRemote) return; /* can't actually do anything... */ @@ -2678,6 +2700,97 @@ SigHupHandler(SIGNAL_ARGS) got_SIGHUP = true; } +/* + * RecoveryConflictInterrupt: out-of-line portion of recovery conflict + * handling ollowing receipt of SIGUSR1. Designed to be similar to die() + * and StatementCancelHandler(). Called only by a normal user backend + * that begins a transaction during recovery. + */ +void +RecoveryConflictInterrupt(ProcSignalReason reason) +{ + int save_errno = errno; + + /* + * Don't joggle the elbow of proc_exit + */ + if (!proc_exit_inprogress) + { + switch (reason) + { + case PROCSIG_RECOVERY_CONFLICT_LOCK: + case PROCSIG_RECOVERY_CONFLICT_TABLESPACE: + case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT: + /* + * If we aren't in a transaction any longer then ignore. + */ + if (!IsTransactionOrTransactionBlock()) + return; + + /* + * If we can abort just the current subtransaction then we + * are OK to throw an ERROR to resolve the conflict. Otherwise + * drop through to the FATAL case. + * + * XXX other times that we can throw just an ERROR *may* be + * PROCSIG_RECOVERY_CONFLICT_LOCK + * if no locks are held in parent transactions + * + * PROCSIG_RECOVERY_CONFLICT_SNAPSHOT + * if no snapshots are held by parent transactions + * and the transaction is not serializable + * + * PROCSIG_RECOVERY_CONFLICT_TABLESPACE + * if no temp files or cursors open in parent transactions + */ + if (!IsSubTransaction()) + { + /* + * If we already aborted then we no longer need to cancel. + * We do this here since we do not wish to ignore aborted + * subtransactions, which must cause FATAL, currently. + */ + if (IsAbortedTransactionBlockState()) + return; + + RecoveryConflictPending = true; + QueryCancelPending = true; + InterruptPending = true; + break; + } + + /* Intentional drop through to session cancel */ + + case PROCSIG_RECOVERY_CONFLICT_DATABASE: + RecoveryConflictPending = true; + ProcDiePending = true; + InterruptPending = true; + break; + + default: + elog(FATAL, "Unknown conflict mode"); + } + + /* + * If it's safe to interrupt, and we're waiting for input or a lock, + * service the interrupt immediately + */ + if (ImmediateInterruptOK && InterruptHoldoffCount == 0 && + CritSectionCount == 0) + { + /* bump holdoff count to make ProcessInterrupts() a no-op */ + /* until we are done getting ready for it */ + InterruptHoldoffCount++; + LockWaitCancel(); /* prevent CheckDeadLock from running */ + DisableNotifyInterrupt(); + DisableCatchupInterrupt(); + InterruptHoldoffCount--; + ProcessInterrupts(); + } + } + + errno = save_errno; +} /* * ProcessInterrupts: out-of-line portion of CHECK_FOR_INTERRUPTS() macro @@ -2707,6 +2820,10 @@ ProcessInterrupts(void) ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("terminating autovacuum process due to administrator command"))); + else if (RecoveryConflictPending) + ereport(FATAL, + (errcode(ERRCODE_ADMIN_SHUTDOWN), + errmsg("terminating connection due to conflict with recovery"))); else ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), @@ -2745,57 +2862,26 @@ ProcessInterrupts(void) (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling autovacuum task"))); } + if (RecoveryConflictPending) { - int cancelMode = MyProc->recoveryConflictMode; + ImmediateInterruptOK = false; /* not idle anymore */ + DisableNotifyInterrupt(); + DisableCatchupInterrupt(); - /* - * XXXHS: We don't yet have a clean way to cancel an - * idle-in-transaction session, so make it FATAL instead. - * This isn't as bad as it looks because we don't issue a - * CONFLICT_MODE_ERROR for a session with proc->xmin == 0 - * on cleanup conflicts. There's a possibility that we - * marked somebody as a conflict and then they go idle. - */ - if (DoingCommandRead && IsTransactionBlock() && - cancelMode == CONFLICT_MODE_ERROR) + if (DoingCommandRead) { - cancelMode = CONFLICT_MODE_FATAL; + ProcDiePending = false; + QueryCancelPending = false; + ereport(FATAL, + (errcode(ERRCODE_ADMIN_SHUTDOWN), + errmsg("terminating connection due to conflict with recovery"))); } - - switch (cancelMode) + else { - case CONFLICT_MODE_FATAL: - ImmediateInterruptOK = false; /* not idle anymore */ - DisableNotifyInterrupt(); - DisableCatchupInterrupt(); - Assert(RecoveryInProgress()); - ereport(FATAL, - (errcode(ERRCODE_QUERY_CANCELED), - errmsg("canceling session due to conflict with recovery"))); - - case CONFLICT_MODE_ERROR: - /* - * We are aborting because we need to release - * locks. So we need to abort out of all - * subtransactions to make sure we release - * all locks at whatever their level. - * - * XXX Should we try to examine the - * transaction tree and cancel just enough - * subxacts to remove locks? Doubt it. - */ - ImmediateInterruptOK = false; /* not idle anymore */ - DisableNotifyInterrupt(); - DisableCatchupInterrupt(); - Assert(RecoveryInProgress()); - AbortOutOfAnyTransaction(); - ereport(ERROR, - (errcode(ERRCODE_QUERY_CANCELED), - errmsg("canceling statement due to conflict with recovery"))); - - default: - /* No conflict pending, so fall through */ - break; + QueryCancelPending = false; + ereport(ERROR, + (errcode(ERRCODE_QUERY_CANCELED), + errmsg("canceling statement due to conflict with recovery"))); } } @@ -3637,7 +3723,12 @@ PostgresMain(int argc, char *argv[], const char *username) */ if (send_ready_for_query) { - if (IsTransactionOrTransactionBlock()) + if (IsAbortedTransactionBlockState()) + { + set_ps_display("idle in transaction (aborted)", false); + pgstat_report_activity("<IDLE> in transaction (aborted)"); + } + else if (IsTransactionOrTransactionBlock()) { set_ps_display("idle in transaction", false); pgstat_report_activity("<IDLE> in transaction"); |