]> granicus.if.org Git - postgresql/blob - src/bin/pg_resetxlog/pg_resetxlog.c
Add GUC to enable WAL-logging of hint bits, even with checksums disabled.
[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-2013, 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 #ifdef HAVE_GETOPT_H
48 #include <getopt.h>
49 #endif
50
51 #include "access/transam.h"
52 #include "access/tuptoaster.h"
53 #include "access/multixact.h"
54 #include "access/xlog_internal.h"
55 #include "catalog/catversion.h"
56 #include "catalog/pg_control.h"
57 #include "common/fe_memutils.h"
58
59 extern int      optind;
60 extern char *optarg;
61
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_xid = 0;
69 static Oid      set_oid = 0;
70 static MultiXactId set_mxid = 0;
71 static MultiXactOffset set_mxoff = (MultiXactOffset) -1;
72 static uint32 minXlogTli = 0;
73 static XLogSegNo minXlogSegNo = 0;
74
75 static bool ReadControlFile(void);
76 static void GuessControlValues(void);
77 static void PrintControlValues(bool guessed);
78 static void PrintNewControlValues(void);
79 static void RewriteControlFile(void);
80 static void FindEndOfXLOG(void);
81 static void KillExistingXLOG(void);
82 static void KillExistingArchiveStatus(void);
83 static void WriteEmptyXLOG(void);
84 static void usage(void);
85
86
87 int
88 main(int argc, char *argv[])
89 {
90         int                     c;
91         bool            force = false;
92         bool            noupdate = false;
93         MultiXactId set_oldestmxid = 0;
94         char       *endptr;
95         char       *endptr2;
96         char       *DataDir;
97         int                     fd;
98
99         set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_resetxlog"));
100
101         progname = get_progname(argv[0]);
102
103         if (argc > 1)
104         {
105                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
106                 {
107                         usage();
108                         exit(0);
109                 }
110                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
111                 {
112                         puts("pg_resetxlog (PostgreSQL) " PG_VERSION);
113                         exit(0);
114                 }
115         }
116
117
118         while ((c = getopt(argc, argv, "fl:m:no:O:x:e:")) != -1)
119         {
120                 switch (c)
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 (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
247         /*
248          * Don't allow pg_resetxlog to be run as root, to avoid overwriting the
249          * ownership of files in the data directory. We need only check for root
250          * -- any other user won't have sufficient permissions to modify files in
251          * the data directory.
252          */
253 #ifndef WIN32
254         if (geteuid() == 0)
255         {
256                 fprintf(stderr, _("%s: cannot be executed by \"root\"\n"),
257                                 progname);
258                 fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
259                                 progname);
260                 exit(1);
261         }
262 #endif
263
264         DataDir = argv[optind];
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 |= (uint32) (tv.tv_sec | tv.tv_usec);
501
502         ControlFile.system_identifier = sysidentifier;
503
504         ControlFile.checkPointCopy.redo = SizeOfXLogLongPHD;
505         ControlFile.checkPointCopy.ThisTimeLineID = 1;
506         ControlFile.checkPointCopy.PrevTimeLineID = 1;
507         ControlFile.checkPointCopy.fullPageWrites = false;
508         ControlFile.checkPointCopy.nextXidEpoch = 0;
509         ControlFile.checkPointCopy.nextXid = FirstNormalTransactionId;
510         ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId;
511         ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
512         ControlFile.checkPointCopy.nextMultiOffset = 0;
513         ControlFile.checkPointCopy.oldestXid = FirstNormalTransactionId;
514         ControlFile.checkPointCopy.oldestXidDB = InvalidOid;
515         ControlFile.checkPointCopy.oldestMulti = FirstMultiXactId;
516         ControlFile.checkPointCopy.oldestMultiDB = InvalidOid;
517         ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
518         ControlFile.checkPointCopy.oldestActiveXid = InvalidTransactionId;
519
520         ControlFile.state = DB_SHUTDOWNED;
521         ControlFile.time = (pg_time_t) time(NULL);
522         ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
523         ControlFile.unloggedLSN = 1;
524
525         /* minRecoveryPoint, backupStartPoint and backupEndPoint can be left zero */
526
527         ControlFile.wal_level = WAL_LEVEL_MINIMAL;
528         ControlFile.wal_log_hintbits = false;
529         ControlFile.MaxConnections = 100;
530         ControlFile.max_worker_processes = 8;
531         ControlFile.max_prepared_xacts = 0;
532         ControlFile.max_locks_per_xact = 64;
533
534         ControlFile.maxAlign = MAXIMUM_ALIGNOF;
535         ControlFile.floatFormat = FLOATFORMAT_VALUE;
536         ControlFile.blcksz = BLCKSZ;
537         ControlFile.relseg_size = RELSEG_SIZE;
538         ControlFile.xlog_blcksz = XLOG_BLCKSZ;
539         ControlFile.xlog_seg_size = XLOG_SEG_SIZE;
540         ControlFile.nameDataLen = NAMEDATALEN;
541         ControlFile.indexMaxKeys = INDEX_MAX_KEYS;
542         ControlFile.toast_max_chunk_size = TOAST_MAX_CHUNK_SIZE;
543 #ifdef HAVE_INT64_TIMESTAMP
544         ControlFile.enableIntTimes = true;
545 #else
546         ControlFile.enableIntTimes = false;
547 #endif
548         ControlFile.float4ByVal = FLOAT4PASSBYVAL;
549         ControlFile.float8ByVal = FLOAT8PASSBYVAL;
550
551         /*
552          * XXX eventually, should try to grovel through old XLOG to develop more
553          * accurate values for TimeLineID, nextXID, etc.
554          */
555 }
556
557
558 /*
559  * Print the guessed pg_control values when we had to guess.
560  *
561  * NB: this display should be just those fields that will not be
562  * reset by RewriteControlFile().
563  */
564 static void
565 PrintControlValues(bool guessed)
566 {
567         char            sysident_str[32];
568
569         if (guessed)
570                 printf(_("Guessed pg_control values:\n\n"));
571         else
572                 printf(_("Current pg_control values:\n\n"));
573
574         /*
575          * Format system_identifier separately to keep platform-dependent format
576          * code out of the translatable message string.
577          */
578         snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
579                          ControlFile.system_identifier);
580
581         printf(_("pg_control version number:            %u\n"),
582                    ControlFile.pg_control_version);
583         printf(_("Catalog version number:               %u\n"),
584                    ControlFile.catalog_version_no);
585         printf(_("Database system identifier:           %s\n"),
586                    sysident_str);
587         printf(_("Latest checkpoint's TimeLineID:       %u\n"),
588                    ControlFile.checkPointCopy.ThisTimeLineID);
589         printf(_("Latest checkpoint's full_page_writes: %s\n"),
590                    ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
591         printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
592                    ControlFile.checkPointCopy.nextXidEpoch,
593                    ControlFile.checkPointCopy.nextXid);
594         printf(_("Latest checkpoint's NextOID:          %u\n"),
595                    ControlFile.checkPointCopy.nextOid);
596         printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
597                    ControlFile.checkPointCopy.nextMulti);
598         printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
599                    ControlFile.checkPointCopy.nextMultiOffset);
600         printf(_("Latest checkpoint's oldestXID:        %u\n"),
601                    ControlFile.checkPointCopy.oldestXid);
602         printf(_("Latest checkpoint's oldestXID's DB:   %u\n"),
603                    ControlFile.checkPointCopy.oldestXidDB);
604         printf(_("Latest checkpoint's oldestActiveXID:  %u\n"),
605                    ControlFile.checkPointCopy.oldestActiveXid);
606         printf(_("Latest checkpoint's oldestMultiXid:   %u\n"),
607                    ControlFile.checkPointCopy.oldestMulti);
608         printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
609                    ControlFile.checkPointCopy.oldestMultiDB);
610         printf(_("Maximum data alignment:               %u\n"),
611                    ControlFile.maxAlign);
612         /* we don't print floatFormat since can't say much useful about it */
613         printf(_("Database block size:                  %u\n"),
614                    ControlFile.blcksz);
615         printf(_("Blocks per segment of large relation: %u\n"),
616                    ControlFile.relseg_size);
617         printf(_("WAL block size:                       %u\n"),
618                    ControlFile.xlog_blcksz);
619         printf(_("Bytes per WAL segment:                %u\n"),
620                    ControlFile.xlog_seg_size);
621         printf(_("Maximum length of identifiers:        %u\n"),
622                    ControlFile.nameDataLen);
623         printf(_("Maximum columns in an index:          %u\n"),
624                    ControlFile.indexMaxKeys);
625         printf(_("Maximum size of a TOAST chunk:        %u\n"),
626                    ControlFile.toast_max_chunk_size);
627         printf(_("Date/time type storage:               %s\n"),
628                    (ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
629         printf(_("Float4 argument passing:              %s\n"),
630                    (ControlFile.float4ByVal ? _("by value") : _("by reference")));
631         printf(_("Float8 argument passing:              %s\n"),
632                    (ControlFile.float8ByVal ? _("by value") : _("by reference")));
633         printf(_("Data page checksum version:           %u\n"),
634                    ControlFile.data_checksum_version);
635 }
636
637
638 /*
639  * Print the values to be changed.
640  */
641 static void
642 PrintNewControlValues()
643 {
644         char            fname[MAXFNAMELEN];
645
646         /* This will be always printed in order to keep format same. */
647         printf(_("\n\nValues to be changed:\n\n"));
648
649         XLogFileName(fname, ControlFile.checkPointCopy.ThisTimeLineID, newXlogSegNo);
650         printf(_("First log segment after reset:        %s\n"), fname);
651
652         if (set_mxid != 0)
653         {
654                 printf(_("NextMultiXactId:                      %u\n"),
655                            ControlFile.checkPointCopy.nextMulti);
656                 printf(_("OldestMultiXid:                       %u\n"),
657                            ControlFile.checkPointCopy.oldestMulti);
658                 printf(_("OldestMulti's DB:                     %u\n"),
659                            ControlFile.checkPointCopy.oldestMultiDB);
660         }
661
662         if (set_mxoff != -1)
663         {
664                 printf(_("NextMultiOffset:                      %u\n"),
665                            ControlFile.checkPointCopy.nextMultiOffset);
666         }
667
668         if (set_oid != 0)
669         {
670                 printf(_("NextOID:                              %u\n"),
671                            ControlFile.checkPointCopy.nextOid);
672         }
673
674         if (set_xid != 0)
675         {
676                 printf(_("NextXID:                              %u\n"),
677                            ControlFile.checkPointCopy.nextXid);
678                 printf(_("OldestXID:                            %u\n"),
679                            ControlFile.checkPointCopy.oldestXid);
680                 printf(_("OldestXID's DB:                       %u\n"),
681                            ControlFile.checkPointCopy.oldestXidDB);
682         }
683
684         if (set_xid_epoch != -1)
685         {
686                 printf(_("NextXID Epoch:                        %u\n"),
687                            ControlFile.checkPointCopy.nextXidEpoch);
688         }
689 }
690
691
692 /*
693  * Write out the new pg_control file.
694  */
695 static void
696 RewriteControlFile(void)
697 {
698         int                     fd;
699         char            buffer[PG_CONTROL_SIZE];                /* need not be aligned */
700
701         /*
702          * Adjust fields as needed to force an empty XLOG starting at
703          * newXlogSegNo.
704          */
705         XLogSegNoOffsetToRecPtr(newXlogSegNo, SizeOfXLogLongPHD,
706                                                         ControlFile.checkPointCopy.redo);
707         ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
708
709         ControlFile.state = DB_SHUTDOWNED;
710         ControlFile.time = (pg_time_t) time(NULL);
711         ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
712         ControlFile.prevCheckPoint = 0;
713         ControlFile.minRecoveryPoint = 0;
714         ControlFile.minRecoveryPointTLI = 0;
715         ControlFile.backupStartPoint = 0;
716         ControlFile.backupEndPoint = 0;
717         ControlFile.backupEndRequired = false;
718
719         /*
720          * Force the defaults for max_* settings. The values don't really matter
721          * as long as wal_level='minimal'; the postmaster will reset these fields
722          * anyway at startup.
723          */
724         ControlFile.wal_level = WAL_LEVEL_MINIMAL;
725         ControlFile.wal_log_hintbits = false;
726         ControlFile.MaxConnections = 100;
727         ControlFile.max_worker_processes = 8;
728         ControlFile.max_prepared_xacts = 0;
729         ControlFile.max_locks_per_xact = 64;
730
731         /* Now we can force the recorded xlog seg size to the right thing. */
732         ControlFile.xlog_seg_size = XLogSegSize;
733
734         /* Contents are protected with a CRC */
735         INIT_CRC32(ControlFile.crc);
736         COMP_CRC32(ControlFile.crc,
737                            (char *) &ControlFile,
738                            offsetof(ControlFileData, crc));
739         FIN_CRC32(ControlFile.crc);
740
741         /*
742          * We write out PG_CONTROL_SIZE bytes into pg_control, zero-padding the
743          * excess over sizeof(ControlFileData).  This reduces the odds of
744          * premature-EOF errors when reading pg_control.  We'll still fail when we
745          * check the contents of the file, but hopefully with a more specific
746          * error than "couldn't read pg_control".
747          */
748         if (sizeof(ControlFileData) > PG_CONTROL_SIZE)
749         {
750                 fprintf(stderr,
751                                 _("%s: internal error -- sizeof(ControlFileData) is too large ... fix PG_CONTROL_SIZE\n"),
752                                 progname);
753                 exit(1);
754         }
755
756         memset(buffer, 0, PG_CONTROL_SIZE);
757         memcpy(buffer, &ControlFile, sizeof(ControlFileData));
758
759         unlink(XLOG_CONTROL_FILE);
760
761         fd = open(XLOG_CONTROL_FILE,
762                           O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
763                           S_IRUSR | S_IWUSR);
764         if (fd < 0)
765         {
766                 fprintf(stderr, _("%s: could not create pg_control file: %s\n"),
767                                 progname, strerror(errno));
768                 exit(1);
769         }
770
771         errno = 0;
772         if (write(fd, buffer, PG_CONTROL_SIZE) != PG_CONTROL_SIZE)
773         {
774                 /* if write didn't set errno, assume problem is no disk space */
775                 if (errno == 0)
776                         errno = ENOSPC;
777                 fprintf(stderr, _("%s: could not write pg_control file: %s\n"),
778                                 progname, strerror(errno));
779                 exit(1);
780         }
781
782         if (fsync(fd) != 0)
783         {
784                 fprintf(stderr, _("%s: fsync error: %s\n"), progname, strerror(errno));
785                 exit(1);
786         }
787
788         close(fd);
789 }
790
791
792 /*
793  * Scan existing XLOG files and determine the highest existing WAL address
794  *
795  * On entry, ControlFile.checkPointCopy.redo and ControlFile.xlog_seg_size
796  * are assumed valid (note that we allow the old xlog seg size to differ
797  * from what we're using).  On exit, newXlogId and newXlogSeg are set to
798  * suitable values for the beginning of replacement WAL (in our seg size).
799  */
800 static void
801 FindEndOfXLOG(void)
802 {
803         DIR                *xldir;
804         struct dirent *xlde;
805         uint64          segs_per_xlogid;
806         uint64          xlogbytepos;
807
808         /*
809          * Initialize the max() computation using the last checkpoint address from
810          * old pg_control.      Note that for the moment we are working with segment
811          * numbering according to the old xlog seg size.
812          */
813         segs_per_xlogid = (UINT64CONST(0x0000000100000000) / ControlFile.xlog_seg_size);
814         newXlogSegNo = ControlFile.checkPointCopy.redo / ControlFile.xlog_seg_size;
815
816         /*
817          * Scan the pg_xlog directory to find existing WAL segment files. We
818          * assume any present have been used; in most scenarios this should be
819          * conservative, because of xlog.c's attempts to pre-create files.
820          */
821         xldir = opendir(XLOGDIR);
822         if (xldir == NULL)
823         {
824                 fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
825                                 progname, XLOGDIR, strerror(errno));
826                 exit(1);
827         }
828
829         errno = 0;
830         while ((xlde = readdir(xldir)) != NULL)
831         {
832                 if (strlen(xlde->d_name) == 24 &&
833                         strspn(xlde->d_name, "0123456789ABCDEF") == 24)
834                 {
835                         unsigned int tli,
836                                                 log,
837                                                 seg;
838                         XLogSegNo       segno;
839
840                         sscanf(xlde->d_name, "%08X%08X%08X", &tli, &log, &seg);
841                         segno = ((uint64) log) * segs_per_xlogid + seg;
842
843                         /*
844                          * Note: we take the max of all files found, regardless of their
845                          * timelines.  Another possibility would be to ignore files of
846                          * timelines other than the target TLI, but this seems safer.
847                          * Better too large a result than too small...
848                          */
849                         if (segno > newXlogSegNo)
850                                 newXlogSegNo = segno;
851                 }
852                 errno = 0;
853         }
854 #ifdef WIN32
855
856         /*
857          * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
858          * released version
859          */
860         if (GetLastError() == ERROR_NO_MORE_FILES)
861                 errno = 0;
862 #endif
863
864         if (errno)
865         {
866                 fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
867                                 progname, XLOGDIR, strerror(errno));
868                 exit(1);
869         }
870         closedir(xldir);
871
872         /*
873          * Finally, convert to new xlog seg size, and advance by one to ensure we
874          * are in virgin territory.
875          */
876         xlogbytepos = newXlogSegNo * ControlFile.xlog_seg_size;
877         newXlogSegNo = (xlogbytepos + XLogSegSize - 1) / XLogSegSize;
878         newXlogSegNo++;
879 }
880
881
882 /*
883  * Remove existing XLOG files
884  */
885 static void
886 KillExistingXLOG(void)
887 {
888         DIR                *xldir;
889         struct dirent *xlde;
890         char            path[MAXPGPATH];
891
892         xldir = opendir(XLOGDIR);
893         if (xldir == NULL)
894         {
895                 fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
896                                 progname, XLOGDIR, strerror(errno));
897                 exit(1);
898         }
899
900         errno = 0;
901         while ((xlde = readdir(xldir)) != NULL)
902         {
903                 if (strlen(xlde->d_name) == 24 &&
904                         strspn(xlde->d_name, "0123456789ABCDEF") == 24)
905                 {
906                         snprintf(path, MAXPGPATH, "%s/%s", XLOGDIR, xlde->d_name);
907                         if (unlink(path) < 0)
908                         {
909                                 fprintf(stderr, _("%s: could not delete file \"%s\": %s\n"),
910                                                 progname, path, strerror(errno));
911                                 exit(1);
912                         }
913                 }
914                 errno = 0;
915         }
916 #ifdef WIN32
917
918         /*
919          * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
920          * released version
921          */
922         if (GetLastError() == ERROR_NO_MORE_FILES)
923                 errno = 0;
924 #endif
925
926         if (errno)
927         {
928                 fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
929                                 progname, XLOGDIR, strerror(errno));
930                 exit(1);
931         }
932         closedir(xldir);
933 }
934
935
936 /*
937  * Remove existing archive status files
938  */
939 static void
940 KillExistingArchiveStatus(void)
941 {
942         DIR                *xldir;
943         struct dirent *xlde;
944         char            path[MAXPGPATH];
945
946 #define ARCHSTATDIR XLOGDIR "/archive_status"
947
948         xldir = opendir(ARCHSTATDIR);
949         if (xldir == NULL)
950         {
951                 fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
952                                 progname, ARCHSTATDIR, strerror(errno));
953                 exit(1);
954         }
955
956         errno = 0;
957         while ((xlde = readdir(xldir)) != NULL)
958         {
959                 if (strspn(xlde->d_name, "0123456789ABCDEF") == 24 &&
960                         (strcmp(xlde->d_name + 24, ".ready") == 0 ||
961                          strcmp(xlde->d_name + 24, ".done") == 0))
962                 {
963                         snprintf(path, MAXPGPATH, "%s/%s", ARCHSTATDIR, xlde->d_name);
964                         if (unlink(path) < 0)
965                         {
966                                 fprintf(stderr, _("%s: could not delete file \"%s\": %s\n"),
967                                                 progname, path, strerror(errno));
968                                 exit(1);
969                         }
970                 }
971                 errno = 0;
972         }
973 #ifdef WIN32
974
975         /*
976          * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
977          * released version
978          */
979         if (GetLastError() == ERROR_NO_MORE_FILES)
980                 errno = 0;
981 #endif
982
983         if (errno)
984         {
985                 fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
986                                 progname, ARCHSTATDIR, strerror(errno));
987                 exit(1);
988         }
989         closedir(xldir);
990 }
991
992
993 /*
994  * Write an empty XLOG file, containing only the checkpoint record
995  * already set up in ControlFile.
996  */
997 static void
998 WriteEmptyXLOG(void)
999 {
1000         char       *buffer;
1001         XLogPageHeader page;
1002         XLogLongPageHeader longpage;
1003         XLogRecord *record;
1004         pg_crc32        crc;
1005         char            path[MAXPGPATH];
1006         int                     fd;
1007         int                     nbytes;
1008
1009         /* Use malloc() to ensure buffer is MAXALIGNED */
1010         buffer = (char *) pg_malloc(XLOG_BLCKSZ);
1011         page = (XLogPageHeader) buffer;
1012         memset(buffer, 0, XLOG_BLCKSZ);
1013
1014         /* Set up the XLOG page header */
1015         page->xlp_magic = XLOG_PAGE_MAGIC;
1016         page->xlp_info = XLP_LONG_HEADER;
1017         page->xlp_tli = ControlFile.checkPointCopy.ThisTimeLineID;
1018         page->xlp_pageaddr = ControlFile.checkPointCopy.redo - SizeOfXLogLongPHD;
1019         longpage = (XLogLongPageHeader) page;
1020         longpage->xlp_sysid = ControlFile.system_identifier;
1021         longpage->xlp_seg_size = XLogSegSize;
1022         longpage->xlp_xlog_blcksz = XLOG_BLCKSZ;
1023
1024         /* Insert the initial checkpoint record */
1025         record = (XLogRecord *) ((char *) page + SizeOfXLogLongPHD);
1026         record->xl_prev = 0;
1027         record->xl_xid = InvalidTransactionId;
1028         record->xl_tot_len = SizeOfXLogRecord + sizeof(CheckPoint);
1029         record->xl_len = sizeof(CheckPoint);
1030         record->xl_info = XLOG_CHECKPOINT_SHUTDOWN;
1031         record->xl_rmid = RM_XLOG_ID;
1032         memcpy(XLogRecGetData(record), &ControlFile.checkPointCopy,
1033                    sizeof(CheckPoint));
1034
1035         INIT_CRC32(crc);
1036         COMP_CRC32(crc, &ControlFile.checkPointCopy, sizeof(CheckPoint));
1037         COMP_CRC32(crc, (char *) record, offsetof(XLogRecord, xl_crc));
1038         FIN_CRC32(crc);
1039         record->xl_crc = crc;
1040
1041         /* Write the first page */
1042         XLogFilePath(path, ControlFile.checkPointCopy.ThisTimeLineID, newXlogSegNo);
1043
1044         unlink(path);
1045
1046         fd = open(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
1047                           S_IRUSR | S_IWUSR);
1048         if (fd < 0)
1049         {
1050                 fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
1051                                 progname, path, strerror(errno));
1052                 exit(1);
1053         }
1054
1055         errno = 0;
1056         if (write(fd, buffer, XLOG_BLCKSZ) != XLOG_BLCKSZ)
1057         {
1058                 /* if write didn't set errno, assume problem is no disk space */
1059                 if (errno == 0)
1060                         errno = ENOSPC;
1061                 fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
1062                                 progname, path, strerror(errno));
1063                 exit(1);
1064         }
1065
1066         /* Fill the rest of the file with zeroes */
1067         memset(buffer, 0, XLOG_BLCKSZ);
1068         for (nbytes = XLOG_BLCKSZ; nbytes < XLogSegSize; nbytes += XLOG_BLCKSZ)
1069         {
1070                 errno = 0;
1071                 if (write(fd, buffer, XLOG_BLCKSZ) != XLOG_BLCKSZ)
1072                 {
1073                         if (errno == 0)
1074                                 errno = ENOSPC;
1075                         fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
1076                                         progname, path, strerror(errno));
1077                         exit(1);
1078                 }
1079         }
1080
1081         if (fsync(fd) != 0)
1082         {
1083                 fprintf(stderr, _("%s: fsync error: %s\n"), progname, strerror(errno));
1084                 exit(1);
1085         }
1086
1087         close(fd);
1088 }
1089
1090
1091 static void
1092 usage(void)
1093 {
1094         printf(_("%s resets the PostgreSQL transaction log.\n\n"), progname);
1095         printf(_("Usage:\n  %s [OPTION]... DATADIR\n\n"), progname);
1096         printf(_("Options:\n"));
1097         printf(_("  -e XIDEPOCH      set next transaction ID epoch\n"));
1098         printf(_("  -f               force update to be done\n"));
1099         printf(_("  -l XLOGFILE      force minimum WAL starting location for new transaction log\n"));
1100         printf(_("  -m MXID,MXID     set next and oldest multitransaction ID\n"));
1101         printf(_("  -n               no update, just show what would be done (for testing)\n"));
1102         printf(_("  -o OID           set next OID\n"));
1103         printf(_("  -O OFFSET        set next multitransaction offset\n"));
1104         printf(_("  -V, --version    output version information, then exit\n"));
1105         printf(_("  -x XID           set next transaction ID\n"));
1106         printf(_("  -?, --help       show this help, then exit\n"));
1107         printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
1108 }