hs-query-cancel.patch
text/x-diff
Filename: hs-query-cancel.patch
Type: text/x-diff
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/access/transam/xact.c | 0 | 0 |
| src/backend/storage/ipc/procarray.c | 0 | 0 |
| src/backend/storage/ipc/procsignal.c | 0 | 0 |
| src/backend/storage/ipc/standby.c | 0 | 0 |
| src/backend/storage/lmgr/proc.c | 0 | 0 |
| src/backend/tcop/postgres.c | 0 | 0 |
| src/include/access/xact.h | 0 | 0 |
| src/include/storage/procarray.h | 0 | 0 |
| src/include/storage/proc.h | 0 | 0 |
| src/include/storage/procsignal.h | 0 | 0 |
| src/include/storage/standby.h | 0 | 0 |
| src/include/tcop/tcopprot.h | 0 | 0 |
| src/include/utils/errcodes.h | 0 | 0 |
| src/pl/plpgsql/src/pl_exec.c | 0 | 0 |
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 90e7b4a..a6e2405 100644
*** a/src/backend/access/transam/xact.c
--- b/src/backend/access/transam/xact.c
*************** AbortOutOfAnyTransaction(void)
*** 3712,3717 ****
--- 3712,3761 ----
}
/*
+ * AbortTransactionAndAnySubtransactions
+ *
+ * Similar to AbortCurrentTransaction but if any subtransactions
+ * in progress we abort them *and* all of their parents. So this is
+ * used when the caller wishes to make the abort untrappable by the user.
+ * After this has run IsAbortedTransactionBlockState() will be true.
+ */
+ void
+ AbortTransactionAndAnySubtransactions(void)
+ {
+ while(true){
+ TransactionState s = CurrentTransactionState;
+
+ switch (s->blockState)
+ {
+ case TBLOCK_DEFAULT:
+ case TBLOCK_STARTED:
+ case TBLOCK_BEGIN:
+ case TBLOCK_INPROGRESS:
+ case TBLOCK_END:
+ case TBLOCK_ABORT:
+ case TBLOCK_SUBABORT:
+ case TBLOCK_ABORT_END:
+ case TBLOCK_ABORT_PENDING:
+ case TBLOCK_PREPARE:
+ case TBLOCK_SUBABORT_END:
+ case TBLOCK_SUBABORT_RESTART:
+ AbortCurrentTransaction();
+ return;
+ break;
+
+ case TBLOCK_SUBINPROGRESS:
+ case TBLOCK_SUBBEGIN:
+ case TBLOCK_SUBEND:
+ case TBLOCK_SUBABORT_PENDING:
+ case TBLOCK_SUBRESTART:
+ AbortSubTransaction();
+ CleanupSubTransaction();
+ break;
+ }
+ }
+ }
+
+ /*
* IsTransactionBlock --- are we within a transaction block?
*/
bool
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 9cb28f7..d8ea470 100644
*** a/src/backend/storage/ipc/procarray.c
--- b/src/backend/storage/ipc/procarray.c
***************
*** 51,56 ****
--- 51,57 ----
#include "access/xact.h"
#include "access/twophase.h"
#include "miscadmin.h"
+ #include "storage/lmgr.h"
#include "storage/procarray.h"
#include "storage/standby.h"
#include "utils/builtins.h"
*************** ProcArrayClearTransaction(PGPROC *proc)
*** 377,383 ****
proc->xid = InvalidTransactionId;
proc->lxid = InvalidLocalTransactionId;
proc->xmin = InvalidTransactionId;
- proc->recoveryConflictMode = 0;
/* redundant, but just in case */
proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
--- 378,383 ----
*************** GetConflictingVirtualXIDs(TransactionId
*** 1665,1672 ****
if (proc->pid == 0)
continue;
! if (skipExistingConflicts && proc->recoveryConflictMode > 0)
! continue;
if (!OidIsValid(dbOid) ||
proc->databaseId == dbOid)
--- 1665,1677 ----
if (proc->pid == 0)
continue;
! /*
! * XXX: doesn't skipExistingConflicts have the danger of
! * shadowing an FATAL error?
! * I.e. ERROR is signalled first and then FATAL is signalled...
! * if (skipExistingConflicts && ...)
! * continue;
! */
if (!OidIsValid(dbOid) ||
proc->databaseId == dbOid)
*************** GetConflictingVirtualXIDs(TransactionId
*** 1704,1717 ****
* Returns pid of the process signaled, or 0 if not found.
*/
pid_t
! CancelVirtualTransaction(VirtualTransactionId vxid, int cancel_mode)
{
ProcArrayStruct *arrayP = procArray;
int index;
pid_t pid = 0;
! LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
{
VirtualTransactionId procvxid;
--- 1709,1728 ----
* Returns pid of the process signaled, or 0 if not found.
*/
pid_t
! CancelVirtualTransaction(VirtualTransactionId vxid,
! recovery_conflict_mode cancel_mode)
{
ProcArrayStruct *arrayP = procArray;
int index;
pid_t pid = 0;
! ProcSignalReason sigmode = PROCSIG_CONFLICT_ERROR_INTERRUPT;
! size_t num_locks = 0;
+ /*
+ * Find pid
+ */
+ LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
{
VirtualTransactionId procvxid;
*************** CancelVirtualTransaction(VirtualTransact
*** 1722,1747 ****
if (procvxid.backendId == vxid.backendId &&
procvxid.localTransactionId == vxid.localTransactionId)
{
- /*
- * Issue orders for the proc to read next time it receives SIGINT
- */
- if (proc->recoveryConflictMode < cancel_mode)
- proc->recoveryConflictMode = cancel_mode;
-
pid = proc->pid;
break;
}
}
-
LWLockRelease(ProcArrayLock);
! if (pid != 0)
! {
/*
! * Kill the pid if it's still here. If not, that's what we wanted
! * so ignore any errors.
*/
! kill(pid, SIGINT);
}
return pid;
--- 1733,1782 ----
if (procvxid.backendId == vxid.backendId &&
procvxid.localTransactionId == vxid.localTransactionId)
{
pid = proc->pid;
break;
}
}
LWLockRelease(ProcArrayLock);
! if(!pid)
! return pid;
!
! /*
! * Kill the transaction. We retry 50 times - if we didnt finish
! * the transaction till then, we start using FATAL.
! *
! * We dont need to lock while signalling as the messages contain
! * only sig_atomic_t wide contents which itself identify the
! * operation completely. I.e. it contains the local transaction
! * id, so it can't abort the wrong transaction.
! */
! if (cancel_mode == CONFLICT_MODE_FATAL)
! sigmode = PROCSIG_CONFLICT_FATAL_INTERRUPT;
!
! while(!ConditionalVirtualXactLockTableWait(vxid)){
! if(sigmode == PROCSIG_CONFLICT_ERROR_INTERRUPT && num_locks++ > 50){
! sigmode = PROCSIG_CONFLICT_FATAL_INTERRUPT;
!
! elog(trace_recovery(DEBUG1),
! "recovery tried to cancel virtual transaction %u/%u pid %ld. No success so far. Using FATAL",
! vxid.backendId,
! vxid.localTransactionId,
! (long) pid);
! }
!
! if(sigmode == PROCSIG_CONFLICT_FATAL_INTERRUPT){
! SendProcSignal(pid, sigmode, vxid.backendId, pid);
! }
! else{
! SendProcSignal(pid, sigmode, vxid.backendId, vxid.localTransactionId);
! }
!
/*
! * Wait awhile for it to die so that we avoid flooding an
! * unresponsive backend when system is heavily loaded.
*/
! pg_usleep(5000);
}
return pid;
*************** CancelDBBackends(Oid databaseid)
*** 1842,1851 ****
{
volatile PGPROC *proc = arrayP->procs[index];
if (proc->databaseId == databaseid)
{
! proc->recoveryConflictMode = CONFLICT_MODE_FATAL;
! kill(proc->pid, SIGINT);
}
}
--- 1877,1887 ----
{
volatile PGPROC *proc = arrayP->procs[index];
+
if (proc->databaseId == databaseid)
{
! SendProcSignal(proc->pid, PROCSIG_CONFLICT_FATAL_INTERRUPT,
! InvalidBackendId, true);
}
}
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 2892727..1fbb886 100644
*** a/src/backend/storage/ipc/procsignal.c
--- b/src/backend/storage/ipc/procsignal.c
***************
*** 24,29 ****
--- 24,31 ----
#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "storage/sinval.h"
+ #include "storage/standby.h"
+ #include "tcop/tcopprot.h"
/*
*************** void
*** 255,260 ****
--- 257,276 ----
procsignal_sigusr1_handler(SIGNAL_ARGS)
{
int save_errno = errno;
+ sig_atomic_t signal_data;
+
+ /*
+ * We check the possible signals in decreasing order of
+ * importance. For example if were FATALing the backend there is
+ * no point in sending out NOTIFYs before that.
+ * XXX: Possibly we should not calll the other handlers at all when
+ * receiving FATAL
+ */
+ if ((signal_data = CheckProcSignal(PROCSIG_CONFLICT_FATAL_INTERRUPT)))
+ RecoveryConflictInterrupt(CONFLICT_MODE_FATAL, signal_data);
+
+ if ((signal_data = CheckProcSignal(PROCSIG_CONFLICT_ERROR_INTERRUPT)))
+ RecoveryConflictInterrupt(CONFLICT_MODE_ERROR, signal_data);
if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT))
HandleCatchupInterrupt();
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 227b9cc..614914d 100644
*** a/src/backend/storage/ipc/standby.c
--- b/src/backend/storage/ipc/standby.c
*************** WaitExceedsMaxStandbyDelay(void)
*** 164,170 ****
*/
void
ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
! char *reason, int cancel_mode)
{
char waitactivitymsg[100];
--- 164,170 ----
*/
void
ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
! char *reason, recovery_conflict_mode cancel_mode)
{
char waitactivitymsg[100];
*************** ResolveRecoveryConflictWithVirtualXIDs(V
*** 234,246 ****
{
case CONFLICT_MODE_FATAL:
elog(trace_recovery(DEBUG1),
! "recovery disconnects session with pid %ld because of conflict with %s",
(long) pid,
reason);
break;
case CONFLICT_MODE_ERROR:
elog(trace_recovery(DEBUG1),
! "recovery cancels virtual transaction %u/%u pid %ld because of conflict with %s",
waitlist->backendId,
waitlist->localTransactionId,
(long) pid,
--- 234,246 ----
{
case CONFLICT_MODE_FATAL:
elog(trace_recovery(DEBUG1),
! "recovery disconnected session with pid %ld because of conflict with %s",
(long) pid,
reason);
break;
case CONFLICT_MODE_ERROR:
elog(trace_recovery(DEBUG1),
! "recovery canceled virtual transaction %u/%u pid %ld because of conflict with %s",
waitlist->backendId,
waitlist->localTransactionId,
(long) pid,
*************** ResolveRecoveryConflictWithVirtualXIDs(V
*** 250,261 ****
/* No conflict pending, so fall through */
break;
}
-
- /*
- * Wait awhile for it to die so that we avoid flooding an
- * unresponsive backend when system is heavily loaded.
- */
- pg_usleep(5000);
}
}
}
--- 250,255 ----
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 7daa5e8..3d4bd10 100644
*** a/src/backend/storage/lmgr/proc.c
--- b/src/backend/storage/lmgr/proc.c
*************** InitProcess(void)
*** 318,324 ****
MyProc->waitProcLock = NULL;
for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
SHMQueueInit(&(MyProc->myProcLocks[i]));
- MyProc->recoveryConflictMode = 0;
/*
* We might be reusing a semaphore that belonged to a failed process. So
--- 318,323 ----
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index be20bf3..919b1a3 100644
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
*************** static int UseNewLine = 1; /* Use newli
*** 172,177 ****
--- 172,178 ----
static int UseNewLine = 0; /* Use EOF as query delimiters */
#endif /* TCOP_DONTUSENEWLINE */
+ static LocalTransactionId CanceledLocalTransaction = InvalidLocalTransactionId;
/* ----------------------------------------------------------------
* decls for routines only used in this file
*************** StatementCancelHandler(SIGNAL_ARGS)
*** 2642,2648 ****
* 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 */
--- 2643,2649 ----
* service the interrupt immediately
*/
if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
! CritSectionCount == 0 && !DoingCommandRead)
{
/* bump holdoff count to make ProcessInterrupts() a no-op */
/* until we are done getting ready for it */
*************** ProcessInterrupts(void)
*** 2711,2819 ****
(errcode(ERRCODE_ADMIN_SHUTDOWN),
errmsg("terminating connection due to administrator command")));
}
if (QueryCancelPending)
{
QueryCancelPending = false;
if (ClientAuthInProgress)
- {
- ImmediateInterruptOK = false; /* not idle anymore */
- DisableNotifyInterrupt();
- DisableCatchupInterrupt();
- /* As in quickdie, don't risk sending to client during auth */
- if (whereToSendOutput == DestRemote)
- whereToSendOutput = DestNone;
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling authentication due to timeout")));
! }
! if (cancel_from_timeout)
! {
! ImmediateInterruptOK = false; /* not idle anymore */
! DisableNotifyInterrupt();
! DisableCatchupInterrupt();
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to statement timeout")));
! }
! if (IsAutoVacuumWorkerProcess())
! {
! ImmediateInterruptOK = false; /* not idle anymore */
! DisableNotifyInterrupt();
! DisableCatchupInterrupt();
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling autovacuum task")));
! }
! {
! 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:
- 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;
! }
}
/*
! * If we are reading a command from the client, just ignore the
! * cancel request --- sending an extra error message won't
! * accomplish anything. Otherwise, go ahead and throw the error.
*/
! if (!DoingCommandRead)
{
! ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
! ereport(ERROR,
! (errcode(ERRCODE_QUERY_CANCELED),
! errmsg("canceling statement due to user request")));
}
}
! /* If we get here, do nothing (probably, QueryCancelPending was reset) */
}
--- 2712,2818 ----
(errcode(ERRCODE_ADMIN_SHUTDOWN),
errmsg("terminating connection due to administrator command")));
}
+
if (QueryCancelPending)
{
QueryCancelPending = false;
+ ImmediateInterruptOK = false; /* not idle anymore */
+ DisableNotifyInterrupt();
+ DisableCatchupInterrupt();
+ /* As in quickdie, don't risk sending to client during auth */
+ if (ClientAuthInProgress && whereToSendOutput == DestRemote)
+ whereToSendOutput = DestNone;
if (ClientAuthInProgress)
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling authentication due to timeout")));
! else if (cancel_from_timeout)
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to statement timeout")));
! else if (IsAutoVacuumWorkerProcess())
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling autovacuum task")));
! else
! ereport(ERROR,
! (errcode(ERRCODE_QUERY_CANCELED),
! errmsg("canceling statement due to user request")));
! }
!
+ {
+ LocalTransactionId current_canceled = CanceledLocalTransaction;
+
+ /*
+ * To avoid cancelling the wrong transaction because the
+ * normal transaction already finished and generated a new
+ * error before the signal handler or ProcessInterrupts has
+ * run we recheck the current LocalTransactionId.
+ */
+
+ if(CanceledLocalTransaction != InvalidLocalTransactionId)
+ {
+ ImmediateInterruptOK = false; /* not idle anymore */
/*
! * Cancel the transaction whether or not it was idle, so don't mention
! * the word idle in the message.
*/
! if (current_canceled == MyProc->lxid){
! ereport(ERROR,
! (errcode(ERRCODE_QUERY_CANCELED_HS),
! errmsg("canceling transaction due to conflict with recovery")));
}
+ }
+ }
+ }
! void
! RecoveryConflictInterrupt(recovery_conflict_mode conflict_mode, sig_atomic_t signal_data)
! {
! int save_errno = errno;
! /*
! * Don't joggle the elbow of proc_exit
! */
! if (!proc_exit_inprogress)
! {
! switch (conflict_mode)
! {
! case CONFLICT_MODE_FATAL:
! ProcDiePending = true;
! break;
!
! case CONFLICT_MODE_ERROR:
! CanceledLocalTransaction = (LocalTransactionId)signal_data;
! break;
!
! default:
! elog(ERROR, "Unknown conflict mode");
}
+ InterruptPending = true;
+
/*
! * If it's safe to interrupt, and we're waiting for input or a lock,
! * service the interrupt immediately. Same as in die()
*/
! 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;
}
*************** PostgresMain(int argc, char *argv[], con
*** 3560,3568 ****
debug_query_string = NULL;
/*
! * Abort the current transaction in order to recover.
*/
! AbortCurrentTransaction();
/*
* Now return to normal top-level context and clear ErrorContext for
--- 3559,3593 ----
debug_query_string = NULL;
/*
! * Abort the current transaction in order to recover. If were
! * in HS this is the only safe point where we can abort not
! * only the current transaction but also all transaction above
! * the current as there exists no higher point to jump to and
! * thus were not playing around with somebodys execution context.
! *
! * To avoid cancelling the wrong transaction because the
! * normal transaction already finished and generated a new
! * error before the signal handler has run we recheck the
! * current LocalTransactionId.
! *
! * XXX: Possibly this should use the new error code for a
! * transaction canceled by HS instead.
*/
! {
! LocalTransactionId current_canceled = CanceledLocalTransaction;
! if(current_canceled != InvalidLocalTransactionId &&
! current_canceled == MyProc->lxid)
! AbortTransactionAndAnySubtransactions();
! else
! AbortCurrentTransaction();
! }
!
! /*
! * We cannot overwrite a newly canceled LocalTransactionId
! * here because we would have to leave that block to start a
! * new transaction.
! */
! CanceledLocalTransaction = InvalidLocalTransactionId;
/*
* Now return to normal top-level context and clear ErrorContext for
*************** PostgresMain(int argc, char *argv[], con
*** 3598,3603 ****
--- 3623,3630 ----
for (;;)
{
+ CanceledLocalTransaction = InvalidLocalTransactionId;
+
/*
* At top of loop, reset extended-query-message flag, so that any
* errors encountered in "idle" state don't provoke skip.
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 4c67be5..47b325a 100644
*** a/src/include/access/xact.h
--- b/src/include/access/xact.h
*************** extern bool IsTransactionBlock(void);
*** 204,209 ****
--- 204,210 ----
extern bool IsTransactionOrTransactionBlock(void);
extern char TransactionBlockStatusCode(void);
extern void AbortOutOfAnyTransaction(void);
+ extern void AbortTransactionAndAnySubtransactions(void);
extern void PreventTransactionChain(bool isTopLevel, const char *stmtType);
extern void RequireTransactionChain(bool isTopLevel, const char *stmtType);
extern bool IsInTransactionChain(bool isTopLevel);
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index de0df3b..46c74ef 100644
*** a/src/include/storage/proc.h
--- b/src/include/storage/proc.h
*************** struct PGPROC
*** 95,107 ****
uint8 vacuumFlags; /* vacuum-related flags, see above */
- /*
- * While in hot standby mode, setting recoveryConflictMode instructs
- * the backend to commit suicide. Possible values are the same as those
- * passed to ResolveRecoveryConflictWithVirtualXIDs().
- */
- int recoveryConflictMode;
-
/* Info about LWLock the process is currently waiting for, if any. */
bool lwWaiting; /* true if waiting for an LW lock */
bool lwExclusive; /* true if waiting for exclusive access */
--- 95,100 ----
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index 314491d..868058b 100644
*** a/src/include/storage/procarray.h
--- b/src/include/storage/procarray.h
***************
*** 17,22 ****
--- 17,23 ----
#include "storage/lock.h"
#include "storage/standby.h"
#include "utils/snapshot.h"
+ #include "storage/procsignal.h"
extern Size ProcArrayShmemSize(void);
*************** extern VirtualTransactionId *GetCurrentV
*** 59,65 ****
extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin,
Oid dbOid, bool skipExistingConflicts);
extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid,
! int cancel_mode);
extern int CountActiveBackends(void);
extern int CountDBBackends(Oid databaseid);
--- 60,66 ----
extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin,
Oid dbOid, bool skipExistingConflicts);
extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid,
! recovery_conflict_mode cancel_mode);
extern int CountActiveBackends(void);
extern int CountDBBackends(Oid databaseid);
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 3b0d56d..795346c 100644
*** a/src/include/storage/procsignal.h
--- b/src/include/storage/procsignal.h
***************
*** 30,35 ****
--- 30,38 ----
*/
typedef enum
{
+ PROCSIG_CONFLICT_FATAL_INTERRUPT, /* recovery conflict fatal */
+ PROCSIG_CONFLICT_ERROR_INTERRUPT, /* recovery conflict error */
+
PROCSIG_CATCHUP_INTERRUPT, /* sinval catchup interrupt */
PROCSIG_NOTIFY_INTERRUPT, /* listen/notify interrupt */
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
index 7a1c41f..ac950b7 100644
*** a/src/include/storage/standby.h
--- b/src/include/storage/standby.h
***************
*** 20,31 ****
extern int vacuum_defer_cleanup_age;
/* cancel modes for ResolveRecoveryConflictWithVirtualXIDs */
! #define CONFLICT_MODE_NOT_SET 0
! #define CONFLICT_MODE_ERROR 1 /* Conflict can be resolved by canceling query */
! #define CONFLICT_MODE_FATAL 2 /* Conflict can only be resolved by disconnecting session */
extern void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
! char *reason, int cancel_mode);
extern void InitRecoveryTransactionEnvironment(void);
extern void ShutdownRecoveryTransactionEnvironment(void);
--- 20,33 ----
extern int vacuum_defer_cleanup_age;
/* cancel modes for ResolveRecoveryConflictWithVirtualXIDs */
! typedef enum {
! CONFLICT_MODE_NOT_SET, /* No conflict */
! CONFLICT_MODE_FATAL, /* Conflict can only be resolved by disconnecting session */
! CONFLICT_MODE_ERROR /* Conflict can be resolved by canceling query */
! } recovery_conflict_mode;
extern void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
! char *reason, recovery_conflict_mode cancel_mode);
extern void InitRecoveryTransactionEnvironment(void);
extern void ShutdownRecoveryTransactionEnvironment(void);
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 1298f7d..b901b6a 100644
*** a/src/include/tcop/tcopprot.h
--- b/src/include/tcop/tcopprot.h
***************
*** 19,27 ****
--- 19,30 ----
#ifndef TCOPPROT_H
#define TCOPPROT_H
+ #include <signal.h>
#include "executor/execdesc.h"
#include "nodes/parsenodes.h"
#include "utils/guc.h"
+ #include "storage/standby.h"
+
/* Required daylight between max_stack_depth and the kernel limit, in bytes */
*************** extern bool assign_max_stack_depth(int n
*** 63,68 ****
--- 66,73 ----
extern void die(SIGNAL_ARGS);
extern void quickdie(SIGNAL_ARGS);
extern void StatementCancelHandler(SIGNAL_ARGS);
+
+ extern void RecoveryConflictInterrupt(recovery_conflict_mode conflict_mode, sig_atomic_t signal_data);
extern void FloatExceptionHandler(SIGNAL_ARGS);
extern void prepare_for_client_read(void);
extern void client_read_ended(void);
diff --git a/src/include/utils/errcodes.h b/src/include/utils/errcodes.h
index 52c09ca..279f0e4 100644
*** a/src/include/utils/errcodes.h
--- b/src/include/utils/errcodes.h
***************
*** 328,333 ****
--- 328,334 ----
/* Class 57 - Operator Intervention (class borrowed from DB2) */
#define ERRCODE_OPERATOR_INTERVENTION MAKE_SQLSTATE('5','7', '0','0','0')
#define ERRCODE_QUERY_CANCELED MAKE_SQLSTATE('5','7', '0','1','4')
+ #define ERRCODE_QUERY_CANCELED_HS MAKE_SQLSTATE('5','7', '0','1','5')
#define ERRCODE_ADMIN_SHUTDOWN MAKE_SQLSTATE('5','7', 'P','0','1')
#define ERRCODE_CRASH_SHUTDOWN MAKE_SQLSTATE('5','7', 'P','0','2')
#define ERRCODE_CANNOT_CONNECT_NOW MAKE_SQLSTATE('5','7', 'P','0','3')
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index b9ca54f..810ef8c 100644
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** exception_matches_conditions(ErrorData *
*** 897,903 ****
* OTHERS matches everything *except* query-canceled; if you're
* foolish enough, you can match that explicitly.
*/
! if (sqlerrstate == 0)
{
if (edata->sqlerrcode != ERRCODE_QUERY_CANCELED)
return true;
--- 897,905 ----
* OTHERS matches everything *except* query-canceled; if you're
* foolish enough, you can match that explicitly.
*/
! if (edata->sqlerrcode == ERRCODE_QUERY_CANCELED_HS)
! ;
! else if (sqlerrstate == 0)
{
if (edata->sqlerrcode != ERRCODE_QUERY_CANCELED)
return true;