diff options
Diffstat (limited to 'src/backend/tcop')
| -rw-r--r-- | src/backend/tcop/postgres.c | 56 | ||||
| -rw-r--r-- | src/backend/tcop/utility.c | 31 |
2 files changed, 82 insertions, 5 deletions
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index f2e892374b..e8c4820a71 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.578 2009/12/16 23:05:00 petere Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.579 2009/12/19 01:32:36 sriggs Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -62,6 +62,7 @@ #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" @@ -2643,8 +2644,8 @@ StatementCancelHandler(SIGNAL_ARGS) * the interrupt immediately. No point in interrupting if we're * waiting for input, however. */ - if (ImmediateInterruptOK && InterruptHoldoffCount == 0 && - CritSectionCount == 0 && !DoingCommandRead) + if (InterruptHoldoffCount == 0 && CritSectionCount == 0 && + (DoingCommandRead || ImmediateInterruptOK)) { /* bump holdoff count to make ProcessInterrupts() a no-op */ /* until we are done getting ready for it */ @@ -2735,9 +2736,58 @@ ProcessInterrupts(void) (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling autovacuum task"))); else + { + int cancelMode = MyProc->recoveryConflictMode; + + /* + * 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) + { + cancelMode = CONFLICT_MODE_FATAL; + } + + switch (cancelMode) + { + case CONFLICT_MODE_FATAL: + 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. + */ + Assert(RecoveryInProgress()); + AbortOutOfAnyTransaction(); + ereport(ERROR, + (errcode(ERRCODE_QUERY_CANCELED), + errmsg("canceling statement due to conflict with recovery"))); + return; + + default: + /* No conflict pending, so fall through */ + break; + } + ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling statement due to user request"))); + } } /* If we get here, do nothing (probably, QueryCancelPending was reset) */ } diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 10fb728fc7..53e59b59b0 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.324 2009/12/15 20:04:49 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.325 2009/12/19 01:32:36 sriggs Exp $ * *------------------------------------------------------------------------- */ @@ -351,6 +351,7 @@ standard_ProcessUtility(Node *parsetree, break; case TRANS_STMT_PREPARE: + PreventCommandDuringRecovery(); if (!PrepareTransactionBlock(stmt->gid)) { /* report unsuccessful commit in completionTag */ @@ -360,11 +361,13 @@ standard_ProcessUtility(Node *parsetree, break; case TRANS_STMT_COMMIT_PREPARED: + PreventCommandDuringRecovery(); PreventTransactionChain(isTopLevel, "COMMIT PREPARED"); FinishPreparedTransaction(stmt->gid, true); break; case TRANS_STMT_ROLLBACK_PREPARED: + PreventCommandDuringRecovery(); PreventTransactionChain(isTopLevel, "ROLLBACK PREPARED"); FinishPreparedTransaction(stmt->gid, false); break; @@ -742,6 +745,7 @@ standard_ProcessUtility(Node *parsetree, break; case T_GrantStmt: + PreventCommandDuringRecovery(); ExecuteGrantStmt((GrantStmt *) parsetree); break; @@ -923,6 +927,7 @@ standard_ProcessUtility(Node *parsetree, case T_NotifyStmt: { NotifyStmt *stmt = (NotifyStmt *) parsetree; + PreventCommandDuringRecovery(); Async_Notify(stmt->conditionname); } @@ -931,6 +936,7 @@ standard_ProcessUtility(Node *parsetree, case T_ListenStmt: { ListenStmt *stmt = (ListenStmt *) parsetree; + PreventCommandDuringRecovery(); CheckRestrictedOperation("LISTEN"); Async_Listen(stmt->conditionname); @@ -940,6 +946,7 @@ standard_ProcessUtility(Node *parsetree, case T_UnlistenStmt: { UnlistenStmt *stmt = (UnlistenStmt *) parsetree; + PreventCommandDuringRecovery(); CheckRestrictedOperation("UNLISTEN"); if (stmt->conditionname) @@ -960,10 +967,12 @@ standard_ProcessUtility(Node *parsetree, break; case T_ClusterStmt: + PreventCommandDuringRecovery(); cluster((ClusterStmt *) parsetree, isTopLevel); break; case T_VacuumStmt: + PreventCommandDuringRecovery(); vacuum((VacuumStmt *) parsetree, InvalidOid, true, NULL, false, isTopLevel); break; @@ -1083,12 +1092,21 @@ standard_ProcessUtility(Node *parsetree, ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to do CHECKPOINT"))); - RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT); + /* + * You might think we should have a PreventCommandDuringRecovery() + * here, but we interpret a CHECKPOINT command during recovery + * as a request for a restartpoint instead. We allow this since + * it can be a useful way of reducing switchover time when + * using various forms of replication. + */ + RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT | + (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE)); break; case T_ReindexStmt: { ReindexStmt *stmt = (ReindexStmt *) parsetree; + PreventCommandDuringRecovery(); switch (stmt->kind) { @@ -2604,3 +2622,12 @@ GetCommandLogLevel(Node *parsetree) return lev; } + +void +PreventCommandDuringRecovery(void) +{ + if (RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), + errmsg("cannot be executed during recovery"))); +} |
