From 2297775aedbc5f2e47083c4dbebdb9f74bdd8bf1 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Wed, 27 Jan 2021 12:28:37 -0500
Subject: [PATCH v2 3/3] In StartupCLOG(), correct an off-by-one error.

nextXid represents the first XID that hasn't yet been allocated, not
the last one that gets allocated. So, we should use the previous XID
to determine the last CLOG page that ought to exist.

As far as we currently know, this mistake doesn't have any adverse
consequences at present, but it seems better to make the logic
more correct.

Patch by me, reviewed by Heikki Linnakangas.

Discussion: http://postgr.es/m/CA+TgmoZYig9+AQodhF5sRXuKkJ=RgFDugLr3XX_dz_F-p=TwTg@mail.gmail.com
---
 src/backend/access/transam/clog.c | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index 6fa4713fb4..560eb48e8f 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -742,6 +742,22 @@ ZeroCLOGPage(int pageno, bool writeXlog)
 	return slotno;
 }
 
+/*
+ * Based on ShmemVariableCache->nextXid, compute the latest CLOG page that
+ * is expected to exist.
+ */
+static int
+FullTransactionIdToLatestPageNumber(FullTransactionId nextXid)
+{
+	/*
+	 * nextXid is the next XID that will be used, but we want to set
+	 * latest_page_number according to the last XID that's already been used.
+	 * So retreat by one. See also GetNewTransactionId().
+	 */
+	FullTransactionIdRetreat(&nextXid);
+	return TransactionIdToPage(XidFromFullTransactionId(nextXid));
+}
+
 /*
  * This must be called ONCE during postmaster or standalone-backend startup,
  * after StartupXLOG has initialized ShmemVariableCache->nextXid.
@@ -749,16 +765,12 @@ ZeroCLOGPage(int pageno, bool writeXlog)
 void
 StartupCLOG(void)
 {
-	TransactionId xid = XidFromFullTransactionId(ShmemVariableCache->nextXid);
-	int			pageno = TransactionIdToPage(xid);
+	int			pageno;
 
+	/* Initialize our idea of the latest page number. */
+	pageno = FullTransactionIdToLatestPageNumber(ShmemVariableCache->nextXid);
 	LWLockAcquire(XactSLRULock, LW_EXCLUSIVE);
-
-	/*
-	 * Initialize our idea of the latest page number.
-	 */
 	XactCtl->shared->latest_page_number = pageno;
-
 	LWLockRelease(XactSLRULock);
 }
 
-- 
2.24.3 (Apple Git-128)

