From 93820badbbfe3453f85dda96af041937e83d833f Mon Sep 17 00:00:00 2001
From: Nikolay Samokhvalov <nik@postgres.ai>
Date: Thu, 5 Feb 2026 05:17:26 +0000
Subject: [PATCH v4] Add --system-identifier option to pg_resetwal

Add option to manually set the database system identifier. Useful for:
- Making restored clusters distinct from originals
- Cloning clusters for testing
- Recovery attempts on corrupted clusters

v4 changes:
- Use "Database system identifier" label (Fujii Masao)
- Use PRIu64 format
- Fix test to use standard PG TAP functions (command_like, etc.)
  instead of non-existent run_command and fragile eval patterns
- Add --dry-run output test for system identifier

---
 doc/src/sgml/ref/pg_resetwal.sgml  | 23 +++++++++++++++++++++++
 src/bin/pg_resetwal/pg_resetwal.c  | 28 ++++++++++++++++++++++++++++
 src/bin/pg_resetwal/t/001_basic.pl | 37 ++++++++++++++++++++++++++++++++
 3 files changed, 88 insertions(+)

diff --git a/doc/src/sgml/ref/pg_resetwal.sgml b/doc/src/sgml/ref/pg_resetwal.sgml
index 41f2b1d..8dc8f6c 100644
--- a/doc/src/sgml/ref/pg_resetwal.sgml
+++ b/doc/src/sgml/ref/pg_resetwal.sgml
@@ -359,6 +359,29 @@ PostgreSQL documentation
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><option>--system-identifier=<replaceable class="parameter">system_identifier</replaceable></option></term>
+    <listitem>
+     <para>
+      Manually set the database system identifier.
+     </para>
+
+     <para>
+      This option should only be used in recovery scenarios where you need
+      to make a restored cluster distinct from the original, or when cloning
+      a cluster for testing purposes. The value must be a positive 64-bit
+      integer and cannot be zero.
+     </para>
+
+     <warning>
+      <para>
+       Changing the system identifier will break compatibility with existing
+       backups and standby servers. Use this option with extreme caution.
+      </para>
+     </warning>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><option>--char-signedness=<replaceable class="parameter">option</replaceable></option></term>
     <listitem>
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 85dc43d..5fe4800 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -97,6 +97,9 @@ static int	wal_segsize_val;
 static bool char_signedness_given = false;
 static bool char_signedness_val;
 
+static bool sysid_given = false;
+static uint64 sysid_val;
+
 
 static TimeLineID minXlogTli = 0;
 static XLogSegNo minXlogSegNo = 0;
@@ -135,6 +138,7 @@ main(int argc, char *argv[])
 		{"next-transaction-id", required_argument, NULL, 'x'},
 		{"wal-segsize", required_argument, NULL, 1},
 		{"char-signedness", required_argument, NULL, 2},
+		{"system-identifier", required_argument, NULL, 3},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -352,6 +356,20 @@ main(int argc, char *argv[])
 					break;
 				}
 
+			case 3:
+				errno = 0;
+				sysid_val = strtou64(optarg, &endptr, 0);
+				if (endptr == optarg || *endptr != '\0' || errno != 0)
+				{
+					pg_log_error("invalid argument for option %s", "--system-identifier");
+					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+					exit(1);
+				}
+				if (sysid_val == 0)
+					pg_fatal("system identifier must not be 0");
+				sysid_given = true;
+				break;
+
 			default:
 				/* getopt_long already emitted a complaint */
 				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
@@ -510,6 +528,9 @@ main(int argc, char *argv[])
 	if (char_signedness_given)
 		ControlFile.default_char_signedness = char_signedness_val;
 
+	if (sysid_given)
+		ControlFile.system_identifier = sysid_val;
+
 	if (minXlogSegNo > newXlogSegNo)
 		newXlogSegNo = minXlogSegNo;
 
@@ -894,6 +915,12 @@ PrintNewControlValues(void)
 		printf(_("Bytes per WAL segment:                %u\n"),
 			   ControlFile.xlog_seg_size);
 	}
+
+	if (sysid_given)
+	{
+		printf(_("Database system identifier:           %" PRIu64 "\n"),
+			   ControlFile.system_identifier);
+	}
 }
 
 
@@ -1236,6 +1263,7 @@ usage(void)
 	printf(_("  -O, --multixact-offset=OFFSET    set next multitransaction offset\n"));
 	printf(_("  -u, --oldest-transaction-id=XID  set oldest transaction ID\n"));
 	printf(_("  -x, --next-transaction-id=XID    set next transaction ID\n"));
+	printf(_("      --system-identifier=SYSID    set database system identifier\n"));
 	printf(_("      --char-signedness=OPTION     set char signedness to \"signed\" or \"unsigned\"\n"));
 	printf(_("      --wal-segsize=SIZE           size of WAL segments, in megabytes\n"));
 
diff --git a/src/bin/pg_resetwal/t/001_basic.pl b/src/bin/pg_resetwal/t/001_basic.pl
index d686584..ea889b3 100644
--- a/src/bin/pg_resetwal/t/001_basic.pl
+++ b/src/bin/pg_resetwal/t/001_basic.pl
@@ -194,6 +194,42 @@ command_fails_like(
 	qr/error: invalid argument for option --char-signedness/,
 	'fails with incorrect --char-signedness option');
 
+# --system-identifier
+command_fails_like(
+	[ 'pg_resetwal', '--system-identifier' => 'foo', $node->data_dir ],
+	qr/error: invalid argument for option --system-identifier/,
+	'fails with incorrect --system-identifier option');
+command_fails_like(
+	[ 'pg_resetwal', '--system-identifier' => '0', $node->data_dir ],
+	qr/system identifier must not be 0/,
+	'fails with zero system identifier');
+
+# Test actual system identifier change with force flag
+$node->stop;
+my $new_sysid = '7654321098765432109';
+command_ok(
+	[ 'pg_resetwal', '-f', '--system-identifier' => $new_sysid, $node->data_dir ],
+	'pg_resetwal --system-identifier with force flag succeeds');
+
+# Verify the change using pg_controldata (handles uint64 correctly)
+command_like(
+	[ 'pg_controldata', $node->data_dir ],
+	qr/Database system identifier:\s+\Q$new_sysid/,
+	'system identifier was changed correctly');
+
+# Test that the server works normally after system identifier change
+$node->start;
+is($node->safe_psql("postgres", "SELECT 1;"),
+	1, 'server running and working after system identifier change');
+
+$node->stop;
+
+# Verify --dry-run output shows the new system identifier
+command_like(
+	[ 'pg_resetwal', '--dry-run', '-f', '--system-identifier' => $new_sysid, $node->data_dir ],
+	qr/Database system identifier:\s+\Q$new_sysid/,
+	'dry-run output shows new system identifier');
+
 # run with control override options
 
 my $out = (run_command([ 'pg_resetwal', '--dry-run', $node->data_dir ]))[0];
