v2-0001-Fix-the-mxid-and-mxoff-wraparound-issues-in-pg_up.patch

text/x-patch

Filename: v2-0001-Fix-the-mxid-and-mxoff-wraparound-issues-in-pg_up.patch
Type: text/x-patch
Part: 0
Message: Re: mxid and mxoff wraparound issues in pg_upgrade

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: format-patch
Series: patch v2-0001
Subject: Fix the mxid and mxoff wraparound issues in pg_upgrade.
File+
src/bin/pg_resetwal/pg_resetwal.c 9 9
src/bin/pg_upgrade/pg_upgrade.c 10 1
From 924c63daf1ceb9d796e18567afa6904cbe672a00 Mon Sep 17 00:00:00 2001
From: Maxim Orlov <orlovmg@gmail.com>
Date: Fri, 20 Jun 2025 17:21:08 +0300
Subject: [PATCH v2 1/1] Fix the mxid and mxoff wraparound issues in
 pg_upgrade.

Per bags:
- BUG #18863: Multixact wraparound and pg_resetwal error "multitransaction ID (-m) must not be 0"
- BUG #18865: pg_resetwal error: multitransaction offset (-O) must not be -1
---
 src/bin/pg_resetwal/pg_resetwal.c | 18 +++++++++---------
 src/bin/pg_upgrade/pg_upgrade.c   | 11 ++++++++++-
 2 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index a89d72fc5cf..3d61f025c5e 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -70,8 +70,10 @@ static TransactionId set_xid = 0;
 static TransactionId set_oldest_commit_ts_xid = 0;
 static TransactionId set_newest_commit_ts_xid = 0;
 static Oid	set_oid = 0;
+static bool mxid_given = false;
 static MultiXactId set_mxid = 0;
-static MultiXactOffset set_mxoff = (MultiXactOffset) -1;
+static bool mxoff_given = false;
+static MultiXactOffset set_mxoff = 0;
 static TimeLineID minXlogTli = 0;
 static XLogSegNo minXlogSegNo = 0;
 static int	WalSegSz;
@@ -246,6 +248,7 @@ main(int argc, char *argv[])
 					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
+				mxid_given = true;
 
 				set_oldestmxid = strtoul(endptr + 1, &endptr2, 0);
 				if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
@@ -254,8 +257,6 @@ main(int argc, char *argv[])
 					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
-				if (set_mxid == 0)
-					pg_fatal("multitransaction ID (-m) must not be 0");
 
 				/*
 				 * XXX It'd be nice to have more sanity checks here, e.g. so
@@ -274,8 +275,7 @@ main(int argc, char *argv[])
 					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
-				if (set_mxoff == -1)
-					pg_fatal("multitransaction offset (-O) must not be -1");
+				mxoff_given = true;
 				break;
 
 			case 'l':
@@ -454,7 +454,7 @@ main(int argc, char *argv[])
 	if (set_oid != 0)
 		ControlFile.checkPointCopy.nextOid = set_oid;
 
-	if (set_mxid != 0)
+	if (mxid_given)
 	{
 		ControlFile.checkPointCopy.nextMulti = set_mxid;
 
@@ -464,7 +464,7 @@ main(int argc, char *argv[])
 		ControlFile.checkPointCopy.oldestMultiDB = InvalidOid;
 	}
 
-	if (set_mxoff != -1)
+	if (mxoff_given)
 		ControlFile.checkPointCopy.nextMultiOffset = set_mxoff;
 
 	if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID)
@@ -805,7 +805,7 @@ PrintNewControlValues(void)
 				 newXlogSegNo, WalSegSz);
 	printf(_("First log segment after reset:        %s\n"), fname);
 
-	if (set_mxid != 0)
+	if (mxid_given)
 	{
 		printf(_("NextMultiXactId:                      %u\n"),
 			   ControlFile.checkPointCopy.nextMulti);
@@ -815,7 +815,7 @@ PrintNewControlValues(void)
 			   ControlFile.checkPointCopy.oldestMultiDB);
 	}
 
-	if (set_mxoff != -1)
+	if (mxoff_given)
 	{
 		printf(_("NextMultiOffset:                      %u\n"),
 			   ControlFile.checkPointCopy.nextMultiOffset);
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 490e98fa26f..2a317e59108 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -816,6 +816,15 @@ copy_xact_xlog_xid(void)
 	if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER &&
 		new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
 	{
+		uint32	old_chkpnt_nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
+
+		/*
+		 * Beware of the possibility that chkpnt_nxtmulti is in the
+		 * wrapped-around state in old cluster.
+		 */
+		if (old_chkpnt_nxtmulti == 0)
+			old_chkpnt_nxtmulti = 1;	/* FirstMultiXactId */
+
 		copy_subdir_files("pg_multixact/offsets", "pg_multixact/offsets");
 		copy_subdir_files("pg_multixact/members", "pg_multixact/members");
 
@@ -829,7 +838,7 @@ copy_xact_xlog_xid(void)
 				  "\"%s/pg_resetwal\" -O %u -m %u,%u \"%s\"",
 				  new_cluster.bindir,
 				  old_cluster.controldata.chkpnt_nxtmxoff,
-				  old_cluster.controldata.chkpnt_nxtmulti,
+				  old_chkpnt_nxtmulti,
 				  old_cluster.controldata.chkpnt_oldstMulti,
 				  new_cluster.pgdata);
 		check_ok();
-- 
2.47.3