1 /*-------------------------------------------------------------------------
4 * A utility to "zero out" the xlog when it's corrupt beyond recovery.
5 * Can also rebuild pg_control if needed.
7 * The theory of operation is fairly simple:
8 * 1. Read the existing pg_control (which will include the last
10 * 2. If pg_control is corrupt, attempt to intuit reasonable values,
11 * by scanning the old xlog if necessary.
12 * 3. Modify pg_control to reflect a "shutdown" state with a checkpoint
13 * record at the start of xlog.
14 * 4. Flush the existing xlog files and write a new segment with
15 * just a checkpoint record in it. The new segment is positioned
16 * just past the end of the old xlog, so that existing LSNs in
17 * data pages will appear to be "in the past".
18 * This is all pretty straightforward except for the intuition part of
22 * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
23 * Portions Copyright (c) 1994, Regents of the University of California
25 * src/bin/pg_resetwal/pg_resetwal.c
27 *-------------------------------------------------------------------------
31 * We have to use postgres.h not postgres_fe.h here, because there's so much
32 * backend-only stuff in the XLOG include files we need. But we need a
33 * frontend-ish environment otherwise. Hence this ugly hack.
46 #include "access/heaptoast.h"
47 #include "access/multixact.h"
48 #include "access/transam.h"
49 #include "access/xlog.h"
50 #include "access/xlog_internal.h"
51 #include "common/controldata_utils.h"
52 #include "common/fe_memutils.h"
53 #include "common/file_perm.h"
54 #include "common/logging.h"
55 #include "common/restricted_token.h"
56 #include "common/string.h"
57 #include "fe_utils/option_utils.h"
58 #include "getopt_long.h"
59 #include "pg_getopt.h"
60 #include "storage/large_object.h"
62 static ControlFileData ControlFile
; /* pg_control values */
63 static XLogSegNo newXlogSegNo
; /* new XLOG segment # */
64 static bool guessed
= false; /* T if we had to guess at any values */
65 static const char *progname
;
66 static uint32 set_xid_epoch
= (uint32
) -1;
67 static TransactionId set_oldest_xid
= 0;
68 static TransactionId set_xid
= 0;
69 static TransactionId set_oldest_commit_ts_xid
= 0;
70 static TransactionId set_newest_commit_ts_xid
= 0;
71 static Oid set_oid
= 0;
72 static MultiXactId set_mxid
= 0;
73 static MultiXactOffset set_mxoff
= (MultiXactOffset
) -1;
74 static TimeLineID minXlogTli
= 0;
75 static XLogSegNo minXlogSegNo
= 0;
77 static int set_wal_segsize
;
79 static void CheckDataVersion(void);
80 static bool read_controlfile(void);
81 static void GuessControlValues(void);
82 static void PrintControlValues(bool guessed
);
83 static void PrintNewControlValues(void);
84 static void RewriteControlFile(void);
85 static void FindEndOfXLOG(void);
86 static void KillExistingXLOG(void);
87 static void KillExistingArchiveStatus(void);
88 static void KillExistingWALSummaries(void);
89 static void WriteEmptyXLOG(void);
90 static void usage(void);
94 main(int argc
, char *argv
[])
96 static struct option long_options
[] = {
97 {"commit-timestamp-ids", required_argument
, NULL
, 'c'},
98 {"pgdata", required_argument
, NULL
, 'D'},
99 {"epoch", required_argument
, NULL
, 'e'},
100 {"force", no_argument
, NULL
, 'f'},
101 {"next-wal-file", required_argument
, NULL
, 'l'},
102 {"multixact-ids", required_argument
, NULL
, 'm'},
103 {"dry-run", no_argument
, NULL
, 'n'},
104 {"next-oid", required_argument
, NULL
, 'o'},
105 {"multixact-offset", required_argument
, NULL
, 'O'},
106 {"oldest-transaction-id", required_argument
, NULL
, 'u'},
107 {"next-transaction-id", required_argument
, NULL
, 'x'},
108 {"wal-segsize", required_argument
, NULL
, 1},
114 bool noupdate
= false;
115 MultiXactId set_oldestmxid
= 0;
118 char *DataDir
= NULL
;
119 char *log_fname
= NULL
;
122 pg_logging_init(argv
[0]);
123 set_pglocale_pgservice(argv
[0], PG_TEXTDOMAIN("pg_resetwal"));
124 progname
= get_progname(argv
[0]);
128 if (strcmp(argv
[1], "--help") == 0 || strcmp(argv
[1], "-?") == 0)
133 if (strcmp(argv
[1], "--version") == 0 || strcmp(argv
[1], "-V") == 0)
135 puts("pg_resetwal (PostgreSQL) " PG_VERSION
);
141 while ((c
= getopt_long(argc
, argv
, "c:D:e:fl:m:no:O:u:x:", long_options
, NULL
)) != -1)
159 set_xid_epoch
= strtoul(optarg
, &endptr
, 0);
160 if (endptr
== optarg
|| *endptr
!= '\0' || errno
!= 0)
163 translator: the second %s is a command line argument (-e, etc) */
164 pg_log_error("invalid argument for option %s", "-e");
165 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
168 if (set_xid_epoch
== -1)
169 pg_fatal("transaction ID epoch (-e) must not be -1");
174 set_oldest_xid
= strtoul(optarg
, &endptr
, 0);
175 if (endptr
== optarg
|| *endptr
!= '\0' || errno
!= 0)
177 pg_log_error("invalid argument for option %s", "-u");
178 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
181 if (!TransactionIdIsNormal(set_oldest_xid
))
182 pg_fatal("oldest transaction ID (-u) must be greater than or equal to %u", FirstNormalTransactionId
);
187 set_xid
= strtoul(optarg
, &endptr
, 0);
188 if (endptr
== optarg
|| *endptr
!= '\0' || errno
!= 0)
190 pg_log_error("invalid argument for option %s", "-x");
191 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
194 if (!TransactionIdIsNormal(set_xid
))
195 pg_fatal("transaction ID (-x) must be greater than or equal to %u", FirstNormalTransactionId
);
200 set_oldest_commit_ts_xid
= strtoul(optarg
, &endptr
, 0);
201 if (endptr
== optarg
|| *endptr
!= ',' || errno
!= 0)
203 pg_log_error("invalid argument for option %s", "-c");
204 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
207 set_newest_commit_ts_xid
= strtoul(endptr
+ 1, &endptr2
, 0);
208 if (endptr2
== endptr
+ 1 || *endptr2
!= '\0' || errno
!= 0)
210 pg_log_error("invalid argument for option %s", "-c");
211 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
215 if (set_oldest_commit_ts_xid
< FirstNormalTransactionId
&&
216 set_oldest_commit_ts_xid
!= InvalidTransactionId
)
217 pg_fatal("transaction ID (-c) must be either %u or greater than or equal to %u", InvalidTransactionId
, FirstNormalTransactionId
);
219 if (set_newest_commit_ts_xid
< FirstNormalTransactionId
&&
220 set_newest_commit_ts_xid
!= InvalidTransactionId
)
221 pg_fatal("transaction ID (-c) must be either %u or greater than or equal to %u", InvalidTransactionId
, FirstNormalTransactionId
);
226 set_oid
= strtoul(optarg
, &endptr
, 0);
227 if (endptr
== optarg
|| *endptr
!= '\0' || errno
!= 0)
229 pg_log_error("invalid argument for option %s", "-o");
230 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
234 pg_fatal("OID (-o) must not be 0");
239 set_mxid
= strtoul(optarg
, &endptr
, 0);
240 if (endptr
== optarg
|| *endptr
!= ',' || errno
!= 0)
242 pg_log_error("invalid argument for option %s", "-m");
243 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
247 set_oldestmxid
= strtoul(endptr
+ 1, &endptr2
, 0);
248 if (endptr2
== endptr
+ 1 || *endptr2
!= '\0' || errno
!= 0)
250 pg_log_error("invalid argument for option %s", "-m");
251 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
255 pg_fatal("multitransaction ID (-m) must not be 0");
258 * XXX It'd be nice to have more sanity checks here, e.g. so
259 * that oldest is not wrapped around w.r.t. nextMulti.
261 if (set_oldestmxid
== 0)
262 pg_fatal("oldest multitransaction ID (-m) must not be 0");
267 set_mxoff
= strtoul(optarg
, &endptr
, 0);
268 if (endptr
== optarg
|| *endptr
!= '\0' || errno
!= 0)
270 pg_log_error("invalid argument for option %s", "-O");
271 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
275 pg_fatal("multitransaction offset (-O) must not be -1");
279 if (strspn(optarg
, "01234567890ABCDEFabcdef") != XLOG_FNAME_LEN
)
281 pg_log_error("invalid argument for option %s", "-l");
282 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
287 * XLogFromFileName requires wal segment size which is not yet
288 * set. Hence wal details are set later on.
290 log_fname
= pg_strdup(optarg
);
297 if (!option_parse_int(optarg
, "--wal-segsize", 1, 1024, &wal_segsize_mb
))
299 set_wal_segsize
= wal_segsize_mb
* 1024 * 1024;
300 if (!IsValidWalSegSize(set_wal_segsize
))
301 pg_fatal("argument of %s must be a power of two between 1 and 1024", "--wal-segsize");
306 /* getopt_long already emitted a complaint */
307 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
312 if (DataDir
== NULL
&& optind
< argc
)
313 DataDir
= argv
[optind
++];
315 /* Complain if any arguments remain */
318 pg_log_error("too many command-line arguments (first is \"%s\")",
320 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
326 pg_log_error("no data directory specified");
327 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
332 * Don't allow pg_resetwal to be run as root, to avoid overwriting the
333 * ownership of files in the data directory. We need only check for root
334 * -- any other user won't have sufficient permissions to modify files in
335 * the data directory.
340 pg_log_error("cannot be executed by \"root\"");
341 pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
347 get_restricted_token();
349 /* Set mask based on PGDATA permissions */
350 if (!GetDataDirectoryCreatePerm(DataDir
))
351 pg_fatal("could not read permissions of directory \"%s\": %m",
356 if (chdir(DataDir
) < 0)
357 pg_fatal("could not change directory to \"%s\": %m",
360 /* Check that data directory matches our server version */
364 * Check for a postmaster lock file --- if there is one, refuse to
365 * proceed, on grounds we might be interfering with a live installation.
367 if ((fd
= open("postmaster.pid", O_RDONLY
, 0)) < 0)
370 pg_fatal("could not open file \"%s\" for reading: %m",
375 pg_log_error("lock file \"%s\" exists", "postmaster.pid");
376 pg_log_error_hint("Is a server running? If not, delete the lock file and try again.");
381 * Attempt to read the existing pg_control file
383 if (!read_controlfile())
384 GuessControlValues();
387 * If no new WAL segment size was specified, use the control file value.
389 if (set_wal_segsize
!= 0)
390 WalSegSz
= set_wal_segsize
;
392 WalSegSz
= ControlFile
.xlog_seg_size
;
394 if (log_fname
!= NULL
)
395 XLogFromFileName(log_fname
, &minXlogTli
, &minXlogSegNo
, WalSegSz
);
398 * Also look at existing segment files to set up newXlogSegNo
403 * If we're not going to proceed with the reset, print the current control
406 if ((guessed
&& !force
) || noupdate
)
407 PrintControlValues(guessed
);
410 * Adjust fields if required by switches. (Do this now so that printout,
411 * if any, includes these values.)
413 if (set_xid_epoch
!= -1)
414 ControlFile
.checkPointCopy
.nextXid
=
415 FullTransactionIdFromEpochAndXid(set_xid_epoch
,
416 XidFromFullTransactionId(ControlFile
.checkPointCopy
.nextXid
));
418 if (set_oldest_xid
!= 0)
420 ControlFile
.checkPointCopy
.oldestXid
= set_oldest_xid
;
421 ControlFile
.checkPointCopy
.oldestXidDB
= InvalidOid
;
425 ControlFile
.checkPointCopy
.nextXid
=
426 FullTransactionIdFromEpochAndXid(EpochFromFullTransactionId(ControlFile
.checkPointCopy
.nextXid
),
429 if (set_oldest_commit_ts_xid
!= 0)
430 ControlFile
.checkPointCopy
.oldestCommitTsXid
= set_oldest_commit_ts_xid
;
431 if (set_newest_commit_ts_xid
!= 0)
432 ControlFile
.checkPointCopy
.newestCommitTsXid
= set_newest_commit_ts_xid
;
435 ControlFile
.checkPointCopy
.nextOid
= set_oid
;
439 ControlFile
.checkPointCopy
.nextMulti
= set_mxid
;
441 ControlFile
.checkPointCopy
.oldestMulti
= set_oldestmxid
;
442 if (ControlFile
.checkPointCopy
.oldestMulti
< FirstMultiXactId
)
443 ControlFile
.checkPointCopy
.oldestMulti
+= FirstMultiXactId
;
444 ControlFile
.checkPointCopy
.oldestMultiDB
= InvalidOid
;
448 ControlFile
.checkPointCopy
.nextMultiOffset
= set_mxoff
;
450 if (minXlogTli
> ControlFile
.checkPointCopy
.ThisTimeLineID
)
452 ControlFile
.checkPointCopy
.ThisTimeLineID
= minXlogTli
;
453 ControlFile
.checkPointCopy
.PrevTimeLineID
= minXlogTli
;
456 if (set_wal_segsize
!= 0)
457 ControlFile
.xlog_seg_size
= WalSegSz
;
459 if (minXlogSegNo
> newXlogSegNo
)
460 newXlogSegNo
= minXlogSegNo
;
464 PrintNewControlValues();
469 * If we had to guess anything, and -f was not given, just print the
470 * guessed values and exit.
472 if (guessed
&& !force
)
474 PrintNewControlValues();
475 pg_log_error("not proceeding because control file values were guessed");
476 pg_log_error_hint("If these values seem acceptable, use -f to force reset.");
481 * Don't reset from a dirty pg_control without -f, either.
483 if (ControlFile
.state
!= DB_SHUTDOWNED
&& !force
)
485 pg_log_error("database server was not shut down cleanly");
486 pg_log_error_detail("Resetting the write-ahead log might cause data to be lost.");
487 pg_log_error_hint("If you want to proceed anyway, use -f to force reset.");
492 * Else, do the dirty deed.
494 RewriteControlFile();
496 KillExistingArchiveStatus();
497 KillExistingWALSummaries();
500 printf(_("Write-ahead log reset\n"));
506 * Look at the version string stored in PG_VERSION and decide if this utility
507 * can be run safely or not.
509 * We don't want to inject pg_control and WAL files that are for a different
510 * major version; that can't do anything good. Note that we don't treat
511 * mismatching version info in pg_control as a reason to bail out, because
512 * recovering from a corrupted pg_control is one of the main reasons for this
513 * program to exist at all. However, PG_VERSION is unlikely to get corrupted,
514 * and if it were it would be easy to fix by hand. So let's make this check
515 * to prevent simple user errors.
518 CheckDataVersion(void)
520 const char *ver_file
= "PG_VERSION";
524 if ((ver_fd
= fopen(ver_file
, "r")) == NULL
)
525 pg_fatal("could not open file \"%s\" for reading: %m",
528 /* version number has to be the first line read */
529 if (!fgets(rawline
, sizeof(rawline
), ver_fd
))
532 pg_fatal("unexpected empty file \"%s\"", ver_file
);
534 pg_fatal("could not read file \"%s\": %m", ver_file
);
537 /* strip trailing newline and carriage return */
538 (void) pg_strip_crlf(rawline
);
540 if (strcmp(rawline
, PG_MAJORVERSION
) != 0)
542 pg_log_error("data directory is of wrong version");
543 pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
544 ver_file
, rawline
, PG_MAJORVERSION
);
553 * Try to read the existing pg_control file.
555 * This routine is also responsible for updating old pg_control versions
556 * to the current format. (Currently we don't do anything of the sort.)
559 read_controlfile(void)
566 if ((fd
= open(XLOG_CONTROL_FILE
, O_RDONLY
| PG_BINARY
, 0)) < 0)
569 * If pg_control is not there at all, or we can't read it, the odds
570 * are we've been handed a bad DataDir path, so give up. User can do
571 * "touch pg_control" to force us to proceed.
573 pg_log_error("could not open file \"%s\" for reading: %m",
576 pg_log_error_hint("If you are sure the data directory path is correct, execute\n"
583 /* Use malloc to ensure we have a maxaligned buffer */
584 buffer
= (char *) pg_malloc(PG_CONTROL_FILE_SIZE
);
586 len
= read(fd
, buffer
, PG_CONTROL_FILE_SIZE
);
588 pg_fatal("could not read file \"%s\": %m", XLOG_CONTROL_FILE
);
591 if (len
>= sizeof(ControlFileData
) &&
592 ((ControlFileData
*) buffer
)->pg_control_version
== PG_CONTROL_VERSION
)
598 offsetof(ControlFileData
, crc
));
601 if (!EQ_CRC32C(crc
, ((ControlFileData
*) buffer
)->crc
))
603 /* We will use the data but treat it as guessed. */
604 pg_log_warning("pg_control exists but has invalid CRC; proceed with caution");
608 memcpy(&ControlFile
, buffer
, sizeof(ControlFile
));
610 /* return false if WAL segment size is not valid */
611 if (!IsValidWalSegSize(ControlFile
.xlog_seg_size
))
613 pg_log_warning(ngettext("pg_control specifies invalid WAL segment size (%d byte); proceed with caution",
614 "pg_control specifies invalid WAL segment size (%d bytes); proceed with caution",
615 ControlFile
.xlog_seg_size
),
616 ControlFile
.xlog_seg_size
);
623 /* Looks like it's a mess. */
624 pg_log_warning("pg_control exists but is broken or wrong version; ignoring it");
630 * Guess at pg_control values when we can't read the old ones.
633 GuessControlValues(void)
635 uint64 sysidentifier
;
639 * Set up a completely default set of pg_control values.
642 memset(&ControlFile
, 0, sizeof(ControlFile
));
644 ControlFile
.pg_control_version
= PG_CONTROL_VERSION
;
645 ControlFile
.catalog_version_no
= CATALOG_VERSION_NO
;
648 * Create a new unique installation identifier, since we can no longer use
649 * any old XLOG records. See notes in xlog.c about the algorithm.
651 gettimeofday(&tv
, NULL
);
652 sysidentifier
= ((uint64
) tv
.tv_sec
) << 32;
653 sysidentifier
|= ((uint64
) tv
.tv_usec
) << 12;
654 sysidentifier
|= getpid() & 0xFFF;
656 ControlFile
.system_identifier
= sysidentifier
;
658 ControlFile
.checkPointCopy
.redo
= SizeOfXLogLongPHD
;
659 ControlFile
.checkPointCopy
.ThisTimeLineID
= 1;
660 ControlFile
.checkPointCopy
.PrevTimeLineID
= 1;
661 ControlFile
.checkPointCopy
.fullPageWrites
= false;
662 ControlFile
.checkPointCopy
.nextXid
=
663 FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId
);
664 ControlFile
.checkPointCopy
.nextOid
= FirstGenbkiObjectId
;
665 ControlFile
.checkPointCopy
.nextMulti
= FirstMultiXactId
;
666 ControlFile
.checkPointCopy
.nextMultiOffset
= 0;
667 ControlFile
.checkPointCopy
.oldestXid
= FirstNormalTransactionId
;
668 ControlFile
.checkPointCopy
.oldestXidDB
= InvalidOid
;
669 ControlFile
.checkPointCopy
.oldestMulti
= FirstMultiXactId
;
670 ControlFile
.checkPointCopy
.oldestMultiDB
= InvalidOid
;
671 ControlFile
.checkPointCopy
.time
= (pg_time_t
) time(NULL
);
672 ControlFile
.checkPointCopy
.oldestActiveXid
= InvalidTransactionId
;
674 ControlFile
.state
= DB_SHUTDOWNED
;
675 ControlFile
.time
= (pg_time_t
) time(NULL
);
676 ControlFile
.checkPoint
= ControlFile
.checkPointCopy
.redo
;
677 ControlFile
.unloggedLSN
= FirstNormalUnloggedLSN
;
679 /* minRecoveryPoint, backupStartPoint and backupEndPoint can be left zero */
681 ControlFile
.wal_level
= WAL_LEVEL_MINIMAL
;
682 ControlFile
.wal_log_hints
= false;
683 ControlFile
.track_commit_timestamp
= false;
684 ControlFile
.MaxConnections
= 100;
685 ControlFile
.max_wal_senders
= 10;
686 ControlFile
.max_worker_processes
= 8;
687 ControlFile
.max_prepared_xacts
= 0;
688 ControlFile
.max_locks_per_xact
= 64;
690 ControlFile
.maxAlign
= MAXIMUM_ALIGNOF
;
691 ControlFile
.floatFormat
= FLOATFORMAT_VALUE
;
692 ControlFile
.blcksz
= BLCKSZ
;
693 ControlFile
.relseg_size
= RELSEG_SIZE
;
694 ControlFile
.xlog_blcksz
= XLOG_BLCKSZ
;
695 ControlFile
.xlog_seg_size
= DEFAULT_XLOG_SEG_SIZE
;
696 ControlFile
.nameDataLen
= NAMEDATALEN
;
697 ControlFile
.indexMaxKeys
= INDEX_MAX_KEYS
;
698 ControlFile
.toast_max_chunk_size
= TOAST_MAX_CHUNK_SIZE
;
699 ControlFile
.loblksize
= LOBLKSIZE
;
700 ControlFile
.float8ByVal
= FLOAT8PASSBYVAL
;
703 * XXX eventually, should try to grovel through old XLOG to develop more
704 * accurate values for TimeLineID, nextXID, etc.
710 * Print the guessed pg_control values when we had to guess.
712 * NB: this display should be just those fields that will not be
713 * reset by RewriteControlFile().
716 PrintControlValues(bool guessed
)
719 printf(_("Guessed pg_control values:\n\n"));
721 printf(_("Current pg_control values:\n\n"));
723 printf(_("pg_control version number: %u\n"),
724 ControlFile
.pg_control_version
);
725 printf(_("Catalog version number: %u\n"),
726 ControlFile
.catalog_version_no
);
727 printf(_("Database system identifier: %llu\n"),
728 (unsigned long long) ControlFile
.system_identifier
);
729 printf(_("Latest checkpoint's TimeLineID: %u\n"),
730 ControlFile
.checkPointCopy
.ThisTimeLineID
);
731 printf(_("Latest checkpoint's full_page_writes: %s\n"),
732 ControlFile
.checkPointCopy
.fullPageWrites
? _("on") : _("off"));
733 printf(_("Latest checkpoint's NextXID: %u:%u\n"),
734 EpochFromFullTransactionId(ControlFile
.checkPointCopy
.nextXid
),
735 XidFromFullTransactionId(ControlFile
.checkPointCopy
.nextXid
));
736 printf(_("Latest checkpoint's NextOID: %u\n"),
737 ControlFile
.checkPointCopy
.nextOid
);
738 printf(_("Latest checkpoint's NextMultiXactId: %u\n"),
739 ControlFile
.checkPointCopy
.nextMulti
);
740 printf(_("Latest checkpoint's NextMultiOffset: %u\n"),
741 ControlFile
.checkPointCopy
.nextMultiOffset
);
742 printf(_("Latest checkpoint's oldestXID: %u\n"),
743 ControlFile
.checkPointCopy
.oldestXid
);
744 printf(_("Latest checkpoint's oldestXID's DB: %u\n"),
745 ControlFile
.checkPointCopy
.oldestXidDB
);
746 printf(_("Latest checkpoint's oldestActiveXID: %u\n"),
747 ControlFile
.checkPointCopy
.oldestActiveXid
);
748 printf(_("Latest checkpoint's oldestMultiXid: %u\n"),
749 ControlFile
.checkPointCopy
.oldestMulti
);
750 printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
751 ControlFile
.checkPointCopy
.oldestMultiDB
);
752 printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"),
753 ControlFile
.checkPointCopy
.oldestCommitTsXid
);
754 printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
755 ControlFile
.checkPointCopy
.newestCommitTsXid
);
756 printf(_("Maximum data alignment: %u\n"),
757 ControlFile
.maxAlign
);
758 /* we don't print floatFormat since can't say much useful about it */
759 printf(_("Database block size: %u\n"),
761 printf(_("Blocks per segment of large relation: %u\n"),
762 ControlFile
.relseg_size
);
763 printf(_("WAL block size: %u\n"),
764 ControlFile
.xlog_blcksz
);
765 printf(_("Bytes per WAL segment: %u\n"),
766 ControlFile
.xlog_seg_size
);
767 printf(_("Maximum length of identifiers: %u\n"),
768 ControlFile
.nameDataLen
);
769 printf(_("Maximum columns in an index: %u\n"),
770 ControlFile
.indexMaxKeys
);
771 printf(_("Maximum size of a TOAST chunk: %u\n"),
772 ControlFile
.toast_max_chunk_size
);
773 printf(_("Size of a large-object chunk: %u\n"),
774 ControlFile
.loblksize
);
775 /* This is no longer configurable, but users may still expect to see it: */
776 printf(_("Date/time type storage: %s\n"),
777 _("64-bit integers"));
778 printf(_("Float8 argument passing: %s\n"),
779 (ControlFile
.float8ByVal
? _("by value") : _("by reference")));
780 printf(_("Data page checksum version: %u\n"),
781 ControlFile
.data_checksum_version
);
786 * Print the values to be changed.
789 PrintNewControlValues(void)
791 char fname
[MAXFNAMELEN
];
793 /* This will be always printed in order to keep format same. */
794 printf(_("\n\nValues to be changed:\n\n"));
796 XLogFileName(fname
, ControlFile
.checkPointCopy
.ThisTimeLineID
,
797 newXlogSegNo
, WalSegSz
);
798 printf(_("First log segment after reset: %s\n"), fname
);
802 printf(_("NextMultiXactId: %u\n"),
803 ControlFile
.checkPointCopy
.nextMulti
);
804 printf(_("OldestMultiXid: %u\n"),
805 ControlFile
.checkPointCopy
.oldestMulti
);
806 printf(_("OldestMulti's DB: %u\n"),
807 ControlFile
.checkPointCopy
.oldestMultiDB
);
812 printf(_("NextMultiOffset: %u\n"),
813 ControlFile
.checkPointCopy
.nextMultiOffset
);
818 printf(_("NextOID: %u\n"),
819 ControlFile
.checkPointCopy
.nextOid
);
824 printf(_("NextXID: %u\n"),
825 XidFromFullTransactionId(ControlFile
.checkPointCopy
.nextXid
));
826 printf(_("OldestXID: %u\n"),
827 ControlFile
.checkPointCopy
.oldestXid
);
828 printf(_("OldestXID's DB: %u\n"),
829 ControlFile
.checkPointCopy
.oldestXidDB
);
832 if (set_xid_epoch
!= -1)
834 printf(_("NextXID epoch: %u\n"),
835 EpochFromFullTransactionId(ControlFile
.checkPointCopy
.nextXid
));
838 if (set_oldest_commit_ts_xid
!= 0)
840 printf(_("oldestCommitTsXid: %u\n"),
841 ControlFile
.checkPointCopy
.oldestCommitTsXid
);
843 if (set_newest_commit_ts_xid
!= 0)
845 printf(_("newestCommitTsXid: %u\n"),
846 ControlFile
.checkPointCopy
.newestCommitTsXid
);
849 if (set_wal_segsize
!= 0)
851 printf(_("Bytes per WAL segment: %u\n"),
852 ControlFile
.xlog_seg_size
);
858 * Write out the new pg_control file.
861 RewriteControlFile(void)
864 * Adjust fields as needed to force an empty XLOG starting at
867 XLogSegNoOffsetToRecPtr(newXlogSegNo
, SizeOfXLogLongPHD
, WalSegSz
,
868 ControlFile
.checkPointCopy
.redo
);
869 ControlFile
.checkPointCopy
.time
= (pg_time_t
) time(NULL
);
871 ControlFile
.state
= DB_SHUTDOWNED
;
872 ControlFile
.checkPoint
= ControlFile
.checkPointCopy
.redo
;
873 ControlFile
.minRecoveryPoint
= 0;
874 ControlFile
.minRecoveryPointTLI
= 0;
875 ControlFile
.backupStartPoint
= 0;
876 ControlFile
.backupEndPoint
= 0;
877 ControlFile
.backupEndRequired
= false;
880 * Force the defaults for max_* settings. The values don't really matter
881 * as long as wal_level='minimal'; the postmaster will reset these fields
884 ControlFile
.wal_level
= WAL_LEVEL_MINIMAL
;
885 ControlFile
.wal_log_hints
= false;
886 ControlFile
.track_commit_timestamp
= false;
887 ControlFile
.MaxConnections
= 100;
888 ControlFile
.max_wal_senders
= 10;
889 ControlFile
.max_worker_processes
= 8;
890 ControlFile
.max_prepared_xacts
= 0;
891 ControlFile
.max_locks_per_xact
= 64;
893 /* The control file gets flushed here. */
894 update_controlfile(".", &ControlFile
, true);
899 * Scan existing XLOG files and determine the highest existing WAL address
901 * On entry, ControlFile.checkPointCopy.redo and ControlFile.xlog_seg_size
902 * are assumed valid (note that we allow the old xlog seg size to differ
903 * from what we're using). On exit, newXlogSegNo is set to suitable
904 * value for the beginning of replacement WAL (in our seg size).
914 * Initialize the max() computation using the last checkpoint address from
915 * old pg_control. Note that for the moment we are working with segment
916 * numbering according to the old xlog seg size.
918 XLByteToSeg(ControlFile
.checkPointCopy
.redo
, newXlogSegNo
,
919 ControlFile
.xlog_seg_size
);
922 * Scan the pg_wal directory to find existing WAL segment files. We assume
923 * any present have been used; in most scenarios this should be
924 * conservative, because of xlog.c's attempts to pre-create files.
926 xldir
= opendir(XLOGDIR
);
928 pg_fatal("could not open directory \"%s\": %m", XLOGDIR
);
930 while (errno
= 0, (xlde
= readdir(xldir
)) != NULL
)
932 if (IsXLogFileName(xlde
->d_name
) ||
933 IsPartialXLogFileName(xlde
->d_name
))
938 /* Use the segment size from the control file */
939 XLogFromFileName(xlde
->d_name
, &tli
, &segno
,
940 ControlFile
.xlog_seg_size
);
943 * Note: we take the max of all files found, regardless of their
944 * timelines. Another possibility would be to ignore files of
945 * timelines other than the target TLI, but this seems safer.
946 * Better too large a result than too small...
948 if (segno
> newXlogSegNo
)
949 newXlogSegNo
= segno
;
954 pg_fatal("could not read directory \"%s\": %m", XLOGDIR
);
957 pg_fatal("could not close directory \"%s\": %m", XLOGDIR
);
960 * Finally, convert to new xlog seg size, and advance by one to ensure we
961 * are in virgin territory.
963 xlogbytepos
= newXlogSegNo
* ControlFile
.xlog_seg_size
;
964 newXlogSegNo
= (xlogbytepos
+ ControlFile
.xlog_seg_size
- 1) / WalSegSz
;
970 * Remove existing XLOG files
973 KillExistingXLOG(void)
977 char path
[MAXPGPATH
+ sizeof(XLOGDIR
)];
979 xldir
= opendir(XLOGDIR
);
981 pg_fatal("could not open directory \"%s\": %m", XLOGDIR
);
983 while (errno
= 0, (xlde
= readdir(xldir
)) != NULL
)
985 if (IsXLogFileName(xlde
->d_name
) ||
986 IsPartialXLogFileName(xlde
->d_name
))
988 snprintf(path
, sizeof(path
), "%s/%s", XLOGDIR
, xlde
->d_name
);
989 if (unlink(path
) < 0)
990 pg_fatal("could not delete file \"%s\": %m", path
);
995 pg_fatal("could not read directory \"%s\": %m", XLOGDIR
);
998 pg_fatal("could not close directory \"%s\": %m", XLOGDIR
);
1003 * Remove existing archive status files
1006 KillExistingArchiveStatus(void)
1008 #define ARCHSTATDIR XLOGDIR "/archive_status"
1011 struct dirent
*xlde
;
1012 char path
[MAXPGPATH
+ sizeof(ARCHSTATDIR
)];
1014 xldir
= opendir(ARCHSTATDIR
);
1016 pg_fatal("could not open directory \"%s\": %m", ARCHSTATDIR
);
1018 while (errno
= 0, (xlde
= readdir(xldir
)) != NULL
)
1020 if (strspn(xlde
->d_name
, "0123456789ABCDEF") == XLOG_FNAME_LEN
&&
1021 (strcmp(xlde
->d_name
+ XLOG_FNAME_LEN
, ".ready") == 0 ||
1022 strcmp(xlde
->d_name
+ XLOG_FNAME_LEN
, ".done") == 0 ||
1023 strcmp(xlde
->d_name
+ XLOG_FNAME_LEN
, ".partial.ready") == 0 ||
1024 strcmp(xlde
->d_name
+ XLOG_FNAME_LEN
, ".partial.done") == 0))
1026 snprintf(path
, sizeof(path
), "%s/%s", ARCHSTATDIR
, xlde
->d_name
);
1027 if (unlink(path
) < 0)
1028 pg_fatal("could not delete file \"%s\": %m", path
);
1033 pg_fatal("could not read directory \"%s\": %m", ARCHSTATDIR
);
1035 if (closedir(xldir
))
1036 pg_fatal("could not close directory \"%s\": %m", ARCHSTATDIR
);
1040 * Remove existing WAL summary files
1043 KillExistingWALSummaries(void)
1045 #define WALSUMMARYDIR XLOGDIR "/summaries"
1046 #define WALSUMMARY_NHEXCHARS 40
1049 struct dirent
*xlde
;
1050 char path
[MAXPGPATH
+ sizeof(WALSUMMARYDIR
)];
1052 xldir
= opendir(WALSUMMARYDIR
);
1054 pg_fatal("could not open directory \"%s\": %m", WALSUMMARYDIR
);
1056 while (errno
= 0, (xlde
= readdir(xldir
)) != NULL
)
1058 if (strspn(xlde
->d_name
, "0123456789ABCDEF") == WALSUMMARY_NHEXCHARS
&&
1059 strcmp(xlde
->d_name
+ WALSUMMARY_NHEXCHARS
, ".summary") == 0)
1061 snprintf(path
, sizeof(path
), "%s/%s", WALSUMMARYDIR
, xlde
->d_name
);
1062 if (unlink(path
) < 0)
1063 pg_fatal("could not delete file \"%s\": %m", path
);
1068 pg_fatal("could not read directory \"%s\": %m", WALSUMMARYDIR
);
1070 if (closedir(xldir
))
1071 pg_fatal("could not close directory \"%s\": %m", ARCHSTATDIR
);
1075 * Write an empty XLOG file, containing only the checkpoint record
1076 * already set up in ControlFile.
1079 WriteEmptyXLOG(void)
1081 PGAlignedXLogBlock buffer
;
1082 XLogPageHeader page
;
1083 XLogLongPageHeader longpage
;
1086 char path
[MAXPGPATH
];
1091 memset(buffer
.data
, 0, XLOG_BLCKSZ
);
1093 /* Set up the XLOG page header */
1094 page
= (XLogPageHeader
) buffer
.data
;
1095 page
->xlp_magic
= XLOG_PAGE_MAGIC
;
1096 page
->xlp_info
= XLP_LONG_HEADER
;
1097 page
->xlp_tli
= ControlFile
.checkPointCopy
.ThisTimeLineID
;
1098 page
->xlp_pageaddr
= ControlFile
.checkPointCopy
.redo
- SizeOfXLogLongPHD
;
1099 longpage
= (XLogLongPageHeader
) page
;
1100 longpage
->xlp_sysid
= ControlFile
.system_identifier
;
1101 longpage
->xlp_seg_size
= WalSegSz
;
1102 longpage
->xlp_xlog_blcksz
= XLOG_BLCKSZ
;
1104 /* Insert the initial checkpoint record */
1105 recptr
= (char *) page
+ SizeOfXLogLongPHD
;
1106 record
= (XLogRecord
*) recptr
;
1107 record
->xl_prev
= 0;
1108 record
->xl_xid
= InvalidTransactionId
;
1109 record
->xl_tot_len
= SizeOfXLogRecord
+ SizeOfXLogRecordDataHeaderShort
+ sizeof(CheckPoint
);
1110 record
->xl_info
= XLOG_CHECKPOINT_SHUTDOWN
;
1111 record
->xl_rmid
= RM_XLOG_ID
;
1113 recptr
+= SizeOfXLogRecord
;
1114 *(recptr
++) = (char) XLR_BLOCK_ID_DATA_SHORT
;
1115 *(recptr
++) = sizeof(CheckPoint
);
1116 memcpy(recptr
, &ControlFile
.checkPointCopy
,
1117 sizeof(CheckPoint
));
1120 COMP_CRC32C(crc
, ((char *) record
) + SizeOfXLogRecord
, record
->xl_tot_len
- SizeOfXLogRecord
);
1121 COMP_CRC32C(crc
, (char *) record
, offsetof(XLogRecord
, xl_crc
));
1123 record
->xl_crc
= crc
;
1125 /* Write the first page */
1126 XLogFilePath(path
, ControlFile
.checkPointCopy
.ThisTimeLineID
,
1127 newXlogSegNo
, WalSegSz
);
1131 fd
= open(path
, O_RDWR
| O_CREAT
| O_EXCL
| PG_BINARY
,
1132 pg_file_create_mode
);
1134 pg_fatal("could not open file \"%s\": %m", path
);
1137 if (write(fd
, buffer
.data
, XLOG_BLCKSZ
) != XLOG_BLCKSZ
)
1139 /* if write didn't set errno, assume problem is no disk space */
1142 pg_fatal("could not write file \"%s\": %m", path
);
1145 /* Fill the rest of the file with zeroes */
1146 memset(buffer
.data
, 0, XLOG_BLCKSZ
);
1147 for (nbytes
= XLOG_BLCKSZ
; nbytes
< WalSegSz
; nbytes
+= XLOG_BLCKSZ
)
1150 if (write(fd
, buffer
.data
, XLOG_BLCKSZ
) != XLOG_BLCKSZ
)
1154 pg_fatal("could not write file \"%s\": %m", path
);
1159 pg_fatal("fsync error: %m");
1168 printf(_("%s resets the PostgreSQL write-ahead log.\n\n"), progname
);
1169 printf(_("Usage:\n"));
1170 printf(_(" %s [OPTION]... DATADIR\n"), progname
);
1172 printf(_("\nOptions:\n"));
1173 printf(_(" [-D, --pgdata=]DATADIR data directory\n"));
1174 printf(_(" -f, --force force update to be done even after unclean shutdown or\n"
1175 " if pg_control values had to be guessed\n"));
1176 printf(_(" -n, --dry-run no update, just show what would be done\n"));
1177 printf(_(" -V, --version output version information, then exit\n"));
1178 printf(_(" -?, --help show this help, then exit\n"));
1180 printf(_("\nOptions to override control file values:\n"));
1181 printf(_(" -c, --commit-timestamp-ids=XID,XID\n"
1182 " set oldest and newest transactions bearing\n"
1183 " commit timestamp (zero means no change)\n"));
1184 printf(_(" -e, --epoch=XIDEPOCH set next transaction ID epoch\n"));
1185 printf(_(" -l, --next-wal-file=WALFILE set minimum starting location for new WAL\n"));
1186 printf(_(" -m, --multixact-ids=MXID,MXID set next and oldest multitransaction ID\n"));
1187 printf(_(" -o, --next-oid=OID set next OID\n"));
1188 printf(_(" -O, --multixact-offset=OFFSET set next multitransaction offset\n"));
1189 printf(_(" -u, --oldest-transaction-id=XID set oldest transaction ID\n"));
1190 printf(_(" -x, --next-transaction-id=XID set next transaction ID\n"));
1191 printf(_(" --wal-segsize=SIZE size of WAL segments, in megabytes\n"));
1193 printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT
);
1194 printf(_("%s home page: <%s>\n"), PACKAGE_NAME
, PACKAGE_URL
);