On Thu, 2010-01-07 at 12:14 -0500, Tom Lane wrote:
> Simon Riggs <si...@2ndquadrant.com> writes:
> > On Thu, 2010-01-07 at 14:45 +0100, Joachim Wieland wrote:
> >> @Simon: Is there a reason why you have not yet removed recoveryConflictMode
> >> from PGPROC?
> 
> > Unfortunately we still need a mechanism to mark which backends have been
> > cancelled already. Transaction state for virtual transactions isn't
> > visible on the procarray, so we need something there to indicate that a
> > backend has been sent a conflict. Otherwise we'd end up waiting for it
> > endlessly. The name will be changing though.
> 
> While we're discussing this: the current coding with
> AbortOutOfAnyTransaction within ProcessInterrupts is *utterly* unsafe.
> I realize that's just a toy placeholder, but getting rid of it has to be
> on the list of stop-ship items.  Right at the moment I'd prefer to see
> CONFLICT_MODE_ERROR always turned into CONFLICT_MODE_FATAL than to
> imagine this is going to work.

Hmmm. Can you check my current attempt? This may suffer this problem.

If, so can you explain a little more for me? Thanks.

-- 
 Simon Riggs           www.2ndQuadrant.com
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index ea40420..e05792e 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -313,8 +313,7 @@ IsTransactionState(void)
 /*
  *	IsAbortedTransactionBlockState
  *
- *	This returns true if we are currently running a query
- *	within an aborted transaction block.
+ *	This returns true if we are within an aborted transaction block.
  */
 bool
 IsAbortedTransactionBlockState(void)
@@ -2692,6 +2691,48 @@ AbortCurrentTransaction(void)
 }
 
 /*
+ *	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)
+{
+	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();
+			break;
+
+		case TBLOCK_SUBINPROGRESS:
+		case TBLOCK_SUBBEGIN:
+		case TBLOCK_SUBEND:
+		case TBLOCK_SUBABORT_PENDING:
+		case TBLOCK_SUBRESTART:
+			AbortSubTransaction();
+			CleanupSubTransaction();
+			AbortTransactionAndAnySubtransactions();
+			break;
+	}
+}
+
+/*
  *	PreventTransactionChain
  *
  *	This routine is to be called by statements that must not run inside
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 85f14f6..e2e64dd 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -324,6 +324,7 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 		/* must be cleared with xid/xmin: */
 		proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
 		proc->inCommit = false; /* be sure this is cleared in abort */
+		proc->recoveryConflictPending = false;
 
 		/* Clear the subtransaction-XID cache too while holding the lock */
 		proc->subxids.nxids = 0;
@@ -350,6 +351,7 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 		/* must be cleared with xid/xmin: */
 		proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
 		proc->inCommit = false; /* be sure this is cleared in abort */
+		proc->recoveryConflictPending = false;
 
 		Assert(proc->subxids.nxids == 0);
 		Assert(proc->subxids.overflowed == false);
@@ -377,7 +379,7 @@ ProcArrayClearTransaction(PGPROC *proc)
 	proc->xid = InvalidTransactionId;
 	proc->lxid = InvalidLocalTransactionId;
 	proc->xmin = InvalidTransactionId;
-	proc->recoveryConflictMode = 0;
+	proc->recoveryConflictPending = false;
 
 	/* redundant, but just in case */
 	proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
@@ -1665,7 +1667,7 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid,
 		if (proc->pid == 0)
 			continue;
 
-		if (skipExistingConflicts && proc->recoveryConflictMode > 0)
+		if (skipExistingConflicts && proc->recoveryConflictPending)
 			continue;
 
 		if (!OidIsValid(dbOid) ||
@@ -1704,7 +1706,7 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid,
  * Returns pid of the process signaled, or 0 if not found.
  */
 pid_t
-CancelVirtualTransaction(VirtualTransactionId vxid, int cancel_mode)
+CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode)
 {
 	ProcArrayStruct *arrayP = procArray;
 	int			index;
@@ -1722,28 +1724,22 @@ CancelVirtualTransaction(VirtualTransactionId vxid, int cancel_mode)
 		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;
-
+			proc->recoveryConflictPending = true;
 			pid = proc->pid;
+			if (pid != 0)
+			{
+				/*
+				 * Kill the pid if it's still here. If not, that's what we wanted
+				 * so ignore any errors.
+				 */
+				(void) SendProcSignal(pid, sigmode, vxid.backendId);
+			}
 			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;
 }
 
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 480af00..c4007fe 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -24,6 +24,8 @@
 #include "storage/procsignal.h"
 #include "storage/shmem.h"
 #include "storage/sinval.h"
+#include "storage/standby.h"
+#include "tcop/tcopprot.h"
 
 
 /*
@@ -258,5 +260,11 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT))
 		HandleNotifyInterrupt();
 
+	if (CheckProcSignal(PROCSIG_CONFLICT_ERROR_INTERRUPT))
+		RecoveryConflictInterrupt(CONFLICT_MODE_ERROR);
+
+	if (CheckProcSignal(PROCSIG_CONFLICT_FATAL_INTERRUPT))
+		RecoveryConflictInterrupt(CONFLICT_MODE_FATAL);
+
 	errno = save_errno;
 }
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 0b4e022..0ea7a32 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -159,8 +159,6 @@ WaitExceedsMaxStandbyDelay(void)
  * recovery processing. Judgement has already been passed on it within
  * a specific rmgr. Here we just issue the orders to the procs. The procs
  * then throw the required error as instructed.
- *
- * We may ask for a specific cancel_mode, typically ERROR or FATAL.
  */
 void
 ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
@@ -218,12 +216,16 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
 			if (WaitExceedsMaxStandbyDelay())
 			{
 				pid_t pid;
+				ProcSignalReason	sigmode = PROCSIG_CONFLICT_ERROR_INTERRUPT;
+
+				if (cancel_mode == CONFLICT_MODE_FATAL)
+					sigmode = PROCSIG_CONFLICT_FATAL_INTERRUPT;
 
 				/*
 				 * Now find out who to throw out of the balloon.
 				 */
 				Assert(VirtualTransactionIdIsValid(*waitlist));
-				pid = CancelVirtualTransaction(*waitlist, cancel_mode);
+				pid = CancelVirtualTransaction(*waitlist, sigmode);
 
 				if (pid != 0)
 				{
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 352ff08..a708369 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -318,7 +318,7 @@ InitProcess(void)
 	MyProc->waitProcLock = NULL;
 	for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
 		SHMQueueInit(&(MyProc->myProcLocks[i]));
-	MyProc->recoveryConflictMode = 0;
+	MyProc->recoveryConflictPending = false;
 
 	/*
 	 * We might be reusing a semaphore that belonged to a failed process. So
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3bddf49..3ebde79 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -172,6 +172,9 @@ 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; 
+static bool TransactionCancelPending = false;
 
 /* ----------------------------------------------------------------
  *		decls for routines only used in this file
@@ -185,6 +188,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);
@@ -945,7 +949,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();
@@ -1254,7 +1259,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.
@@ -1534,7 +1540,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
@@ -1975,7 +1982,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();
@@ -2236,6 +2244,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
@@ -2292,7 +2314,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... */
@@ -2372,7 +2395,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... */
@@ -2643,9 +2667,59 @@ StatementCancelHandler(SIGNAL_ARGS)
 		 * If it's safe to interrupt, and we're waiting for a lock, service
 		 * the interrupt immediately.  No point in interrupting if we're
 		 * waiting for input, however.
+		*/
+		if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
+				CritSectionCount == 0 && !DoingCommandRead)
+		{
+			/* 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;
+}
+
+void
+RecoveryConflictInterrupt(int conflict_mode)
+{
+	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:
+				if (IsAbortedTransactionBlockState())
+					return;
+				TransactionCancelPending = true;
+				break;
+
+			default:
+				elog(ERROR, "Unknown conflict mode");
+		}
+
+		RecoveryConflictPending = true;
+		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 (InterruptHoldoffCount == 0 && CritSectionCount == 0 &&
-			(DoingCommandRead || ImmediateInterruptOK))
+		if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
+			CritSectionCount == 0)
 		{
 			/* bump holdoff count to make ProcessInterrupts() a no-op */
 			/* until we are done getting ready for it */
@@ -2709,6 +2783,10 @@ ProcessInterrupts(void)
 			ereport(FATAL,
 					(errcode(ERRCODE_ADMIN_SHUTDOWN),
 					 errmsg("terminating autovacuum process due to administrator command")));
+		else if (RecoveryConflictPending)
+			ereport(NOTICE,
+					(errcode(ERRCODE_ADMIN_SHUTDOWN),
+					 errmsg("terminating connection due to conflict with recovery")));
 		else
 			ereport(FATAL,
 					(errcode(ERRCODE_ADMIN_SHUTDOWN),
@@ -2735,59 +2813,42 @@ ProcessInterrupts(void)
 			ereport(ERROR,
 					(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;
-			}
-
+		else 
 			ereport(ERROR,
 					(errcode(ERRCODE_QUERY_CANCELED),
 					 errmsg("canceling statement due to user request")));
-		}
+	}
+	if (TransactionCancelPending)
+	{
+		TransactionCancelPending = false;
+		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 (RecoveryConflictPending)
+			ereport(NOTICE,
+					(errcode(ERRCODE_QUERY_CANCELED),
+					 errmsg("canceling transaction due to conflict with recovery")));
+		else
+			ereport(NOTICE,
+					(errcode(ERRCODE_QUERY_CANCELED),
+					 errmsg("canceling transaction due to administrator request")));
+
+		/* 
+		 * Indicate transaction aborted by recovery so we can use the appropriate
+		 * error message when we enter the aborted state.
+		 */
+		AbortTransactionAndAnySubtransactions();
+		RecoveryConflictPending = false;
+
+		/*
+		 * Set the display correctly now, rather than wait for client wait loop
+		 * to cycle round and reset display at some later time.
+		 */
+		set_ps_display("idle in transaction (aborted)", false);
+		pgstat_report_activity("<IDLE> in transaction (aborted)");
 	}
 	/* If we get here, do nothing (probably, QueryCancelPending was reset) */
 }
@@ -3603,7 +3664,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");
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 6eee8ca..89481a3 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -204,6 +204,7 @@ extern bool IsTransactionBlock(void);
 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 2298bf2..8105d39 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -96,11 +96,11 @@ struct PGPROC
 	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().
+	 * While in hot standby mode, shows that a conflict signal has been sent
+	 * for the current transaction. Set/cleared while holding ProcArrayLock,
+	 * though not required. Accessed without lock, if needed.
 	 */
-	int			recoveryConflictMode;
+	bool		recoveryConflictPending;
 
 	/* Info about LWLock the process is currently waiting for, if any. */
 	bool		lwWaiting;		/* true if waiting for an LW lock */
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index 1cee639..ec358b1 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -15,6 +15,7 @@
 #define PROCARRAY_H
 
 #include "storage/lock.h"
+#include "storage/procsignal.h"
 #include "storage/standby.h"
 #include "utils/snapshot.h"
 
@@ -58,8 +59,7 @@ extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 					  int *nvxids);
 extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin,
 					Oid dbOid, bool skipExistingConflicts);
-extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid,
-						 int cancel_mode);
+extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode);
 
 extern int	CountActiveBackends(void);
 extern int	CountDBBackends(Oid databaseid);
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 7f3b65e..59a2f97 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -31,6 +31,8 @@ typedef enum
 {
 	PROCSIG_CATCHUP_INTERRUPT,	/* sinval catchup interrupt */
 	PROCSIG_NOTIFY_INTERRUPT,	/* listen/notify interrupt */
+	PROCSIG_CONFLICT_ERROR_INTERRUPT,	/* recovery conflict error */
+	PROCSIG_CONFLICT_FATAL_INTERRUPT,	/* recovery conflict fatal */
 
 	NUM_PROCSIGNALS				/* Must be last! */
 } ProcSignalReason;
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
index 38df3d7..d6a7f0c 100644
--- a/src/include/storage/standby.h
+++ b/src/include/storage/standby.h
@@ -26,6 +26,7 @@ extern int	vacuum_defer_cleanup_age;
 
 extern void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
 									   char *reason, int cancel_mode);
+extern void HandleConflictInterrupt(int conflict_mode);
 
 extern void InitRecoveryTransactionEnvironment(void);
 extern void ShutdownRecoveryTransactionEnvironment(void);
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index d7ede2b..fc5ab9e 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -63,6 +63,7 @@ extern bool assign_max_stack_depth(int newval, bool doit, GucSource source);
 extern void die(SIGNAL_ARGS);
 extern void quickdie(SIGNAL_ARGS);
 extern void StatementCancelHandler(SIGNAL_ARGS);
+extern void RecoveryConflictInterrupt(int conflict_mode);
 extern void FloatExceptionHandler(SIGNAL_ARGS);
 extern void prepare_for_client_read(void);
 extern void client_read_ended(void);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to