From d698bf83d16416cf7222c9e825004bdd6b12ad39 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 3 Nov 2008 19:24:03 +0000
Subject: [PATCH] Fix TransactionIdSetStatusBit so that it doesn't try to
 change a transaction from COMMITTED to SUBCOMMITTED during recovery.  This
 wasn't previously possible, but it is now due to the recent changes on clog
 commit protocol for subtransactions.

Simon Riggs
---
 src/backend/access/transam/clog.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index fcff3ea3cfd..3d62417387b 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -26,7 +26,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.48 2008/10/20 19:18:18 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.49 2008/11/03 19:24:03 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -321,13 +321,29 @@ TransactionIdSetStatusBit(TransactionId xid, XidStatus status, XLogRecPtr lsn, i
 	int			bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
 	char	   *byteptr;
 	char		byteval;
+	char		curval;
 
 	byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
+	curval = (*byteptr >> shift) & CLOG_XACT_BITMASK;
 
-	/* Current state should be 0, subcommitted or target state */
-	Assert(((*byteptr >> bshift) & CLOG_XACT_BITMASK) == 0 ||
-		   ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == TRANSACTION_STATUS_SUB_COMMITTED ||
-		   ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == status);
+	/*
+	 * When replaying transactions during recovery we still need to perform
+	 * the two phases of subcommit and then commit. However, some transactions
+	 * are already correctly marked, so we just treat those as a no-op which
+	 * allows us to keep the following Assert as restrictive as possible.
+	 */
+	if (InRecovery && status == TRANSACTION_STATUS_SUB_COMMITTED &&
+		curval == TRANSACTION_STATUS_COMMITTED)
+		return;
+
+	/* 
+	 * Current state change should be from 0 or subcommitted to target state
+	 * or we should already be there when replaying changes during recovery.
+	 */
+	Assert(curval == 0 ||
+		   (curval == TRANSACTION_STATUS_SUB_COMMITTED &&
+			status != TRANSACTION_STATUS_IN_PROGRESS) ||
+		   curval == status);
 
 	/* note this assumes exclusive access to the clog page */
 	byteval = *byteptr;
-- 
GitLab