aboutsummaryrefslogtreecommitdiff
path: root/src/backend/tcop/postgres.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/tcop/postgres.c')
-rw-r--r--src/backend/tcop/postgres.c201
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");