From ae06a73a417505f2c74d9751b09d08597c72d448 Mon Sep 17 00:00:00 2001
From: Shihao <zhong950419@gmail.com>
Date: Thu, 1 Oct 2026 22:34:04 -0600
Subject: [PATCH v2 3/4] pg_resetwal: Add --cluster-state option

pg_resetwal always marks the cluster as shut down.  This option stores
another state instead.  With any other state the next start goes through
crash recovery, which for example empties unlogged tables.

Discussion: https://postgr.es/m/ar8mhs05SzC4hjmu@paquier.xyz
---
 doc/src/sgml/ref/pg_resetwal.sgml | 22 +++++++++++++++++
 src/bin/pg_resetwal/pg_resetwal.c | 40 ++++++++++++++++++++++++++++++-
 2 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/pg_resetwal.sgml b/doc/src/sgml/ref/pg_resetwal.sgml
index 229cf34f00b..0170a5029bf 100644
--- a/doc/src/sgml/ref/pg_resetwal.sgml
+++ b/doc/src/sgml/ref/pg_resetwal.sgml
@@ -396,6 +396,28 @@ PostgreSQL documentation
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><option>--cluster-state=<replaceable class="parameter">state</replaceable></option></term>
+    <listitem>
+     <para>
+      Manually set the database cluster state stored in
+      <filename>pg_control</filename>.  Possible values are
+      <literal>shut-down</literal> (the default),
+      <literal>shut-down-in-recovery</literal>,
+      <literal>shutting-down</literal>,
+      <literal>in-crash-recovery</literal>,
+      <literal>in-archive-recovery</literal> and
+      <literal>in-production</literal>.
+     </para>
+     <para>
+      With any state other than <literal>shut-down</literal>, the server
+      goes through crash recovery at its next start.  There is no WAL to
+      replay after a reset, but the other steps of crash recovery still
+      run.  For example, unlogged tables are emptied.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><option>--wal-segsize=<replaceable class="parameter">wal_segment_size</replaceable></option></term>
     <listitem>
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index bc81463c180..6ee74187fdc 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -97,6 +97,10 @@ static int	wal_segsize_val;
 static bool char_signedness_given = false;
 static bool char_signedness_val;
 
+static bool cluster_state_given = false;
+static DBState cluster_state_val;
+static const char *cluster_state_name;
+
 
 static TimeLineID minXlogTli = 0;
 static XLogSegNo minXlogSegNo = 0;
@@ -135,6 +139,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},
+		{"cluster-state", required_argument, NULL, 3},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -356,6 +361,32 @@ main(int argc, char *argv[])
 					break;
 				}
 
+			case 3:
+				{
+					/* DB_STARTUP is left out, the server rejects it */
+					if (strcmp(optarg, "shut-down") == 0)
+						cluster_state_val = DB_SHUTDOWNED;
+					else if (strcmp(optarg, "shut-down-in-recovery") == 0)
+						cluster_state_val = DB_SHUTDOWNED_IN_RECOVERY;
+					else if (strcmp(optarg, "shutting-down") == 0)
+						cluster_state_val = DB_SHUTDOWNING;
+					else if (strcmp(optarg, "in-crash-recovery") == 0)
+						cluster_state_val = DB_IN_CRASH_RECOVERY;
+					else if (strcmp(optarg, "in-archive-recovery") == 0)
+						cluster_state_val = DB_IN_ARCHIVE_RECOVERY;
+					else if (strcmp(optarg, "in-production") == 0)
+						cluster_state_val = DB_IN_PRODUCTION;
+					else
+					{
+						pg_log_error("invalid argument for option %s", "--cluster-state");
+						pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+						exit(1);
+					}
+					cluster_state_name = optarg;
+					cluster_state_given = true;
+					break;
+				}
+
 			default:
 				/* getopt_long already emitted a complaint */
 				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
@@ -919,6 +950,12 @@ PrintNewControlValues(void)
 		printf(_("Bytes per WAL segment:                %u\n"),
 			   ControlFile.xlog_seg_size);
 	}
+
+	if (cluster_state_given)
+	{
+		printf(_("Database cluster state:               %s\n"),
+			   cluster_state_name);
+	}
 }
 
 
@@ -936,7 +973,7 @@ RewriteControlFile(void)
 							ControlFile.checkPointCopy.redo);
 	ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
 
-	ControlFile.state = DB_SHUTDOWNED;
+	ControlFile.state = cluster_state_given ? cluster_state_val : DB_SHUTDOWNED;
 	ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
 	ControlFile.minRecoveryPoint = InvalidXLogRecPtr;
 	ControlFile.minRecoveryPointTLI = 0;
@@ -1269,6 +1306,7 @@ usage(void)
 	printf(_("  -u, --oldest-transaction-id=XID  set oldest transaction ID\n"));
 	printf(_("  -x, --next-transaction-id=XID    set next transaction ID\n"));
 	printf(_("      --char-signedness=OPTION     set char signedness to \"signed\" or \"unsigned\"\n"));
+	printf(_("      --cluster-state=STATE        set database cluster state\n"));
 	printf(_("      --wal-segsize=SIZE           size of WAL segments, in megabytes\n"));
 
 	printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
-- 
2.37.1 (Apple Git-137.1)

