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