From 2565a6b914e814063b1c0ed501c60fd8b5942f3d Mon Sep 17 00:00:00 2001
From: Maxim Orlov <orlovmg@gmail.com>
Date: Fri, 20 Jun 2025 17:20:59 +0300
Subject: [PATCH v1 1/2] Test case for BUG #18863 and BUG #18865

---
 src/bin/pg_upgrade/meson.build     |   1 +
 src/bin/pg_upgrade/t/007_mxwrap.pl | 111 +++++++++++++++++++++++++++++
 2 files changed, 112 insertions(+)
 create mode 100644 src/bin/pg_upgrade/t/007_mxwrap.pl

diff --git a/src/bin/pg_upgrade/meson.build b/src/bin/pg_upgrade/meson.build
index ac992f0d14..aff3c5ddb4 100644
--- a/src/bin/pg_upgrade/meson.build
+++ b/src/bin/pg_upgrade/meson.build
@@ -47,6 +47,7 @@ tests += {
       't/004_subscription.pl',
       't/005_char_signedness.pl',
       't/006_transfer_modes.pl',
+      't/007_mxwrap.pl',
     ],
     'test_kwargs': {'priority': 40}, # pg_upgrade tests are slow
   },
diff --git a/src/bin/pg_upgrade/t/007_mxwrap.pl b/src/bin/pg_upgrade/t/007_mxwrap.pl
new file mode 100644
index 0000000000..2cba978c18
--- /dev/null
+++ b/src/bin/pg_upgrade/t/007_mxwrap.pl
@@ -0,0 +1,111 @@
+#
+# Copyright (c) 2025, PostgreSQL Global Development Group
+#
+use strict;
+use warnings FATAL => 'all';
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+sub print_controldata_info
+{
+	my $node = shift;
+	my ($stdout, $stderr) = run_command([ 'pg_controldata', $node->data_dir ]);
+
+	foreach (split("\n", $stdout))
+	{
+		if ($_ =~ /^Latest checkpoint's Next\s*(.*)$/mg or
+			$_ =~ /^Latest checkpoint's oldest\s*(.*)$/mg)
+		{
+			print $_."\n";
+		}
+	}
+}
+
+# 1) Create the new cluster - source_cluster
+my $oldnode = PostgreSQL::Test::Cluster->new('main');
+$oldnode->init;
+
+# 2) Reset mxid to the limit
+command_ok(
+	[ 'pg_resetwal', '-m', '4294967295,4294967295', $oldnode->data_dir ],
+	'approaching the mxid limit');
+command_ok(
+	[ 'pg_resetwal', '-O', '4294967293', $oldnode->data_dir ],
+	'approaching the mxoff limit');
+
+# 3) Create files in pg_multixact offsets and members
+my $offsets_seg = $oldnode->data_dir . '/pg_multixact/offsets/FFFF';
+open my $fh1, '>', $offsets_seg or BAIL_OUT($!);
+binmode $fh1;
+print $fh1 pack("x[262144]");
+close $fh1;
+
+my $members_seg = $oldnode->data_dir . '/pg_multixact/members/14078';
+open my $fh2, '>', $members_seg or BAIL_OUT($!);
+binmode $fh2;
+print $fh2 pack("x[262144]");
+close $fh2;
+
+# 4) Create the test table
+$oldnode->start;
+$oldnode->safe_psql('postgres',
+qq(
+	CREATE TABLE test_table (id integer NOT NULL PRIMARY KEY, val text);
+	INSERT INTO test_table VALUES (1, 'a');
+));
+
+# 5-9) Create a multitransaction
+my $conn1 = $oldnode->background_psql('postgres');
+my $conn2 = $oldnode->background_psql('postgres');
+
+$conn1->query_safe(qq(
+	BEGIN;
+	SELECT * FROM test_table WHERE id = 1 FOR SHARE;
+));
+$conn2->query_safe(qq(
+	BEGIN;
+	SELECT * FROM test_table WHERE id = 1 FOR SHARE;
+));
+
+$conn1->query_safe(qq(COMMIT;));
+$conn2->query_safe(qq(COMMIT;));
+
+$conn1->quit;
+$conn2->quit;
+
+$oldnode->stop;
+
+# 10) Run pg_upgrade
+my $newnode = PostgreSQL::Test::Cluster->new('new_node');
+$newnode->init;
+
+command_ok(
+	[
+		'pg_upgrade', '--no-sync', '-d', $oldnode->data_dir,
+		'-D', $newnode->data_dir, '-b', $oldnode->config_data('--bindir'),
+		'-B', $newnode->config_data('--bindir'), '-s', $newnode->host,
+		'-p', $oldnode->port, '-P', $newnode->port,
+		'--copy',
+	],
+	'run of pg_upgrade --check for new instance');
+ok(!-d $newnode->data_dir . "/pg_upgrade_output.d",
+	"pg_upgrade_output.d/ removed after pg_upgrade --check success");
+
+print ">>> OLD NODE: \n";
+print_controldata_info($oldnode);
+
+print "\n>>> NEW NODE: \n";
+print_controldata_info($newnode);
+
+# just in case...
+$newnode->start;
+$oldnode->start;
+is($newnode->safe_psql('postgres', qq(TABLE test_table;)),
+	$oldnode->safe_psql('postgres', qq(TABLE test_table;)),
+	'check invariant');
+$newnode->stop;
+$oldnode->stop;
+
+done_testing();
-- 
2.43.0

