From 32d47cc75e4a1e6f3744e76a63bd1d90477ce8ae Mon Sep 17 00:00:00 2001 From: shihao zhong Date: Thu, 8 Oct 2026 23:58:06 -0400 Subject: [PATCH v2 1/2] 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 Discussion: https://postgr.es/m/CAGRkXqRH=xzB7svv-xFRF=6Lexo8tX0T4XX207uPAwuLys-Kcw@mail.gmail.com --- doc/src/sgml/ref/pg_resetwal.sgml | 27 +++++++++++- src/bin/pg_resetwal/pg_resetwal.c | 72 ++++++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 3 deletions(-) diff --git a/doc/src/sgml/ref/pg_resetwal.sgml b/doc/src/sgml/ref/pg_resetwal.sgml index d34aa26413c..d8805ef6f3b 100644 --- a/doc/src/sgml/ref/pg_resetwal.sgml +++ b/doc/src/sgml/ref/pg_resetwal.sgml @@ -67,7 +67,9 @@ PostgreSQL documentation If pg_resetwal is used on a data directory where the server has been cleanly shut down and the control file is sound, then it will have no effect on the contents of the database system, except that no - longer used WAL files are cleared away. Any other use is potentially + longer used WAL files are cleared away. This does not hold when + is used to set a state other than + shut-down, see below. Any other use is potentially dangerous and must be done with great care. pg_resetwal will require the (force) option to be specified before working on a data directory in an unclean shutdown state or with a @@ -384,6 +386,29 @@ PostgreSQL documentation + + + + + Manually set the database cluster state stored in + pg_control. Possible values are + shut-down (the default), + shut-down-in-recovery, + shutting-down, + in-crash-recovery, + in-archive-recovery and + in-production. The state currently stored is + shown by . + + + With any state other than shut-down, 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. + + + + diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c index 634d966da9e..38a9ae8c9f1 100644 --- a/src/bin/pg_resetwal/pg_resetwal.c +++ b/src/bin/pg_resetwal/pg_resetwal.c @@ -97,6 +97,25 @@ 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; + +/* DB_STARTUP is left out, the server rejects it */ +struct cluster_state_opt +{ + const char *name; + DBState state; +}; + +static const struct cluster_state_opt cluster_states[] = { + {"shut-down", DB_SHUTDOWNED}, + {"shut-down-in-recovery", DB_SHUTDOWNED_IN_RECOVERY}, + {"shutting-down", DB_SHUTDOWNING}, + {"in-crash-recovery", DB_IN_CRASH_RECOVERY}, + {"in-archive-recovery", DB_IN_ARCHIVE_RECOVERY}, + {"in-production", DB_IN_PRODUCTION}, +}; + static TimeLineID minXlogTli = 0; static XLogSegNo minXlogSegNo = 0; @@ -105,6 +124,7 @@ static int WalSegSz; static void CheckDataVersion(void); static bool read_controlfile(void); static void GuessControlValues(void); +static const char *cluster_state_name(DBState state); static void PrintControlValues(bool guessed); static void PrintNewControlValues(void); static void RewriteControlFile(void); @@ -135,6 +155,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 +377,26 @@ main(int argc, char *argv[]) break; } + case 3: + { + for (int i = 0; i < lengthof(cluster_states); i++) + { + if (strcmp(optarg, cluster_states[i].name) == 0) + { + cluster_state_val = cluster_states[i].state; + cluster_state_given = true; + break; + } + } + if (!cluster_state_given) + { + pg_log_error("invalid argument for option %s", "--cluster-state"); + pg_log_error_hint("Try \"%s --help\" for more information.", progname); + exit(1); + } + break; + } + default: /* getopt_long already emitted a complaint */ pg_log_error_hint("Try \"%s --help\" for more information.", progname); @@ -750,11 +791,26 @@ GuessControlValues(void) } +/* + * Return the --cluster-state spelling of a cluster state. + */ +static const char * +cluster_state_name(DBState state) +{ + for (int i = 0; i < lengthof(cluster_states); i++) + { + if (cluster_states[i].state == state) + return cluster_states[i].name; + } + return (state == DB_STARTUP) ? "starting-up" : _("unrecognized"); +} + + /* * Print the guessed pg_control values when we had to guess. * * NB: this display should be just those fields that will not be - * reset by RewriteControlFile(). + * reset by RewriteControlFile(), plus the cluster state. */ static void PrintControlValues(bool guessed) @@ -770,6 +826,8 @@ PrintControlValues(bool guessed) ControlFile.catalog_version_no); printf(_("Database system identifier: %" PRIu64 "\n"), ControlFile.system_identifier); + printf(_("Database cluster state: %s\n"), + cluster_state_name(ControlFile.state)); printf(_("Latest checkpoint's TimeLineID: %u\n"), ControlFile.checkPointCopy.ThisTimeLineID); printf(_("Latest checkpoint's full_page_writes: %s\n"), @@ -900,6 +958,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(cluster_state_val)); + } } @@ -917,7 +981,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; @@ -1250,6 +1314,10 @@ 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 to \"shut-down\",\n" + " \"shut-down-in-recovery\", \"shutting-down\",\n" + " \"in-crash-recovery\", \"in-archive-recovery\"\n" + " or \"in-production\"\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)