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