Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_resetwal.c
4 : * A utility to "zero out" the xlog when it's corrupt beyond recovery.
5 : * Can also rebuild pg_control if needed.
6 : *
7 : * The theory of operation is fairly simple:
8 : * 1. Read the existing pg_control (which will include the last
9 : * checkpoint record).
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
19 : * step 2 ...
20 : *
21 : *
22 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
23 : * Portions Copyright (c) 1994, Regents of the University of California
24 : *
25 : * src/bin/pg_resetwal/pg_resetwal.c
26 : *
27 : *-------------------------------------------------------------------------
28 : */
29 :
30 : /*
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.
34 : */
35 : #define FRONTEND 1
36 :
37 : #include "postgres.h"
38 :
39 : #include <dirent.h>
40 : #include <fcntl.h>
41 : #include <sys/stat.h>
42 : #include <sys/time.h>
43 : #include <time.h>
44 : #include <unistd.h>
45 :
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"
61 :
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;
76 : static int WalSegSz;
77 : static int set_wal_segsize;
78 :
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 WriteEmptyXLOG(void);
89 : static void usage(void);
90 :
91 :
92 : int
93 158 : main(int argc, char *argv[])
94 : {
95 : static struct option long_options[] = {
96 : {"commit-timestamp-ids", required_argument, NULL, 'c'},
97 : {"pgdata", required_argument, NULL, 'D'},
98 : {"epoch", required_argument, NULL, 'e'},
99 : {"force", no_argument, NULL, 'f'},
100 : {"next-wal-file", required_argument, NULL, 'l'},
101 : {"multixact-ids", required_argument, NULL, 'm'},
102 : {"dry-run", no_argument, NULL, 'n'},
103 : {"next-oid", required_argument, NULL, 'o'},
104 : {"multixact-offset", required_argument, NULL, 'O'},
105 : {"oldest-transaction-id", required_argument, NULL, 'u'},
106 : {"next-transaction-id", required_argument, NULL, 'x'},
107 : {"wal-segsize", required_argument, NULL, 1},
108 : {NULL, 0, NULL, 0}
109 : };
110 :
111 : int c;
112 158 : bool force = false;
113 158 : bool noupdate = false;
114 158 : MultiXactId set_oldestmxid = 0;
115 : char *endptr;
116 : char *endptr2;
117 158 : char *DataDir = NULL;
118 158 : char *log_fname = NULL;
119 : int fd;
120 :
121 158 : pg_logging_init(argv[0]);
122 158 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_resetwal"));
123 158 : progname = get_progname(argv[0]);
124 :
125 158 : if (argc > 1)
126 : {
127 156 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
128 : {
129 2 : usage();
130 2 : exit(0);
131 : }
132 154 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
133 : {
134 26 : puts("pg_resetwal (PostgreSQL) " PG_VERSION);
135 26 : exit(0);
136 : }
137 : }
138 :
139 :
140 262 : while ((c = getopt_long(argc, argv, "c:D:e:fl:m:no:O:u:x:", long_options, NULL)) != -1)
141 : {
142 176 : switch (c)
143 : {
144 6 : case 'D':
145 6 : DataDir = optarg;
146 6 : break;
147 :
148 20 : case 'f':
149 20 : force = true;
150 20 : break;
151 :
152 36 : case 'n':
153 36 : noupdate = true;
154 36 : break;
155 :
156 14 : case 'e':
157 14 : errno = 0;
158 14 : set_xid_epoch = strtoul(optarg, &endptr, 0);
159 14 : if (endptr == optarg || *endptr != '\0' || errno != 0)
160 : {
161 : /*------
162 : translator: the second %s is a command line argument (-e, etc) */
163 2 : pg_log_error("invalid argument for option %s", "-e");
164 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
165 2 : exit(1);
166 : }
167 12 : if (set_xid_epoch == -1)
168 2 : pg_fatal("transaction ID epoch (-e) must not be -1");
169 10 : break;
170 :
171 12 : case 'u':
172 12 : errno = 0;
173 12 : set_oldest_xid = strtoul(optarg, &endptr, 0);
174 12 : if (endptr == optarg || *endptr != '\0' || errno != 0)
175 : {
176 2 : pg_log_error("invalid argument for option %s", "-u");
177 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
178 2 : exit(1);
179 : }
180 10 : if (!TransactionIdIsNormal(set_oldest_xid))
181 2 : pg_fatal("oldest transaction ID (-u) must be greater than or equal to %u", FirstNormalTransactionId);
182 8 : break;
183 :
184 12 : case 'x':
185 12 : errno = 0;
186 12 : set_xid = strtoul(optarg, &endptr, 0);
187 12 : if (endptr == optarg || *endptr != '\0' || errno != 0)
188 : {
189 2 : pg_log_error("invalid argument for option %s", "-x");
190 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
191 2 : exit(1);
192 : }
193 10 : if (!TransactionIdIsNormal(set_xid))
194 2 : pg_fatal("transaction ID (-x) must be greater than or equal to %u", FirstNormalTransactionId);
195 8 : break;
196 :
197 16 : case 'c':
198 16 : errno = 0;
199 16 : set_oldest_commit_ts_xid = strtoul(optarg, &endptr, 0);
200 16 : if (endptr == optarg || *endptr != ',' || errno != 0)
201 : {
202 2 : pg_log_error("invalid argument for option %s", "-c");
203 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
204 2 : exit(1);
205 : }
206 14 : set_newest_commit_ts_xid = strtoul(endptr + 1, &endptr2, 0);
207 14 : if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
208 : {
209 2 : pg_log_error("invalid argument for option %s", "-c");
210 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
211 2 : exit(1);
212 : }
213 :
214 12 : if (set_oldest_commit_ts_xid < FirstNormalTransactionId &&
215 2 : set_oldest_commit_ts_xid != InvalidTransactionId)
216 2 : pg_fatal("transaction ID (-c) must be either %u or greater than or equal to %u", InvalidTransactionId, FirstNormalTransactionId);
217 :
218 10 : if (set_newest_commit_ts_xid < FirstNormalTransactionId &&
219 6 : set_newest_commit_ts_xid != InvalidTransactionId)
220 2 : pg_fatal("transaction ID (-c) must be either %u or greater than or equal to %u", InvalidTransactionId, FirstNormalTransactionId);
221 8 : break;
222 :
223 12 : case 'o':
224 12 : errno = 0;
225 12 : set_oid = strtoul(optarg, &endptr, 0);
226 12 : if (endptr == optarg || *endptr != '\0' || errno != 0)
227 : {
228 2 : pg_log_error("invalid argument for option %s", "-o");
229 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
230 2 : exit(1);
231 : }
232 10 : if (set_oid == 0)
233 2 : pg_fatal("OID (-o) must not be 0");
234 8 : break;
235 :
236 16 : case 'm':
237 16 : errno = 0;
238 16 : set_mxid = strtoul(optarg, &endptr, 0);
239 16 : if (endptr == optarg || *endptr != ',' || errno != 0)
240 : {
241 2 : pg_log_error("invalid argument for option %s", "-m");
242 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
243 2 : exit(1);
244 : }
245 :
246 14 : set_oldestmxid = strtoul(endptr + 1, &endptr2, 0);
247 14 : if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
248 : {
249 2 : pg_log_error("invalid argument for option %s", "-m");
250 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
251 2 : exit(1);
252 : }
253 12 : if (set_mxid == 0)
254 2 : pg_fatal("multitransaction ID (-m) must not be 0");
255 :
256 : /*
257 : * XXX It'd be nice to have more sanity checks here, e.g. so
258 : * that oldest is not wrapped around w.r.t. nextMulti.
259 : */
260 10 : if (set_oldestmxid == 0)
261 2 : pg_fatal("oldest multitransaction ID (-m) must not be 0");
262 8 : break;
263 :
264 12 : case 'O':
265 12 : errno = 0;
266 12 : set_mxoff = strtoul(optarg, &endptr, 0);
267 12 : if (endptr == optarg || *endptr != '\0' || errno != 0)
268 : {
269 2 : pg_log_error("invalid argument for option %s", "-O");
270 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
271 2 : exit(1);
272 : }
273 10 : if (set_mxoff == -1)
274 2 : pg_fatal("multitransaction offset (-O) must not be -1");
275 8 : break;
276 :
277 10 : case 'l':
278 10 : if (strspn(optarg, "01234567890ABCDEFabcdef") != XLOG_FNAME_LEN)
279 : {
280 2 : pg_log_error("invalid argument for option %s", "-l");
281 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
282 2 : exit(1);
283 : }
284 :
285 : /*
286 : * XLogFromFileName requires wal segment size which is not yet
287 : * set. Hence wal details are set later on.
288 : */
289 8 : log_fname = pg_strdup(optarg);
290 8 : break;
291 :
292 8 : case 1:
293 : {
294 : int wal_segsize_mb;
295 :
296 8 : if (!option_parse_int(optarg, "--wal-segsize", 1, 1024, &wal_segsize_mb))
297 2 : exit(1);
298 6 : set_wal_segsize = wal_segsize_mb * 1024 * 1024;
299 6 : if (!IsValidWalSegSize(set_wal_segsize))
300 2 : pg_fatal("argument of %s must be a power of two between 1 and 1024", "--wal-segsize");
301 4 : break;
302 : }
303 :
304 2 : default:
305 : /* getopt_long already emitted a complaint */
306 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
307 2 : exit(1);
308 : }
309 : }
310 :
311 86 : if (DataDir == NULL && optind < argc)
312 78 : DataDir = argv[optind++];
313 :
314 : /* Complain if any arguments remain */
315 86 : if (optind < argc)
316 : {
317 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
318 : argv[optind]);
319 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
320 2 : exit(1);
321 : }
322 :
323 84 : if (DataDir == NULL)
324 : {
325 2 : pg_log_error("no data directory specified");
326 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
327 2 : exit(1);
328 : }
329 :
330 : /*
331 : * Don't allow pg_resetwal to be run as root, to avoid overwriting the
332 : * ownership of files in the data directory. We need only check for root
333 : * -- any other user won't have sufficient permissions to modify files in
334 : * the data directory.
335 : */
336 : #ifndef WIN32
337 82 : if (geteuid() == 0)
338 : {
339 0 : pg_log_error("cannot be executed by \"root\"");
340 0 : pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
341 : progname);
342 0 : exit(1);
343 : }
344 : #endif
345 :
346 82 : get_restricted_token();
347 :
348 : /* Set mask based on PGDATA permissions */
349 82 : if (!GetDataDirectoryCreatePerm(DataDir))
350 2 : pg_fatal("could not read permissions of directory \"%s\": %m",
351 : DataDir);
352 :
353 80 : umask(pg_mode_mask);
354 :
355 80 : if (chdir(DataDir) < 0)
356 0 : pg_fatal("could not change directory to \"%s\": %m",
357 : DataDir);
358 :
359 : /* Check that data directory matches our server version */
360 80 : CheckDataVersion();
361 :
362 : /*
363 : * Check for a postmaster lock file --- if there is one, refuse to
364 : * proceed, on grounds we might be interfering with a live installation.
365 : */
366 80 : if ((fd = open("postmaster.pid", O_RDONLY, 0)) < 0)
367 : {
368 78 : if (errno != ENOENT)
369 0 : pg_fatal("could not open file \"%s\" for reading: %m",
370 : "postmaster.pid");
371 : }
372 : else
373 : {
374 2 : pg_log_error("lock file \"%s\" exists", "postmaster.pid");
375 2 : pg_log_error_hint("Is a server running? If not, delete the lock file and try again.");
376 2 : exit(1);
377 : }
378 :
379 : /*
380 : * Attempt to read the existing pg_control file
381 : */
382 78 : if (!read_controlfile())
383 8 : GuessControlValues();
384 :
385 : /*
386 : * If no new WAL segment size was specified, use the control file value.
387 : */
388 78 : if (set_wal_segsize != 0)
389 4 : WalSegSz = set_wal_segsize;
390 : else
391 74 : WalSegSz = ControlFile.xlog_seg_size;
392 :
393 78 : if (log_fname != NULL)
394 8 : XLogFromFileName(log_fname, &minXlogTli, &minXlogSegNo, WalSegSz);
395 :
396 : /*
397 : * Also look at existing segment files to set up newXlogSegNo
398 : */
399 78 : FindEndOfXLOG();
400 :
401 : /*
402 : * If we're not going to proceed with the reset, print the current control
403 : * file parameters.
404 : */
405 78 : if ((guessed && !force) || noupdate)
406 38 : PrintControlValues(guessed);
407 :
408 : /*
409 : * Adjust fields if required by switches. (Do this now so that printout,
410 : * if any, includes these values.)
411 : */
412 78 : if (set_xid_epoch != -1)
413 : ControlFile.checkPointCopy.nextXid =
414 10 : FullTransactionIdFromEpochAndXid(set_xid_epoch,
415 10 : XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
416 :
417 78 : if (set_oldest_xid != 0)
418 : {
419 8 : ControlFile.checkPointCopy.oldestXid = set_oldest_xid;
420 8 : ControlFile.checkPointCopy.oldestXidDB = InvalidOid;
421 : }
422 :
423 78 : if (set_xid != 0)
424 : ControlFile.checkPointCopy.nextXid =
425 8 : FullTransactionIdFromEpochAndXid(EpochFromFullTransactionId(ControlFile.checkPointCopy.nextXid),
426 : set_xid);
427 :
428 78 : if (set_oldest_commit_ts_xid != 0)
429 8 : ControlFile.checkPointCopy.oldestCommitTsXid = set_oldest_commit_ts_xid;
430 78 : if (set_newest_commit_ts_xid != 0)
431 4 : ControlFile.checkPointCopy.newestCommitTsXid = set_newest_commit_ts_xid;
432 :
433 78 : if (set_oid != 0)
434 8 : ControlFile.checkPointCopy.nextOid = set_oid;
435 :
436 78 : if (set_mxid != 0)
437 : {
438 8 : ControlFile.checkPointCopy.nextMulti = set_mxid;
439 :
440 8 : ControlFile.checkPointCopy.oldestMulti = set_oldestmxid;
441 8 : if (ControlFile.checkPointCopy.oldestMulti < FirstMultiXactId)
442 0 : ControlFile.checkPointCopy.oldestMulti += FirstMultiXactId;
443 8 : ControlFile.checkPointCopy.oldestMultiDB = InvalidOid;
444 : }
445 :
446 78 : if (set_mxoff != -1)
447 8 : ControlFile.checkPointCopy.nextMultiOffset = set_mxoff;
448 :
449 78 : if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID)
450 : {
451 0 : ControlFile.checkPointCopy.ThisTimeLineID = minXlogTli;
452 0 : ControlFile.checkPointCopy.PrevTimeLineID = minXlogTli;
453 : }
454 :
455 78 : if (set_wal_segsize != 0)
456 4 : ControlFile.xlog_seg_size = WalSegSz;
457 :
458 78 : if (minXlogSegNo > newXlogSegNo)
459 6 : newXlogSegNo = minXlogSegNo;
460 :
461 78 : if (noupdate)
462 : {
463 36 : PrintNewControlValues();
464 36 : exit(0);
465 : }
466 :
467 : /*
468 : * If we had to guess anything, and -f was not given, just print the
469 : * guessed values and exit.
470 : */
471 42 : if (guessed && !force)
472 : {
473 2 : PrintNewControlValues();
474 2 : pg_log_error("not proceeding because control file values were guessed");
475 2 : pg_log_error_hint("If these values seem acceptable, use -f to force reset.");
476 2 : exit(1);
477 : }
478 :
479 : /*
480 : * Don't reset from a dirty pg_control without -f, either.
481 : */
482 40 : if (ControlFile.state != DB_SHUTDOWNED && !force)
483 : {
484 2 : pg_log_error("database server was not shut down cleanly");
485 2 : pg_log_error_detail("Resetting the write-ahead log might cause data to be lost.");
486 2 : pg_log_error_hint("If you want to proceed anyway, use -f to force reset.");
487 2 : exit(1);
488 : }
489 :
490 : /*
491 : * Else, do the dirty deed.
492 : */
493 38 : RewriteControlFile();
494 38 : KillExistingXLOG();
495 38 : KillExistingArchiveStatus();
496 38 : WriteEmptyXLOG();
497 :
498 38 : printf(_("Write-ahead log reset\n"));
499 38 : return 0;
500 : }
501 :
502 :
503 : /*
504 : * Look at the version string stored in PG_VERSION and decide if this utility
505 : * can be run safely or not.
506 : *
507 : * We don't want to inject pg_control and WAL files that are for a different
508 : * major version; that can't do anything good. Note that we don't treat
509 : * mismatching version info in pg_control as a reason to bail out, because
510 : * recovering from a corrupted pg_control is one of the main reasons for this
511 : * program to exist at all. However, PG_VERSION is unlikely to get corrupted,
512 : * and if it were it would be easy to fix by hand. So let's make this check
513 : * to prevent simple user errors.
514 : */
515 : static void
516 80 : CheckDataVersion(void)
517 : {
518 80 : const char *ver_file = "PG_VERSION";
519 : FILE *ver_fd;
520 : char rawline[64];
521 :
522 80 : if ((ver_fd = fopen(ver_file, "r")) == NULL)
523 0 : pg_fatal("could not open file \"%s\" for reading: %m",
524 : ver_file);
525 :
526 : /* version number has to be the first line read */
527 80 : if (!fgets(rawline, sizeof(rawline), ver_fd))
528 : {
529 0 : if (!ferror(ver_fd))
530 0 : pg_fatal("unexpected empty file \"%s\"", ver_file);
531 : else
532 0 : pg_fatal("could not read file \"%s\": %m", ver_file);
533 : }
534 :
535 : /* strip trailing newline and carriage return */
536 80 : (void) pg_strip_crlf(rawline);
537 :
538 80 : if (strcmp(rawline, PG_MAJORVERSION) != 0)
539 : {
540 0 : pg_log_error("data directory is of wrong version");
541 0 : pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
542 : ver_file, rawline, PG_MAJORVERSION);
543 0 : exit(1);
544 : }
545 :
546 80 : fclose(ver_fd);
547 80 : }
548 :
549 :
550 : /*
551 : * Try to read the existing pg_control file.
552 : *
553 : * This routine is also responsible for updating old pg_control versions
554 : * to the current format. (Currently we don't do anything of the sort.)
555 : */
556 : static bool
557 78 : read_controlfile(void)
558 : {
559 : int fd;
560 : int len;
561 : char *buffer;
562 : pg_crc32c crc;
563 :
564 78 : if ((fd = open(XLOG_CONTROL_FILE, O_RDONLY | PG_BINARY, 0)) < 0)
565 : {
566 : /*
567 : * If pg_control is not there at all, or we can't read it, the odds
568 : * are we've been handed a bad DataDir path, so give up. User can do
569 : * "touch pg_control" to force us to proceed.
570 : */
571 0 : pg_log_error("could not open file \"%s\" for reading: %m",
572 : XLOG_CONTROL_FILE);
573 0 : if (errno == ENOENT)
574 0 : pg_log_error_hint("If you are sure the data directory path is correct, execute\n"
575 : " touch %s\n"
576 : "and try again.",
577 : XLOG_CONTROL_FILE);
578 0 : exit(1);
579 : }
580 :
581 : /* Use malloc to ensure we have a maxaligned buffer */
582 78 : buffer = (char *) pg_malloc(PG_CONTROL_FILE_SIZE);
583 :
584 78 : len = read(fd, buffer, PG_CONTROL_FILE_SIZE);
585 78 : if (len < 0)
586 0 : pg_fatal("could not read file \"%s\": %m", XLOG_CONTROL_FILE);
587 78 : close(fd);
588 :
589 78 : if (len >= sizeof(ControlFileData) &&
590 78 : ((ControlFileData *) buffer)->pg_control_version == PG_CONTROL_VERSION)
591 : {
592 : /* Check the CRC. */
593 76 : INIT_CRC32C(crc);
594 76 : COMP_CRC32C(crc,
595 : buffer,
596 : offsetof(ControlFileData, crc));
597 76 : FIN_CRC32C(crc);
598 :
599 76 : if (!EQ_CRC32C(crc, ((ControlFileData *) buffer)->crc))
600 : {
601 : /* We will use the data but treat it as guessed. */
602 6 : pg_log_warning("pg_control exists but has invalid CRC; proceed with caution");
603 6 : guessed = true;
604 : }
605 :
606 76 : memcpy(&ControlFile, buffer, sizeof(ControlFile));
607 :
608 : /* return false if WAL segment size is not valid */
609 76 : if (!IsValidWalSegSize(ControlFile.xlog_seg_size))
610 : {
611 6 : pg_log_warning(ngettext("pg_control specifies invalid WAL segment size (%d byte); proceed with caution",
612 : "pg_control specifies invalid WAL segment size (%d bytes); proceed with caution",
613 : ControlFile.xlog_seg_size),
614 : ControlFile.xlog_seg_size);
615 6 : return false;
616 : }
617 :
618 70 : return true;
619 : }
620 :
621 : /* Looks like it's a mess. */
622 2 : pg_log_warning("pg_control exists but is broken or wrong version; ignoring it");
623 2 : return false;
624 : }
625 :
626 :
627 : /*
628 : * Guess at pg_control values when we can't read the old ones.
629 : */
630 : static void
631 8 : GuessControlValues(void)
632 : {
633 : uint64 sysidentifier;
634 : struct timeval tv;
635 :
636 : /*
637 : * Set up a completely default set of pg_control values.
638 : */
639 8 : guessed = true;
640 8 : memset(&ControlFile, 0, sizeof(ControlFile));
641 :
642 8 : ControlFile.pg_control_version = PG_CONTROL_VERSION;
643 8 : ControlFile.catalog_version_no = CATALOG_VERSION_NO;
644 :
645 : /*
646 : * Create a new unique installation identifier, since we can no longer use
647 : * any old XLOG records. See notes in xlog.c about the algorithm.
648 : */
649 8 : gettimeofday(&tv, NULL);
650 8 : sysidentifier = ((uint64) tv.tv_sec) << 32;
651 8 : sysidentifier |= ((uint64) tv.tv_usec) << 12;
652 8 : sysidentifier |= getpid() & 0xFFF;
653 :
654 8 : ControlFile.system_identifier = sysidentifier;
655 :
656 8 : ControlFile.checkPointCopy.redo = SizeOfXLogLongPHD;
657 8 : ControlFile.checkPointCopy.ThisTimeLineID = 1;
658 8 : ControlFile.checkPointCopy.PrevTimeLineID = 1;
659 8 : ControlFile.checkPointCopy.fullPageWrites = false;
660 : ControlFile.checkPointCopy.nextXid =
661 8 : FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId);
662 8 : ControlFile.checkPointCopy.nextOid = FirstGenbkiObjectId;
663 8 : ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
664 8 : ControlFile.checkPointCopy.nextMultiOffset = 0;
665 8 : ControlFile.checkPointCopy.oldestXid = FirstNormalTransactionId;
666 8 : ControlFile.checkPointCopy.oldestXidDB = InvalidOid;
667 8 : ControlFile.checkPointCopy.oldestMulti = FirstMultiXactId;
668 8 : ControlFile.checkPointCopy.oldestMultiDB = InvalidOid;
669 8 : ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
670 8 : ControlFile.checkPointCopy.oldestActiveXid = InvalidTransactionId;
671 :
672 8 : ControlFile.state = DB_SHUTDOWNED;
673 8 : ControlFile.time = (pg_time_t) time(NULL);
674 8 : ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
675 8 : ControlFile.unloggedLSN = FirstNormalUnloggedLSN;
676 :
677 : /* minRecoveryPoint, backupStartPoint and backupEndPoint can be left zero */
678 :
679 8 : ControlFile.wal_level = WAL_LEVEL_MINIMAL;
680 8 : ControlFile.wal_log_hints = false;
681 8 : ControlFile.track_commit_timestamp = false;
682 8 : ControlFile.MaxConnections = 100;
683 8 : ControlFile.max_wal_senders = 10;
684 8 : ControlFile.max_worker_processes = 8;
685 8 : ControlFile.max_prepared_xacts = 0;
686 8 : ControlFile.max_locks_per_xact = 64;
687 :
688 8 : ControlFile.maxAlign = MAXIMUM_ALIGNOF;
689 8 : ControlFile.floatFormat = FLOATFORMAT_VALUE;
690 8 : ControlFile.blcksz = BLCKSZ;
691 8 : ControlFile.relseg_size = RELSEG_SIZE;
692 8 : ControlFile.xlog_blcksz = XLOG_BLCKSZ;
693 8 : ControlFile.xlog_seg_size = DEFAULT_XLOG_SEG_SIZE;
694 8 : ControlFile.nameDataLen = NAMEDATALEN;
695 8 : ControlFile.indexMaxKeys = INDEX_MAX_KEYS;
696 8 : ControlFile.toast_max_chunk_size = TOAST_MAX_CHUNK_SIZE;
697 8 : ControlFile.loblksize = LOBLKSIZE;
698 8 : ControlFile.float8ByVal = FLOAT8PASSBYVAL;
699 :
700 : /*
701 : * XXX eventually, should try to grovel through old XLOG to develop more
702 : * accurate values for TimeLineID, nextXID, etc.
703 : */
704 8 : }
705 :
706 :
707 : /*
708 : * Print the guessed pg_control values when we had to guess.
709 : *
710 : * NB: this display should be just those fields that will not be
711 : * reset by RewriteControlFile().
712 : */
713 : static void
714 38 : PrintControlValues(bool guessed)
715 : {
716 38 : if (guessed)
717 6 : printf(_("Guessed pg_control values:\n\n"));
718 : else
719 32 : printf(_("Current pg_control values:\n\n"));
720 :
721 38 : printf(_("pg_control version number: %u\n"),
722 : ControlFile.pg_control_version);
723 38 : printf(_("Catalog version number: %u\n"),
724 : ControlFile.catalog_version_no);
725 38 : printf(_("Database system identifier: %llu\n"),
726 : (unsigned long long) ControlFile.system_identifier);
727 38 : printf(_("Latest checkpoint's TimeLineID: %u\n"),
728 : ControlFile.checkPointCopy.ThisTimeLineID);
729 38 : printf(_("Latest checkpoint's full_page_writes: %s\n"),
730 : ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
731 38 : printf(_("Latest checkpoint's NextXID: %u:%u\n"),
732 : EpochFromFullTransactionId(ControlFile.checkPointCopy.nextXid),
733 : XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
734 38 : printf(_("Latest checkpoint's NextOID: %u\n"),
735 : ControlFile.checkPointCopy.nextOid);
736 38 : printf(_("Latest checkpoint's NextMultiXactId: %u\n"),
737 : ControlFile.checkPointCopy.nextMulti);
738 38 : printf(_("Latest checkpoint's NextMultiOffset: %u\n"),
739 : ControlFile.checkPointCopy.nextMultiOffset);
740 38 : printf(_("Latest checkpoint's oldestXID: %u\n"),
741 : ControlFile.checkPointCopy.oldestXid);
742 38 : printf(_("Latest checkpoint's oldestXID's DB: %u\n"),
743 : ControlFile.checkPointCopy.oldestXidDB);
744 38 : printf(_("Latest checkpoint's oldestActiveXID: %u\n"),
745 : ControlFile.checkPointCopy.oldestActiveXid);
746 38 : printf(_("Latest checkpoint's oldestMultiXid: %u\n"),
747 : ControlFile.checkPointCopy.oldestMulti);
748 38 : printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
749 : ControlFile.checkPointCopy.oldestMultiDB);
750 38 : printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"),
751 : ControlFile.checkPointCopy.oldestCommitTsXid);
752 38 : printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
753 : ControlFile.checkPointCopy.newestCommitTsXid);
754 38 : printf(_("Maximum data alignment: %u\n"),
755 : ControlFile.maxAlign);
756 : /* we don't print floatFormat since can't say much useful about it */
757 38 : printf(_("Database block size: %u\n"),
758 : ControlFile.blcksz);
759 38 : printf(_("Blocks per segment of large relation: %u\n"),
760 : ControlFile.relseg_size);
761 38 : printf(_("WAL block size: %u\n"),
762 : ControlFile.xlog_blcksz);
763 38 : printf(_("Bytes per WAL segment: %u\n"),
764 : ControlFile.xlog_seg_size);
765 38 : printf(_("Maximum length of identifiers: %u\n"),
766 : ControlFile.nameDataLen);
767 38 : printf(_("Maximum columns in an index: %u\n"),
768 : ControlFile.indexMaxKeys);
769 38 : printf(_("Maximum size of a TOAST chunk: %u\n"),
770 : ControlFile.toast_max_chunk_size);
771 38 : printf(_("Size of a large-object chunk: %u\n"),
772 : ControlFile.loblksize);
773 : /* This is no longer configurable, but users may still expect to see it: */
774 38 : printf(_("Date/time type storage: %s\n"),
775 : _("64-bit integers"));
776 38 : printf(_("Float8 argument passing: %s\n"),
777 : (ControlFile.float8ByVal ? _("by value") : _("by reference")));
778 38 : printf(_("Data page checksum version: %u\n"),
779 : ControlFile.data_checksum_version);
780 38 : }
781 :
782 :
783 : /*
784 : * Print the values to be changed.
785 : */
786 : static void
787 38 : PrintNewControlValues(void)
788 : {
789 : char fname[MAXFNAMELEN];
790 :
791 : /* This will be always printed in order to keep format same. */
792 38 : printf(_("\n\nValues to be changed:\n\n"));
793 :
794 38 : XLogFileName(fname, ControlFile.checkPointCopy.ThisTimeLineID,
795 : newXlogSegNo, WalSegSz);
796 38 : printf(_("First log segment after reset: %s\n"), fname);
797 :
798 38 : if (set_mxid != 0)
799 : {
800 2 : printf(_("NextMultiXactId: %u\n"),
801 : ControlFile.checkPointCopy.nextMulti);
802 2 : printf(_("OldestMultiXid: %u\n"),
803 : ControlFile.checkPointCopy.oldestMulti);
804 2 : printf(_("OldestMulti's DB: %u\n"),
805 : ControlFile.checkPointCopy.oldestMultiDB);
806 : }
807 :
808 38 : if (set_mxoff != -1)
809 : {
810 2 : printf(_("NextMultiOffset: %u\n"),
811 : ControlFile.checkPointCopy.nextMultiOffset);
812 : }
813 :
814 38 : if (set_oid != 0)
815 : {
816 2 : printf(_("NextOID: %u\n"),
817 : ControlFile.checkPointCopy.nextOid);
818 : }
819 :
820 38 : if (set_xid != 0)
821 : {
822 2 : printf(_("NextXID: %u\n"),
823 : XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
824 2 : printf(_("OldestXID: %u\n"),
825 : ControlFile.checkPointCopy.oldestXid);
826 2 : printf(_("OldestXID's DB: %u\n"),
827 : ControlFile.checkPointCopy.oldestXidDB);
828 : }
829 :
830 38 : if (set_xid_epoch != -1)
831 : {
832 2 : printf(_("NextXID epoch: %u\n"),
833 : EpochFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
834 : }
835 :
836 38 : if (set_oldest_commit_ts_xid != 0)
837 : {
838 2 : printf(_("oldestCommitTsXid: %u\n"),
839 : ControlFile.checkPointCopy.oldestCommitTsXid);
840 : }
841 38 : if (set_newest_commit_ts_xid != 0)
842 : {
843 0 : printf(_("newestCommitTsXid: %u\n"),
844 : ControlFile.checkPointCopy.newestCommitTsXid);
845 : }
846 :
847 38 : if (set_wal_segsize != 0)
848 : {
849 2 : printf(_("Bytes per WAL segment: %u\n"),
850 : ControlFile.xlog_seg_size);
851 : }
852 38 : }
853 :
854 :
855 : /*
856 : * Write out the new pg_control file.
857 : */
858 : static void
859 38 : RewriteControlFile(void)
860 : {
861 : /*
862 : * Adjust fields as needed to force an empty XLOG starting at
863 : * newXlogSegNo.
864 : */
865 38 : XLogSegNoOffsetToRecPtr(newXlogSegNo, SizeOfXLogLongPHD, WalSegSz,
866 : ControlFile.checkPointCopy.redo);
867 38 : ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
868 :
869 38 : ControlFile.state = DB_SHUTDOWNED;
870 38 : ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
871 38 : ControlFile.minRecoveryPoint = 0;
872 38 : ControlFile.minRecoveryPointTLI = 0;
873 38 : ControlFile.backupStartPoint = 0;
874 38 : ControlFile.backupEndPoint = 0;
875 38 : ControlFile.backupEndRequired = false;
876 :
877 : /*
878 : * Force the defaults for max_* settings. The values don't really matter
879 : * as long as wal_level='minimal'; the postmaster will reset these fields
880 : * anyway at startup.
881 : */
882 38 : ControlFile.wal_level = WAL_LEVEL_MINIMAL;
883 38 : ControlFile.wal_log_hints = false;
884 38 : ControlFile.track_commit_timestamp = false;
885 38 : ControlFile.MaxConnections = 100;
886 38 : ControlFile.max_wal_senders = 10;
887 38 : ControlFile.max_worker_processes = 8;
888 38 : ControlFile.max_prepared_xacts = 0;
889 38 : ControlFile.max_locks_per_xact = 64;
890 :
891 : /* The control file gets flushed here. */
892 38 : update_controlfile(".", &ControlFile, true);
893 38 : }
894 :
895 :
896 : /*
897 : * Scan existing XLOG files and determine the highest existing WAL address
898 : *
899 : * On entry, ControlFile.checkPointCopy.redo and ControlFile.xlog_seg_size
900 : * are assumed valid (note that we allow the old xlog seg size to differ
901 : * from what we're using). On exit, newXlogSegNo is set to suitable
902 : * value for the beginning of replacement WAL (in our seg size).
903 : */
904 : static void
905 78 : FindEndOfXLOG(void)
906 : {
907 : DIR *xldir;
908 : struct dirent *xlde;
909 : uint64 xlogbytepos;
910 :
911 : /*
912 : * Initialize the max() computation using the last checkpoint address from
913 : * old pg_control. Note that for the moment we are working with segment
914 : * numbering according to the old xlog seg size.
915 : */
916 78 : XLByteToSeg(ControlFile.checkPointCopy.redo, newXlogSegNo,
917 : ControlFile.xlog_seg_size);
918 :
919 : /*
920 : * Scan the pg_wal directory to find existing WAL segment files. We assume
921 : * any present have been used; in most scenarios this should be
922 : * conservative, because of xlog.c's attempts to pre-create files.
923 : */
924 78 : xldir = opendir(XLOGDIR);
925 78 : if (xldir == NULL)
926 0 : pg_fatal("could not open directory \"%s\": %m", XLOGDIR);
927 :
928 766 : while (errno = 0, (xlde = readdir(xldir)) != NULL)
929 : {
930 922 : if (IsXLogFileName(xlde->d_name) ||
931 234 : IsPartialXLogFileName(xlde->d_name))
932 : {
933 : TimeLineID tli;
934 : XLogSegNo segno;
935 :
936 : /* Use the segment size from the control file */
937 454 : XLogFromFileName(xlde->d_name, &tli, &segno,
938 454 : ControlFile.xlog_seg_size);
939 :
940 : /*
941 : * Note: we take the max of all files found, regardless of their
942 : * timelines. Another possibility would be to ignore files of
943 : * timelines other than the target TLI, but this seems safer.
944 : * Better too large a result than too small...
945 : */
946 454 : if (segno > newXlogSegNo)
947 44 : newXlogSegNo = segno;
948 : }
949 : }
950 :
951 78 : if (errno)
952 0 : pg_fatal("could not read directory \"%s\": %m", XLOGDIR);
953 :
954 78 : if (closedir(xldir))
955 0 : pg_fatal("could not close directory \"%s\": %m", XLOGDIR);
956 :
957 : /*
958 : * Finally, convert to new xlog seg size, and advance by one to ensure we
959 : * are in virgin territory.
960 : */
961 78 : xlogbytepos = newXlogSegNo * ControlFile.xlog_seg_size;
962 78 : newXlogSegNo = (xlogbytepos + ControlFile.xlog_seg_size - 1) / WalSegSz;
963 78 : newXlogSegNo++;
964 78 : }
965 :
966 :
967 : /*
968 : * Remove existing XLOG files
969 : */
970 : static void
971 38 : KillExistingXLOG(void)
972 : {
973 : DIR *xldir;
974 : struct dirent *xlde;
975 : char path[MAXPGPATH + sizeof(XLOGDIR)];
976 :
977 38 : xldir = opendir(XLOGDIR);
978 38 : if (xldir == NULL)
979 0 : pg_fatal("could not open directory \"%s\": %m", XLOGDIR);
980 :
981 296 : while (errno = 0, (xlde = readdir(xldir)) != NULL)
982 : {
983 372 : if (IsXLogFileName(xlde->d_name) ||
984 114 : IsPartialXLogFileName(xlde->d_name))
985 : {
986 144 : snprintf(path, sizeof(path), "%s/%s", XLOGDIR, xlde->d_name);
987 144 : if (unlink(path) < 0)
988 0 : pg_fatal("could not delete file \"%s\": %m", path);
989 : }
990 : }
991 :
992 38 : if (errno)
993 0 : pg_fatal("could not read directory \"%s\": %m", XLOGDIR);
994 :
995 38 : if (closedir(xldir))
996 0 : pg_fatal("could not close directory \"%s\": %m", XLOGDIR);
997 38 : }
998 :
999 :
1000 : /*
1001 : * Remove existing archive status files
1002 : */
1003 : static void
1004 38 : KillExistingArchiveStatus(void)
1005 : {
1006 : #define ARCHSTATDIR XLOGDIR "/archive_status"
1007 :
1008 : DIR *xldir;
1009 : struct dirent *xlde;
1010 : char path[MAXPGPATH + sizeof(ARCHSTATDIR)];
1011 :
1012 38 : xldir = opendir(ARCHSTATDIR);
1013 38 : if (xldir == NULL)
1014 0 : pg_fatal("could not open directory \"%s\": %m", ARCHSTATDIR);
1015 :
1016 114 : while (errno = 0, (xlde = readdir(xldir)) != NULL)
1017 : {
1018 76 : if (strspn(xlde->d_name, "0123456789ABCDEF") == XLOG_FNAME_LEN &&
1019 0 : (strcmp(xlde->d_name + XLOG_FNAME_LEN, ".ready") == 0 ||
1020 0 : strcmp(xlde->d_name + XLOG_FNAME_LEN, ".done") == 0 ||
1021 0 : strcmp(xlde->d_name + XLOG_FNAME_LEN, ".partial.ready") == 0 ||
1022 0 : strcmp(xlde->d_name + XLOG_FNAME_LEN, ".partial.done") == 0))
1023 : {
1024 0 : snprintf(path, sizeof(path), "%s/%s", ARCHSTATDIR, xlde->d_name);
1025 0 : if (unlink(path) < 0)
1026 0 : pg_fatal("could not delete file \"%s\": %m", path);
1027 : }
1028 : }
1029 :
1030 38 : if (errno)
1031 0 : pg_fatal("could not read directory \"%s\": %m", ARCHSTATDIR);
1032 :
1033 38 : if (closedir(xldir))
1034 0 : pg_fatal("could not close directory \"%s\": %m", ARCHSTATDIR);
1035 38 : }
1036 :
1037 :
1038 : /*
1039 : * Write an empty XLOG file, containing only the checkpoint record
1040 : * already set up in ControlFile.
1041 : */
1042 : static void
1043 38 : WriteEmptyXLOG(void)
1044 : {
1045 : PGAlignedXLogBlock buffer;
1046 : XLogPageHeader page;
1047 : XLogLongPageHeader longpage;
1048 : XLogRecord *record;
1049 : pg_crc32c crc;
1050 : char path[MAXPGPATH];
1051 : int fd;
1052 : int nbytes;
1053 : char *recptr;
1054 :
1055 38 : memset(buffer.data, 0, XLOG_BLCKSZ);
1056 :
1057 : /* Set up the XLOG page header */
1058 38 : page = (XLogPageHeader) buffer.data;
1059 38 : page->xlp_magic = XLOG_PAGE_MAGIC;
1060 38 : page->xlp_info = XLP_LONG_HEADER;
1061 38 : page->xlp_tli = ControlFile.checkPointCopy.ThisTimeLineID;
1062 38 : page->xlp_pageaddr = ControlFile.checkPointCopy.redo - SizeOfXLogLongPHD;
1063 38 : longpage = (XLogLongPageHeader) page;
1064 38 : longpage->xlp_sysid = ControlFile.system_identifier;
1065 38 : longpage->xlp_seg_size = WalSegSz;
1066 38 : longpage->xlp_xlog_blcksz = XLOG_BLCKSZ;
1067 :
1068 : /* Insert the initial checkpoint record */
1069 38 : recptr = (char *) page + SizeOfXLogLongPHD;
1070 38 : record = (XLogRecord *) recptr;
1071 38 : record->xl_prev = 0;
1072 38 : record->xl_xid = InvalidTransactionId;
1073 38 : record->xl_tot_len = SizeOfXLogRecord + SizeOfXLogRecordDataHeaderShort + sizeof(CheckPoint);
1074 38 : record->xl_info = XLOG_CHECKPOINT_SHUTDOWN;
1075 38 : record->xl_rmid = RM_XLOG_ID;
1076 :
1077 38 : recptr += SizeOfXLogRecord;
1078 38 : *(recptr++) = (char) XLR_BLOCK_ID_DATA_SHORT;
1079 38 : *(recptr++) = sizeof(CheckPoint);
1080 38 : memcpy(recptr, &ControlFile.checkPointCopy,
1081 : sizeof(CheckPoint));
1082 :
1083 38 : INIT_CRC32C(crc);
1084 38 : COMP_CRC32C(crc, ((char *) record) + SizeOfXLogRecord, record->xl_tot_len - SizeOfXLogRecord);
1085 38 : COMP_CRC32C(crc, (char *) record, offsetof(XLogRecord, xl_crc));
1086 38 : FIN_CRC32C(crc);
1087 38 : record->xl_crc = crc;
1088 :
1089 : /* Write the first page */
1090 38 : XLogFilePath(path, ControlFile.checkPointCopy.ThisTimeLineID,
1091 : newXlogSegNo, WalSegSz);
1092 :
1093 38 : unlink(path);
1094 :
1095 38 : fd = open(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
1096 : pg_file_create_mode);
1097 38 : if (fd < 0)
1098 0 : pg_fatal("could not open file \"%s\": %m", path);
1099 :
1100 38 : errno = 0;
1101 38 : if (write(fd, buffer.data, XLOG_BLCKSZ) != XLOG_BLCKSZ)
1102 : {
1103 : /* if write didn't set errno, assume problem is no disk space */
1104 0 : if (errno == 0)
1105 0 : errno = ENOSPC;
1106 0 : pg_fatal("could not write file \"%s\": %m", path);
1107 : }
1108 :
1109 : /* Fill the rest of the file with zeroes */
1110 38 : memset(buffer.data, 0, XLOG_BLCKSZ);
1111 47104 : for (nbytes = XLOG_BLCKSZ; nbytes < WalSegSz; nbytes += XLOG_BLCKSZ)
1112 : {
1113 47066 : errno = 0;
1114 47066 : if (write(fd, buffer.data, XLOG_BLCKSZ) != XLOG_BLCKSZ)
1115 : {
1116 0 : if (errno == 0)
1117 0 : errno = ENOSPC;
1118 0 : pg_fatal("could not write file \"%s\": %m", path);
1119 : }
1120 : }
1121 :
1122 38 : if (fsync(fd) != 0)
1123 0 : pg_fatal("fsync error: %m");
1124 :
1125 38 : close(fd);
1126 38 : }
1127 :
1128 :
1129 : static void
1130 2 : usage(void)
1131 : {
1132 2 : printf(_("%s resets the PostgreSQL write-ahead log.\n\n"), progname);
1133 2 : printf(_("Usage:\n"));
1134 2 : printf(_(" %s [OPTION]... DATADIR\n"), progname);
1135 :
1136 2 : printf(_("\nOptions:\n"));
1137 2 : printf(_(" [-D, --pgdata=]DATADIR data directory\n"));
1138 2 : printf(_(" -f, --force force update to be done even after unclean shutdown or\n"
1139 : " if pg_control values had to be guessed\n"));
1140 2 : printf(_(" -n, --dry-run no update, just show what would be done\n"));
1141 2 : printf(_(" -V, --version output version information, then exit\n"));
1142 2 : printf(_(" -?, --help show this help, then exit\n"));
1143 :
1144 2 : printf(_("\nOptions to override control file values:\n"));
1145 2 : printf(_(" -c, --commit-timestamp-ids=XID,XID\n"
1146 : " set oldest and newest transactions bearing\n"
1147 : " commit timestamp (zero means no change)\n"));
1148 2 : printf(_(" -e, --epoch=XIDEPOCH set next transaction ID epoch\n"));
1149 2 : printf(_(" -l, --next-wal-file=WALFILE set minimum starting location for new WAL\n"));
1150 2 : printf(_(" -m, --multixact-ids=MXID,MXID set next and oldest multitransaction ID\n"));
1151 2 : printf(_(" -o, --next-oid=OID set next OID\n"));
1152 2 : printf(_(" -O, --multixact-offset=OFFSET set next multitransaction offset\n"));
1153 2 : printf(_(" -u, --oldest-transaction-id=XID set oldest transaction ID\n"));
1154 2 : printf(_(" -x, --next-transaction-id=XID set next transaction ID\n"));
1155 2 : printf(_(" --wal-segsize=SIZE size of WAL segments, in megabytes\n"));
1156 :
1157 2 : printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1158 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1159 2 : }
|