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