From 05360e06c2d4a01b8d9eed6df43faf95feb06cef Mon Sep 17 00:00:00 2001 From: Zsolt Parragi Date: Thu, 23 Jul 2026 07:25:47 +0000 Subject: [PATCH] pg_resetwal: do not allow zero next multixact offset Offset 0 is the "invalid" marker in pg_multixact/offsets since offsets went 64-bit and the allocator stopped skipping it. pg_resetwal could still produce it via -O 0 or guessed control values, breaking the first multixact created after the reset ("MultiXact n has invalid offset", and vacuum of the affected table fails from then on). Reject -O 0 like -m and -o already do, and guess 1 like initdb does. --- src/bin/pg_resetwal/pg_resetwal.c | 6 +++++- src/bin/pg_resetwal/t/001_basic.pl | 4 ++++ src/bin/pg_resetwal/t/002_corrupted.pl | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c index d072e7c2ea4..1542a56ca4b 100644 --- a/src/bin/pg_resetwal/pg_resetwal.c +++ b/src/bin/pg_resetwal/pg_resetwal.c @@ -303,6 +303,10 @@ main(int argc, char *argv[]) pg_log_error_hint("Try \"%s --help\" for more information.", progname); exit(1); } + + /* offset 0 means "invalid" in pg_multixact/offsets */ + if (next_mxoff_val == 0) + pg_fatal("next multitransaction offset (-O) must not be 0"); next_mxoff_given = true; break; @@ -700,7 +704,7 @@ GuessControlValues(void) FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId); ControlFile.checkPointCopy.nextOid = FirstGenbkiObjectId; ControlFile.checkPointCopy.nextMulti = FirstMultiXactId; - ControlFile.checkPointCopy.nextMultiOffset = 0; + ControlFile.checkPointCopy.nextMultiOffset = 1; ControlFile.checkPointCopy.oldestXid = FirstNormalTransactionId; ControlFile.checkPointCopy.oldestXidDB = InvalidOid; ControlFile.checkPointCopy.oldestMulti = FirstMultiXactId; diff --git a/src/bin/pg_resetwal/t/001_basic.pl b/src/bin/pg_resetwal/t/001_basic.pl index d686584eb96..cff0b6423f3 100644 --- a/src/bin/pg_resetwal/t/001_basic.pl +++ b/src/bin/pg_resetwal/t/001_basic.pl @@ -145,6 +145,10 @@ command_fails_like( [ 'pg_resetwal', '-O' => '-1', $node->data_dir ], qr/error: invalid argument for option -O/, 'fails with -O value -1'); +command_fails_like( + [ 'pg_resetwal', '-O' => '0', $node->data_dir ], + qr/must not be 0/, + 'fails with -O value 0'); # --wal-segsize command_fails_like( [ 'pg_resetwal', '--wal-segsize' => 'foo', $node->data_dir ], diff --git a/src/bin/pg_resetwal/t/002_corrupted.pl b/src/bin/pg_resetwal/t/002_corrupted.pl index dcd06913778..a83ae631d5c 100644 --- a/src/bin/pg_resetwal/t/002_corrupted.pl +++ b/src/bin/pg_resetwal/t/002_corrupted.pl @@ -64,4 +64,11 @@ command_ok( [ 'pg_resetwal', '--force', $node->data_dir ], 'runs with force when control file values were guessed'); +# Offset 0 means "invalid" in pg_multixact/offsets, so the guessed +# next multixact offset must not be 0. +command_like( + [ 'pg_controldata', $node->data_dir ], + qr/^Latest checkpoint's NextMultiOffset:\s*[1-9]/m, + 'guessed next multixact offset is not 0'); + done_testing(); -- 2.54.0