Header And Logo

PostgreSQL
| The world's most advanced open source database.

pg_resetxlog.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * pg_resetxlog.c
00004  *    A utility to "zero out" the xlog when it's corrupt beyond recovery.
00005  *    Can also rebuild pg_control if needed.
00006  *
00007  * The theory of operation is fairly simple:
00008  *    1. Read the existing pg_control (which will include the last
00009  *       checkpoint record).  If it is an old format then update to
00010  *       current format.
00011  *    2. If pg_control is corrupt, attempt to intuit reasonable values,
00012  *       by scanning the old xlog if necessary.
00013  *    3. Modify pg_control to reflect a "shutdown" state with a checkpoint
00014  *       record at the start of xlog.
00015  *    4. Flush the existing xlog files and write a new segment with
00016  *       just a checkpoint record in it.  The new segment is positioned
00017  *       just past the end of the old xlog, so that existing LSNs in
00018  *       data pages will appear to be "in the past".
00019  * This is all pretty straightforward except for the intuition part of
00020  * step 2 ...
00021  *
00022  *
00023  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00024  * Portions Copyright (c) 1994, Regents of the University of California
00025  *
00026  * src/bin/pg_resetxlog/pg_resetxlog.c
00027  *
00028  *-------------------------------------------------------------------------
00029  */
00030 
00031 /*
00032  * We have to use postgres.h not postgres_fe.h here, because there's so much
00033  * backend-only stuff in the XLOG include files we need.  But we need a
00034  * frontend-ish environment otherwise.  Hence this ugly hack.
00035  */
00036 #define FRONTEND 1
00037 
00038 #include "postgres.h"
00039 
00040 #include <dirent.h>
00041 #include <fcntl.h>
00042 #include <locale.h>
00043 #include <sys/stat.h>
00044 #include <sys/time.h>
00045 #include <time.h>
00046 #include <unistd.h>
00047 #ifdef HAVE_GETOPT_H
00048 #include <getopt.h>
00049 #endif
00050 
00051 #include "access/transam.h"
00052 #include "access/tuptoaster.h"
00053 #include "access/multixact.h"
00054 #include "access/xlog_internal.h"
00055 #include "catalog/catversion.h"
00056 #include "catalog/pg_control.h"
00057 #include "common/fe_memutils.h"
00058 
00059 extern int  optind;
00060 extern char *optarg;
00061 
00062 
00063 static ControlFileData ControlFile;     /* pg_control values */
00064 static XLogSegNo newXlogSegNo;  /* new XLOG segment # */
00065 static bool guessed = false;    /* T if we had to guess at any values */
00066 static const char *progname;
00067 
00068 static bool ReadControlFile(void);
00069 static void GuessControlValues(void);
00070 static void PrintControlValues(bool guessed);
00071 static void RewriteControlFile(void);
00072 static void FindEndOfXLOG(void);
00073 static void KillExistingXLOG(void);
00074 static void KillExistingArchiveStatus(void);
00075 static void WriteEmptyXLOG(void);
00076 static void usage(void);
00077 
00078 
00079 int
00080 main(int argc, char *argv[])
00081 {
00082     int         c;
00083     bool        force = false;
00084     bool        noupdate = false;
00085     uint32      set_xid_epoch = (uint32) -1;
00086     TransactionId set_xid = 0;
00087     Oid         set_oid = 0;
00088     MultiXactId set_mxid = 0;
00089     MultiXactId set_oldestmxid = 0;
00090     MultiXactOffset set_mxoff = (MultiXactOffset) -1;
00091     uint32      minXlogTli = 0;
00092     XLogSegNo   minXlogSegNo = 0;
00093     char       *endptr;
00094     char       *endptr2;
00095     char       *DataDir;
00096     int         fd;
00097 
00098     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_resetxlog"));
00099 
00100     progname = get_progname(argv[0]);
00101 
00102     if (argc > 1)
00103     {
00104         if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
00105         {
00106             usage();
00107             exit(0);
00108         }
00109         if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
00110         {
00111             puts("pg_resetxlog (PostgreSQL) " PG_VERSION);
00112             exit(0);
00113         }
00114     }
00115 
00116 
00117     while ((c = getopt(argc, argv, "fl:m:no:O:x:e:")) != -1)
00118     {
00119         switch (c)
00120         {
00121             case 'f':
00122                 force = true;
00123                 break;
00124 
00125             case 'n':
00126                 noupdate = true;
00127                 break;
00128 
00129             case 'e':
00130                 set_xid_epoch = strtoul(optarg, &endptr, 0);
00131                 if (endptr == optarg || *endptr != '\0')
00132                 {
00133                     fprintf(stderr, _("%s: invalid argument for option -e\n"), progname);
00134                     fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
00135                     exit(1);
00136                 }
00137                 if (set_xid_epoch == -1)
00138                 {
00139                     fprintf(stderr, _("%s: transaction ID epoch (-e) must not be -1\n"), progname);
00140                     exit(1);
00141                 }
00142                 break;
00143 
00144             case 'x':
00145                 set_xid = strtoul(optarg, &endptr, 0);
00146                 if (endptr == optarg || *endptr != '\0')
00147                 {
00148                     fprintf(stderr, _("%s: invalid argument for option -x\n"), progname);
00149                     fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
00150                     exit(1);
00151                 }
00152                 if (set_xid == 0)
00153                 {
00154                     fprintf(stderr, _("%s: transaction ID (-x) must not be 0\n"), progname);
00155                     exit(1);
00156                 }
00157                 break;
00158 
00159             case 'o':
00160                 set_oid = strtoul(optarg, &endptr, 0);
00161                 if (endptr == optarg || *endptr != '\0')
00162                 {
00163                     fprintf(stderr, _("%s: invalid argument for option -o\n"), progname);
00164                     fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
00165                     exit(1);
00166                 }
00167                 if (set_oid == 0)
00168                 {
00169                     fprintf(stderr, _("%s: OID (-o) must not be 0\n"), progname);
00170                     exit(1);
00171                 }
00172                 break;
00173 
00174             case 'm':
00175                 set_mxid = strtoul(optarg, &endptr, 0);
00176                 if (endptr == optarg || *endptr != ',')
00177                 {
00178                     fprintf(stderr, _("%s: invalid argument for option -m\n"), progname);
00179                     fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
00180                     exit(1);
00181                 }
00182 
00183                 set_oldestmxid = strtoul(endptr + 1, &endptr2, 0);
00184                 if (endptr2 == endptr + 1 || *endptr2 != '\0')
00185                 {
00186                     fprintf(stderr, _("%s: invalid argument for option -m\n"), progname);
00187                     fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
00188                     exit(1);
00189                 }
00190                 if (set_mxid == 0)
00191                 {
00192                     fprintf(stderr, _("%s: multitransaction ID (-m) must not be 0\n"), progname);
00193                     exit(1);
00194                 }
00195                 /*
00196                  * XXX It'd be nice to have more sanity checks here, e.g. so
00197                  * that oldest is not wrapped around w.r.t. nextMulti.
00198                  */
00199                 if (set_oldestmxid == 0)
00200                 {
00201                     fprintf(stderr, _("%s: oldest multitransaction ID (-m) must not be 0\n"),
00202                             progname);
00203                     exit(1);
00204                 }
00205                 break;
00206 
00207             case 'O':
00208                 set_mxoff = strtoul(optarg, &endptr, 0);
00209                 if (endptr == optarg || *endptr != '\0')
00210                 {
00211                     fprintf(stderr, _("%s: invalid argument for option -O\n"), progname);
00212                     fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
00213                     exit(1);
00214                 }
00215                 if (set_mxoff == -1)
00216                 {
00217                     fprintf(stderr, _("%s: multitransaction offset (-O) must not be -1\n"), progname);
00218                     exit(1);
00219                 }
00220                 break;
00221 
00222             case 'l':
00223                 if (strspn(optarg, "01234567890ABCDEFabcdef") != 24)
00224                 {
00225                     fprintf(stderr, _("%s: invalid argument for option -l\n"), progname);
00226                     fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
00227                     exit(1);
00228                 }
00229                 XLogFromFileName(optarg, &minXlogTli, &minXlogSegNo);
00230                 break;
00231 
00232             default:
00233                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
00234                 exit(1);
00235         }
00236     }
00237 
00238     if (optind == argc)
00239     {
00240         fprintf(stderr, _("%s: no data directory specified\n"), progname);
00241         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
00242         exit(1);
00243     }
00244 
00245     /*
00246      * Don't allow pg_resetxlog to be run as root, to avoid overwriting the
00247      * ownership of files in the data directory. We need only check for root
00248      * -- any other user won't have sufficient permissions to modify files in
00249      * the data directory.
00250      */
00251 #ifndef WIN32
00252     if (geteuid() == 0)
00253     {
00254         fprintf(stderr, _("%s: cannot be executed by \"root\"\n"),
00255                 progname);
00256         fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
00257                 progname);
00258         exit(1);
00259     }
00260 #endif
00261 
00262     DataDir = argv[optind];
00263 
00264     if (chdir(DataDir) < 0)
00265     {
00266         fprintf(stderr, _("%s: could not change directory to \"%s\": %s\n"),
00267                 progname, DataDir, strerror(errno));
00268         exit(1);
00269     }
00270 
00271     /*
00272      * Check for a postmaster lock file --- if there is one, refuse to
00273      * proceed, on grounds we might be interfering with a live installation.
00274      */
00275     if ((fd = open("postmaster.pid", O_RDONLY, 0)) < 0)
00276     {
00277         if (errno != ENOENT)
00278         {
00279             fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
00280                     progname, "postmaster.pid", strerror(errno));
00281             exit(1);
00282         }
00283     }
00284     else
00285     {
00286         fprintf(stderr, _("%s: lock file \"%s\" exists\n"
00287                           "Is a server running?  If not, delete the lock file and try again.\n"),
00288                 progname, "postmaster.pid");
00289         exit(1);
00290     }
00291 
00292     /*
00293      * Attempt to read the existing pg_control file
00294      */
00295     if (!ReadControlFile())
00296         GuessControlValues();
00297 
00298     /*
00299      * Also look at existing segment files to set up newXlogSegNo
00300      */
00301     FindEndOfXLOG();
00302 
00303     /*
00304      * Adjust fields if required by switches.  (Do this now so that printout,
00305      * if any, includes these values.)
00306      */
00307     if (set_xid_epoch != -1)
00308         ControlFile.checkPointCopy.nextXidEpoch = set_xid_epoch;
00309 
00310     if (set_xid != 0)
00311     {
00312         ControlFile.checkPointCopy.nextXid = set_xid;
00313 
00314         /*
00315          * For the moment, just set oldestXid to a value that will force
00316          * immediate autovacuum-for-wraparound.  It's not clear whether adding
00317          * user control of this is useful, so let's just do something that's
00318          * reasonably safe.  The magic constant here corresponds to the
00319          * maximum allowed value of autovacuum_freeze_max_age.
00320          */
00321         ControlFile.checkPointCopy.oldestXid = set_xid - 2000000000;
00322         if (ControlFile.checkPointCopy.oldestXid < FirstNormalTransactionId)
00323             ControlFile.checkPointCopy.oldestXid += FirstNormalTransactionId;
00324         ControlFile.checkPointCopy.oldestXidDB = InvalidOid;
00325     }
00326 
00327     if (set_oid != 0)
00328         ControlFile.checkPointCopy.nextOid = set_oid;
00329 
00330     if (set_mxid != 0)
00331     {
00332         ControlFile.checkPointCopy.nextMulti = set_mxid;
00333 
00334         ControlFile.checkPointCopy.oldestMulti = set_oldestmxid;
00335         if (ControlFile.checkPointCopy.oldestMulti < FirstMultiXactId)
00336             ControlFile.checkPointCopy.oldestMulti += FirstMultiXactId;
00337         ControlFile.checkPointCopy.oldestMultiDB = InvalidOid;
00338     }
00339 
00340     if (set_mxoff != -1)
00341         ControlFile.checkPointCopy.nextMultiOffset = set_mxoff;
00342 
00343     if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID)
00344     {
00345         ControlFile.checkPointCopy.ThisTimeLineID = minXlogTli;
00346         ControlFile.checkPointCopy.PrevTimeLineID = minXlogTli;
00347     }
00348 
00349     if (minXlogSegNo > newXlogSegNo)
00350         newXlogSegNo = minXlogSegNo;
00351 
00352     /*
00353      * If we had to guess anything, and -f was not given, just print the
00354      * guessed values and exit.  Also print if -n is given.
00355      */
00356     if ((guessed && !force) || noupdate)
00357     {
00358         PrintControlValues(guessed);
00359         if (!noupdate)
00360         {
00361             printf(_("\nIf these values seem acceptable, use -f to force reset.\n"));
00362             exit(1);
00363         }
00364         else
00365             exit(0);
00366     }
00367 
00368     /*
00369      * Don't reset from a dirty pg_control without -f, either.
00370      */
00371     if (ControlFile.state != DB_SHUTDOWNED && !force)
00372     {
00373         printf(_("The database server was not shut down cleanly.\n"
00374                "Resetting the transaction log might cause data to be lost.\n"
00375                  "If you want to proceed anyway, use -f to force reset.\n"));
00376         exit(1);
00377     }
00378 
00379     /*
00380      * Else, do the dirty deed.
00381      */
00382     RewriteControlFile();
00383     KillExistingXLOG();
00384     KillExistingArchiveStatus();
00385     WriteEmptyXLOG();
00386 
00387     printf(_("Transaction log reset\n"));
00388     return 0;
00389 }
00390 
00391 
00392 /*
00393  * Try to read the existing pg_control file.
00394  *
00395  * This routine is also responsible for updating old pg_control versions
00396  * to the current format.  (Currently we don't do anything of the sort.)
00397  */
00398 static bool
00399 ReadControlFile(void)
00400 {
00401     int         fd;
00402     int         len;
00403     char       *buffer;
00404     pg_crc32    crc;
00405 
00406     if ((fd = open(XLOG_CONTROL_FILE, O_RDONLY | PG_BINARY, 0)) < 0)
00407     {
00408         /*
00409          * If pg_control is not there at all, or we can't read it, the odds
00410          * are we've been handed a bad DataDir path, so give up. User can do
00411          * "touch pg_control" to force us to proceed.
00412          */
00413         fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
00414                 progname, XLOG_CONTROL_FILE, strerror(errno));
00415         if (errno == ENOENT)
00416             fprintf(stderr, _("If you are sure the data directory path is correct, execute\n"
00417                               "  touch %s\n"
00418                               "and try again.\n"),
00419                     XLOG_CONTROL_FILE);
00420         exit(1);
00421     }
00422 
00423     /* Use malloc to ensure we have a maxaligned buffer */
00424     buffer = (char *) pg_malloc(PG_CONTROL_SIZE);
00425 
00426     len = read(fd, buffer, PG_CONTROL_SIZE);
00427     if (len < 0)
00428     {
00429         fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
00430                 progname, XLOG_CONTROL_FILE, strerror(errno));
00431         exit(1);
00432     }
00433     close(fd);
00434 
00435     if (len >= sizeof(ControlFileData) &&
00436       ((ControlFileData *) buffer)->pg_control_version == PG_CONTROL_VERSION)
00437     {
00438         /* Check the CRC. */
00439         INIT_CRC32(crc);
00440         COMP_CRC32(crc,
00441                    buffer,
00442                    offsetof(ControlFileData, crc));
00443         FIN_CRC32(crc);
00444 
00445         if (EQ_CRC32(crc, ((ControlFileData *) buffer)->crc))
00446         {
00447             /* Valid data... */
00448             memcpy(&ControlFile, buffer, sizeof(ControlFile));
00449             return true;
00450         }
00451 
00452         fprintf(stderr, _("%s: pg_control exists but has invalid CRC; proceed with caution\n"),
00453                 progname);
00454         /* We will use the data anyway, but treat it as guessed. */
00455         memcpy(&ControlFile, buffer, sizeof(ControlFile));
00456         guessed = true;
00457         return true;
00458     }
00459 
00460     /* Looks like it's a mess. */
00461     fprintf(stderr, _("%s: pg_control exists but is broken or unknown version; ignoring it\n"),
00462             progname);
00463     return false;
00464 }
00465 
00466 
00467 /*
00468  * Guess at pg_control values when we can't read the old ones.
00469  */
00470 static void
00471 GuessControlValues(void)
00472 {
00473     uint64      sysidentifier;
00474     struct timeval tv;
00475 
00476     /*
00477      * Set up a completely default set of pg_control values.
00478      */
00479     guessed = true;
00480     memset(&ControlFile, 0, sizeof(ControlFile));
00481 
00482     ControlFile.pg_control_version = PG_CONTROL_VERSION;
00483     ControlFile.catalog_version_no = CATALOG_VERSION_NO;
00484 
00485     /*
00486      * Create a new unique installation identifier, since we can no longer use
00487      * any old XLOG records.  See notes in xlog.c about the algorithm.
00488      */
00489     gettimeofday(&tv, NULL);
00490     sysidentifier = ((uint64) tv.tv_sec) << 32;
00491     sysidentifier |= (uint32) (tv.tv_sec | tv.tv_usec);
00492 
00493     ControlFile.system_identifier = sysidentifier;
00494 
00495     ControlFile.checkPointCopy.redo = SizeOfXLogLongPHD;
00496     ControlFile.checkPointCopy.ThisTimeLineID = 1;
00497     ControlFile.checkPointCopy.PrevTimeLineID = 1;
00498     ControlFile.checkPointCopy.fullPageWrites = false;
00499     ControlFile.checkPointCopy.nextXidEpoch = 0;
00500     ControlFile.checkPointCopy.nextXid = FirstNormalTransactionId;
00501     ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId;
00502     ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
00503     ControlFile.checkPointCopy.nextMultiOffset = 0;
00504     ControlFile.checkPointCopy.oldestXid = FirstNormalTransactionId;
00505     ControlFile.checkPointCopy.oldestXidDB = InvalidOid;
00506     ControlFile.checkPointCopy.oldestMulti = FirstMultiXactId;
00507     ControlFile.checkPointCopy.oldestMultiDB = InvalidOid;
00508     ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
00509     ControlFile.checkPointCopy.oldestActiveXid = InvalidTransactionId;
00510 
00511     ControlFile.state = DB_SHUTDOWNED;
00512     ControlFile.time = (pg_time_t) time(NULL);
00513     ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
00514     ControlFile.unloggedLSN = 1;
00515 
00516     /* minRecoveryPoint, backupStartPoint and backupEndPoint can be left zero */
00517 
00518     ControlFile.wal_level = WAL_LEVEL_MINIMAL;
00519     ControlFile.MaxConnections = 100;
00520     ControlFile.max_prepared_xacts = 0;
00521     ControlFile.max_locks_per_xact = 64;
00522 
00523     ControlFile.maxAlign = MAXIMUM_ALIGNOF;
00524     ControlFile.floatFormat = FLOATFORMAT_VALUE;
00525     ControlFile.blcksz = BLCKSZ;
00526     ControlFile.relseg_size = RELSEG_SIZE;
00527     ControlFile.xlog_blcksz = XLOG_BLCKSZ;
00528     ControlFile.xlog_seg_size = XLOG_SEG_SIZE;
00529     ControlFile.nameDataLen = NAMEDATALEN;
00530     ControlFile.indexMaxKeys = INDEX_MAX_KEYS;
00531     ControlFile.toast_max_chunk_size = TOAST_MAX_CHUNK_SIZE;
00532 #ifdef HAVE_INT64_TIMESTAMP
00533     ControlFile.enableIntTimes = true;
00534 #else
00535     ControlFile.enableIntTimes = false;
00536 #endif
00537     ControlFile.float4ByVal = FLOAT4PASSBYVAL;
00538     ControlFile.float8ByVal = FLOAT8PASSBYVAL;
00539 
00540     /*
00541      * XXX eventually, should try to grovel through old XLOG to develop more
00542      * accurate values for TimeLineID, nextXID, etc.
00543      */
00544 }
00545 
00546 
00547 /*
00548  * Print the guessed pg_control values when we had to guess.
00549  *
00550  * NB: this display should be just those fields that will not be
00551  * reset by RewriteControlFile().
00552  */
00553 static void
00554 PrintControlValues(bool guessed)
00555 {
00556     char        sysident_str[32];
00557     char        fname[MAXFNAMELEN];
00558 
00559     if (guessed)
00560         printf(_("Guessed pg_control values:\n\n"));
00561     else
00562         printf(_("pg_control values:\n\n"));
00563 
00564     /*
00565      * Format system_identifier separately to keep platform-dependent format
00566      * code out of the translatable message string.
00567      */
00568     snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
00569              ControlFile.system_identifier);
00570 
00571     XLogFileName(fname, ControlFile.checkPointCopy.ThisTimeLineID, newXlogSegNo);
00572 
00573     printf(_("First log segment after reset:        %s\n"),
00574            fname);
00575     printf(_("pg_control version number:            %u\n"),
00576            ControlFile.pg_control_version);
00577     printf(_("Catalog version number:               %u\n"),
00578            ControlFile.catalog_version_no);
00579     printf(_("Database system identifier:           %s\n"),
00580            sysident_str);
00581     printf(_("Latest checkpoint's TimeLineID:       %u\n"),
00582            ControlFile.checkPointCopy.ThisTimeLineID);
00583     printf(_("Latest checkpoint's full_page_writes: %s\n"),
00584            ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
00585     printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
00586            ControlFile.checkPointCopy.nextXidEpoch,
00587            ControlFile.checkPointCopy.nextXid);
00588     printf(_("Latest checkpoint's NextOID:          %u\n"),
00589            ControlFile.checkPointCopy.nextOid);
00590     printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
00591            ControlFile.checkPointCopy.nextMulti);
00592     printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
00593            ControlFile.checkPointCopy.nextMultiOffset);
00594     printf(_("Latest checkpoint's oldestXID:        %u\n"),
00595            ControlFile.checkPointCopy.oldestXid);
00596     printf(_("Latest checkpoint's oldestXID's DB:   %u\n"),
00597            ControlFile.checkPointCopy.oldestXidDB);
00598     printf(_("Latest checkpoint's oldestActiveXID:  %u\n"),
00599            ControlFile.checkPointCopy.oldestActiveXid);
00600     printf(_("Latest checkpoint's oldestMultiXid:   %u\n"),
00601            ControlFile.checkPointCopy.oldestMulti);
00602     printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
00603            ControlFile.checkPointCopy.oldestMultiDB);
00604     printf(_("Maximum data alignment:               %u\n"),
00605            ControlFile.maxAlign);
00606     /* we don't print floatFormat since can't say much useful about it */
00607     printf(_("Database block size:                  %u\n"),
00608            ControlFile.blcksz);
00609     printf(_("Blocks per segment of large relation: %u\n"),
00610            ControlFile.relseg_size);
00611     printf(_("WAL block size:                       %u\n"),
00612            ControlFile.xlog_blcksz);
00613     printf(_("Bytes per WAL segment:                %u\n"),
00614            ControlFile.xlog_seg_size);
00615     printf(_("Maximum length of identifiers:        %u\n"),
00616            ControlFile.nameDataLen);
00617     printf(_("Maximum columns in an index:          %u\n"),
00618            ControlFile.indexMaxKeys);
00619     printf(_("Maximum size of a TOAST chunk:        %u\n"),
00620            ControlFile.toast_max_chunk_size);
00621     printf(_("Date/time type storage:               %s\n"),
00622            (ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
00623     printf(_("Float4 argument passing:              %s\n"),
00624            (ControlFile.float4ByVal ? _("by value") : _("by reference")));
00625     printf(_("Float8 argument passing:              %s\n"),
00626            (ControlFile.float8ByVal ? _("by value") : _("by reference")));
00627     printf(_("Data page checksum version:           %u\n"),
00628            ControlFile.data_checksum_version);
00629 }
00630 
00631 
00632 /*
00633  * Write out the new pg_control file.
00634  */
00635 static void
00636 RewriteControlFile(void)
00637 {
00638     int         fd;
00639     char        buffer[PG_CONTROL_SIZE];        /* need not be aligned */
00640 
00641     /*
00642      * Adjust fields as needed to force an empty XLOG starting at
00643      * newXlogSegNo.
00644      */
00645     XLogSegNoOffsetToRecPtr(newXlogSegNo, SizeOfXLogLongPHD,
00646                             ControlFile.checkPointCopy.redo);
00647     ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
00648 
00649     ControlFile.state = DB_SHUTDOWNED;
00650     ControlFile.time = (pg_time_t) time(NULL);
00651     ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
00652     ControlFile.prevCheckPoint = 0;
00653     ControlFile.minRecoveryPoint = 0;
00654     ControlFile.minRecoveryPointTLI = 0;
00655     ControlFile.backupStartPoint = 0;
00656     ControlFile.backupEndPoint = 0;
00657     ControlFile.backupEndRequired = false;
00658 
00659     /*
00660      * Force the defaults for max_* settings. The values don't really matter
00661      * as long as wal_level='minimal'; the postmaster will reset these fields
00662      * anyway at startup.
00663      */
00664     ControlFile.wal_level = WAL_LEVEL_MINIMAL;
00665     ControlFile.MaxConnections = 100;
00666     ControlFile.max_prepared_xacts = 0;
00667     ControlFile.max_locks_per_xact = 64;
00668 
00669     /* Now we can force the recorded xlog seg size to the right thing. */
00670     ControlFile.xlog_seg_size = XLogSegSize;
00671 
00672     /* Contents are protected with a CRC */
00673     INIT_CRC32(ControlFile.crc);
00674     COMP_CRC32(ControlFile.crc,
00675                (char *) &ControlFile,
00676                offsetof(ControlFileData, crc));
00677     FIN_CRC32(ControlFile.crc);
00678 
00679     /*
00680      * We write out PG_CONTROL_SIZE bytes into pg_control, zero-padding the
00681      * excess over sizeof(ControlFileData).  This reduces the odds of
00682      * premature-EOF errors when reading pg_control.  We'll still fail when we
00683      * check the contents of the file, but hopefully with a more specific
00684      * error than "couldn't read pg_control".
00685      */
00686     if (sizeof(ControlFileData) > PG_CONTROL_SIZE)
00687     {
00688         fprintf(stderr,
00689                 _("%s: internal error -- sizeof(ControlFileData) is too large ... fix PG_CONTROL_SIZE\n"),
00690                 progname);
00691         exit(1);
00692     }
00693 
00694     memset(buffer, 0, PG_CONTROL_SIZE);
00695     memcpy(buffer, &ControlFile, sizeof(ControlFileData));
00696 
00697     unlink(XLOG_CONTROL_FILE);
00698 
00699     fd = open(XLOG_CONTROL_FILE,
00700               O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
00701               S_IRUSR | S_IWUSR);
00702     if (fd < 0)
00703     {
00704         fprintf(stderr, _("%s: could not create pg_control file: %s\n"),
00705                 progname, strerror(errno));
00706         exit(1);
00707     }
00708 
00709     errno = 0;
00710     if (write(fd, buffer, PG_CONTROL_SIZE) != PG_CONTROL_SIZE)
00711     {
00712         /* if write didn't set errno, assume problem is no disk space */
00713         if (errno == 0)
00714             errno = ENOSPC;
00715         fprintf(stderr, _("%s: could not write pg_control file: %s\n"),
00716                 progname, strerror(errno));
00717         exit(1);
00718     }
00719 
00720     if (fsync(fd) != 0)
00721     {
00722         fprintf(stderr, _("%s: fsync error: %s\n"), progname, strerror(errno));
00723         exit(1);
00724     }
00725 
00726     close(fd);
00727 }
00728 
00729 
00730 /*
00731  * Scan existing XLOG files and determine the highest existing WAL address
00732  *
00733  * On entry, ControlFile.checkPointCopy.redo and ControlFile.xlog_seg_size
00734  * are assumed valid (note that we allow the old xlog seg size to differ
00735  * from what we're using).  On exit, newXlogId and newXlogSeg are set to
00736  * suitable values for the beginning of replacement WAL (in our seg size).
00737  */
00738 static void
00739 FindEndOfXLOG(void)
00740 {
00741     DIR        *xldir;
00742     struct dirent *xlde;
00743     uint64      segs_per_xlogid;
00744     uint64      xlogbytepos;
00745 
00746     /*
00747      * Initialize the max() computation using the last checkpoint address from
00748      * old pg_control.  Note that for the moment we are working with segment
00749      * numbering according to the old xlog seg size.
00750      */
00751     segs_per_xlogid = (UINT64CONST(0x0000000100000000) / ControlFile.xlog_seg_size);
00752     newXlogSegNo = ControlFile.checkPointCopy.redo / ControlFile.xlog_seg_size;
00753 
00754     /*
00755      * Scan the pg_xlog directory to find existing WAL segment files. We
00756      * assume any present have been used; in most scenarios this should be
00757      * conservative, because of xlog.c's attempts to pre-create files.
00758      */
00759     xldir = opendir(XLOGDIR);
00760     if (xldir == NULL)
00761     {
00762         fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
00763                 progname, XLOGDIR, strerror(errno));
00764         exit(1);
00765     }
00766 
00767     errno = 0;
00768     while ((xlde = readdir(xldir)) != NULL)
00769     {
00770         if (strlen(xlde->d_name) == 24 &&
00771             strspn(xlde->d_name, "0123456789ABCDEF") == 24)
00772         {
00773             unsigned int tli,
00774                         log,
00775                         seg;
00776             XLogSegNo   segno;
00777 
00778             sscanf(xlde->d_name, "%08X%08X%08X", &tli, &log, &seg);
00779             segno = ((uint64) log) * segs_per_xlogid + seg;
00780 
00781             /*
00782              * Note: we take the max of all files found, regardless of their
00783              * timelines.  Another possibility would be to ignore files of
00784              * timelines other than the target TLI, but this seems safer.
00785              * Better too large a result than too small...
00786              */
00787             if (segno > newXlogSegNo)
00788                 newXlogSegNo = segno;
00789         }
00790         errno = 0;
00791     }
00792 #ifdef WIN32
00793 
00794     /*
00795      * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
00796      * released version
00797      */
00798     if (GetLastError() == ERROR_NO_MORE_FILES)
00799         errno = 0;
00800 #endif
00801 
00802     if (errno)
00803     {
00804         fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
00805                 progname, XLOGDIR, strerror(errno));
00806         exit(1);
00807     }
00808     closedir(xldir);
00809 
00810     /*
00811      * Finally, convert to new xlog seg size, and advance by one to ensure we
00812      * are in virgin territory.
00813      */
00814     xlogbytepos = newXlogSegNo * ControlFile.xlog_seg_size;
00815     newXlogSegNo = (xlogbytepos + XLogSegSize - 1) / XLogSegSize;
00816     newXlogSegNo++;
00817 }
00818 
00819 
00820 /*
00821  * Remove existing XLOG files
00822  */
00823 static void
00824 KillExistingXLOG(void)
00825 {
00826     DIR        *xldir;
00827     struct dirent *xlde;
00828     char        path[MAXPGPATH];
00829 
00830     xldir = opendir(XLOGDIR);
00831     if (xldir == NULL)
00832     {
00833         fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
00834                 progname, XLOGDIR, strerror(errno));
00835         exit(1);
00836     }
00837 
00838     errno = 0;
00839     while ((xlde = readdir(xldir)) != NULL)
00840     {
00841         if (strlen(xlde->d_name) == 24 &&
00842             strspn(xlde->d_name, "0123456789ABCDEF") == 24)
00843         {
00844             snprintf(path, MAXPGPATH, "%s/%s", XLOGDIR, xlde->d_name);
00845             if (unlink(path) < 0)
00846             {
00847                 fprintf(stderr, _("%s: could not delete file \"%s\": %s\n"),
00848                         progname, path, strerror(errno));
00849                 exit(1);
00850             }
00851         }
00852         errno = 0;
00853     }
00854 #ifdef WIN32
00855 
00856     /*
00857      * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
00858      * released version
00859      */
00860     if (GetLastError() == ERROR_NO_MORE_FILES)
00861         errno = 0;
00862 #endif
00863 
00864     if (errno)
00865     {
00866         fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
00867                 progname, XLOGDIR, strerror(errno));
00868         exit(1);
00869     }
00870     closedir(xldir);
00871 }
00872 
00873 
00874 /*
00875  * Remove existing archive status files
00876  */
00877 static void
00878 KillExistingArchiveStatus(void)
00879 {
00880     DIR        *xldir;
00881     struct dirent *xlde;
00882     char        path[MAXPGPATH];
00883 
00884 #define ARCHSTATDIR XLOGDIR "/archive_status"
00885 
00886     xldir = opendir(ARCHSTATDIR);
00887     if (xldir == NULL)
00888     {
00889         fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
00890                 progname, ARCHSTATDIR, strerror(errno));
00891         exit(1);
00892     }
00893 
00894     errno = 0;
00895     while ((xlde = readdir(xldir)) != NULL)
00896     {
00897         if (strspn(xlde->d_name, "0123456789ABCDEF") == 24 &&
00898             (strcmp(xlde->d_name + 24, ".ready") == 0 ||
00899              strcmp(xlde->d_name + 24, ".done") == 0))
00900         {
00901             snprintf(path, MAXPGPATH, "%s/%s", ARCHSTATDIR, xlde->d_name);
00902             if (unlink(path) < 0)
00903             {
00904                 fprintf(stderr, _("%s: could not delete file \"%s\": %s\n"),
00905                         progname, path, strerror(errno));
00906                 exit(1);
00907             }
00908         }
00909         errno = 0;
00910     }
00911 #ifdef WIN32
00912 
00913     /*
00914      * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
00915      * released version
00916      */
00917     if (GetLastError() == ERROR_NO_MORE_FILES)
00918         errno = 0;
00919 #endif
00920 
00921     if (errno)
00922     {
00923         fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
00924                 progname, ARCHSTATDIR, strerror(errno));
00925         exit(1);
00926     }
00927     closedir(xldir);
00928 }
00929 
00930 
00931 /*
00932  * Write an empty XLOG file, containing only the checkpoint record
00933  * already set up in ControlFile.
00934  */
00935 static void
00936 WriteEmptyXLOG(void)
00937 {
00938     char       *buffer;
00939     XLogPageHeader page;
00940     XLogLongPageHeader longpage;
00941     XLogRecord *record;
00942     pg_crc32    crc;
00943     char        path[MAXPGPATH];
00944     int         fd;
00945     int         nbytes;
00946 
00947     /* Use malloc() to ensure buffer is MAXALIGNED */
00948     buffer = (char *) pg_malloc(XLOG_BLCKSZ);
00949     page = (XLogPageHeader) buffer;
00950     memset(buffer, 0, XLOG_BLCKSZ);
00951 
00952     /* Set up the XLOG page header */
00953     page->xlp_magic = XLOG_PAGE_MAGIC;
00954     page->xlp_info = XLP_LONG_HEADER;
00955     page->xlp_tli = ControlFile.checkPointCopy.ThisTimeLineID;
00956     page->xlp_pageaddr = ControlFile.checkPointCopy.redo - SizeOfXLogLongPHD;
00957     longpage = (XLogLongPageHeader) page;
00958     longpage->xlp_sysid = ControlFile.system_identifier;
00959     longpage->xlp_seg_size = XLogSegSize;
00960     longpage->xlp_xlog_blcksz = XLOG_BLCKSZ;
00961 
00962     /* Insert the initial checkpoint record */
00963     record = (XLogRecord *) ((char *) page + SizeOfXLogLongPHD);
00964     record->xl_prev = 0;
00965     record->xl_xid = InvalidTransactionId;
00966     record->xl_tot_len = SizeOfXLogRecord + sizeof(CheckPoint);
00967     record->xl_len = sizeof(CheckPoint);
00968     record->xl_info = XLOG_CHECKPOINT_SHUTDOWN;
00969     record->xl_rmid = RM_XLOG_ID;
00970     memcpy(XLogRecGetData(record), &ControlFile.checkPointCopy,
00971            sizeof(CheckPoint));
00972 
00973     INIT_CRC32(crc);
00974     COMP_CRC32(crc, &ControlFile.checkPointCopy, sizeof(CheckPoint));
00975     COMP_CRC32(crc, (char *) record, offsetof(XLogRecord, xl_crc));
00976     FIN_CRC32(crc);
00977     record->xl_crc = crc;
00978 
00979     /* Write the first page */
00980     XLogFilePath(path, ControlFile.checkPointCopy.ThisTimeLineID, newXlogSegNo);
00981 
00982     unlink(path);
00983 
00984     fd = open(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
00985               S_IRUSR | S_IWUSR);
00986     if (fd < 0)
00987     {
00988         fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
00989                 progname, path, strerror(errno));
00990         exit(1);
00991     }
00992 
00993     errno = 0;
00994     if (write(fd, buffer, XLOG_BLCKSZ) != XLOG_BLCKSZ)
00995     {
00996         /* if write didn't set errno, assume problem is no disk space */
00997         if (errno == 0)
00998             errno = ENOSPC;
00999         fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
01000                 progname, path, strerror(errno));
01001         exit(1);
01002     }
01003 
01004     /* Fill the rest of the file with zeroes */
01005     memset(buffer, 0, XLOG_BLCKSZ);
01006     for (nbytes = XLOG_BLCKSZ; nbytes < XLogSegSize; nbytes += XLOG_BLCKSZ)
01007     {
01008         errno = 0;
01009         if (write(fd, buffer, XLOG_BLCKSZ) != XLOG_BLCKSZ)
01010         {
01011             if (errno == 0)
01012                 errno = ENOSPC;
01013             fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
01014                     progname, path, strerror(errno));
01015             exit(1);
01016         }
01017     }
01018 
01019     if (fsync(fd) != 0)
01020     {
01021         fprintf(stderr, _("%s: fsync error: %s\n"), progname, strerror(errno));
01022         exit(1);
01023     }
01024 
01025     close(fd);
01026 }
01027 
01028 
01029 static void
01030 usage(void)
01031 {
01032     printf(_("%s resets the PostgreSQL transaction log.\n\n"), progname);
01033     printf(_("Usage:\n  %s [OPTION]... DATADIR\n\n"), progname);
01034     printf(_("Options:\n"));
01035     printf(_("  -e XIDEPOCH      set next transaction ID epoch\n"));
01036     printf(_("  -f               force update to be done\n"));
01037     printf(_("  -l XLOGFILE      force minimum WAL starting location for new transaction log\n"));
01038     printf(_("  -m XID,OLDEST    set next multitransaction ID and oldest value\n"));
01039     printf(_("  -n               no update, just show extracted control values (for testing)\n"));
01040     printf(_("  -o OID           set next OID\n"));
01041     printf(_("  -O OFFSET        set next multitransaction offset\n"));
01042     printf(_("  -V, --version    output version information, then exit\n"));
01043     printf(_("  -x XID           set next transaction ID\n"));
01044     printf(_("  -?, --help       show this help, then exit\n"));
01045     printf(_("\nReport bugs to <[email protected]>.\n"));
01046 }