diff --git a/doc/src/sgml/ref/pg_resetxlog.sgml b/doc/src/sgml/ref/pg_resetxlog.sgml
index 27b9ab41..5d6aef6 100644
--- a/doc/src/sgml/ref/pg_resetxlog.sgml
+++ b/doc/src/sgml/ref/pg_resetxlog.sgml
@@ -24,6 +24,7 @@ PostgreSQL documentation
pg_resetxlog
-f
-n
+ -r
-o oid
-x xid
-e xid_epoch
@@ -61,6 +62,20 @@ PostgreSQL documentation
+ If pg_resetxlog complains that it cannot determine valid
+ data for pg_control>, you can restore the
+ pg_control> data from WAL files if they are intact by
+ specifying the -r> (restore) option. In this case the values will
+ be substituted for the missing data from WAL files will be displayed.
+ If the user feels the values are correct, the user can force these values
+ to set in the control file by specifying the -fr> by this missing
+ data is restored from WAL files, user can recover database and use as normal
+ database after restart. if the missing data is not able to restore then in
+ this case plausible values will be substituted for the missing data.
+ which follows same as below information.
+
+
+
If pg_resetxlog complains that it cannot determine
valid data for pg_control>, you can force it to proceed anyway
by specifying the -f> (force) option. In this case plausible
diff --git a/src/bin/pg_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c
index d5d89ec..9e9caf4 100644
--- a/src/bin/pg_resetxlog/pg_resetxlog.c
+++ b/src/bin/pg_resetxlog/pg_resetxlog.c
@@ -19,6 +19,13 @@
* This is all pretty straightforward except for the intuition part of
* step 2 ...
*
+ * The algorithm of restoring the pg_control value from old xlog file:
+ * 1. Retrieve all of the active xlog files from xlog direcotry into a list
+ * by increasing order, according their timeline, xlogsegno.
+ * 2. Search the list to find the oldest xlog file of the lastest time line.
+ * 3. Search the records from the oldest xlog file of latest time line
+ * to the latest xlog file of latest time line, if the checkpoint record
+ * has been found, update the latest checkpoint and previous checkpoint.
*
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -62,16 +69,87 @@ extern char *optarg;
static ControlFileData ControlFile; /* pg_control values */
static XLogSegNo newXlogSegNo; /* new XLOG segment # */
static bool guessed = false; /* T if we had to guess at any values */
+static bool restored = false; /* T if we had restore any values from WAL */
static const char *progname;
+/*
+ * We use a list to store the active xlog files we had found in the
+ * xlog directory in increasing order according the time line, logid,
+ * segment id.
+ *
+ */
+typedef struct XLogFileName
+{
+ TimeLineID tli;
+ XLogSegNo segno;
+ char fname[MAXFNAMELEN];
+ struct XLogFileName *next;
+} XLogFileName;
+
+/* The list head */
+static XLogFileName *xlogfilelist = NULL;
+
+/* LastXLogfile is the latest file in the latest time line,
+ * CurXLogfile is the oldest file in the latest time line
+ */
+static XLogFileName *CurXLogFile,
+ *LastXLogFile;
+
+/*
+ * readFile is -1 or a kernel FD for an open log file segment.
+ * When it's open, readOff is the current seek offset in the file.
+ * readSegNo identifies the segment. These variables are only
+ * used to read the XLOG.
+ */
+static int readFile = -1;
+static XLogSegNo readSegNo = 0;
+static TimeLineID readTli;
+static uint32 readOff = 0;
+
+/* Buffer for currently read page (XLOG_BLCKSZ bytes) */
+static char *readBuf = NULL;
+
+/* Buffer for current ReadRecord result (expandable) */
+static char *readRecordBuf = NULL;
+static uint32 readRecordBufSize = 0;
+
+/* State information for XLOG reading */
+static XLogRecPtr ReadRecPtr; /* start of last record read */
+static XLogRecPtr EndRecPtr; /* end+1 of last record read */
+
+/* The last checkpoint found in xlog file.*/
+static CheckPoint lastcheckpoint;
+
+/* The last and previous checkpoint pointers found in xlog file.*/
+static XLogRecPtr prevchkp,
+ lastchkp;
+
static bool ReadControlFile(void);
static void GuessControlValues(void);
-static void PrintControlValues(bool guessed);
+static void PrintControlValues(void);
static void RewriteControlFile(void);
static void FindEndOfXLOG(void);
static void KillExistingXLOG(void);
static void KillExistingArchiveStatus(void);
static void WriteEmptyXLOG(void);
+static void RestoreControlValues(bool mode);
+static bool SearchLastCheckpoint(void);
+static bool CompareXLogFileNames(XLogFileName * f1, XLogFileName * f2);
+static void AddXLogFileIntoList(char *fname);
+static bool ValidXLogFileName(char *fname);
+static bool ValidXLOGPageHeader(XLogPageHeader hdr, uint32 tli,
+ XLogSegNo segno);
+static void ValidateXLOGDirectoryStructure(void);
+static bool PrepareXLogFileList(void);
+static bool IsNextSeg(XLogFileName * prev, XLogFileName * cur);
+static bool GetStartXLogFile(void);
+static bool RecordIsValid(XLogRecord *record);
+static void CleanUpList(XLogFileName * list);
+static bool ValidXLogRecordHeader(XLogRecPtr *RecPtr, XLogRecord *record,
+ bool randAccess);
+static bool XLogPageRead(XLogRecPtr *RecPtr);
+static XLogRecord *ReadRecord(XLogRecPtr *RecPtr, bool randAccess,
+ bool *bContRec);
static void usage(void);
@@ -81,6 +159,7 @@ main(int argc, char *argv[])
int c;
bool force = false;
bool noupdate = false;
+ bool restore = false;
uint32 set_xid_epoch = (uint32) -1;
TransactionId set_xid = 0;
Oid set_oid = 0;
@@ -112,7 +191,7 @@ main(int argc, char *argv[])
}
- while ((c = getopt(argc, argv, "fl:m:no:O:x:e:")) != -1)
+ while ((c = getopt(argc, argv, "fl:m:no:O:x:e:r")) != -1)
{
switch (c)
{
@@ -123,6 +202,9 @@ main(int argc, char *argv[])
case 'n':
noupdate = true;
break;
+ case 'r':
+ restore = true;
+ break;
case 'e':
set_xid_epoch = strtoul(optarg, &endptr, 0);
@@ -271,15 +353,21 @@ main(int argc, char *argv[])
}
/*
- * Attempt to read the existing pg_control file
+ * Attempt to read the existing pg_control file, if the pg_control file is
+ * not proper then it will first try to restore pg_control values from WAL
+ * files, if failed then guess the values. if the restore option is not
+ * provided then the values will be guessed directly.
*/
if (!ReadControlFile())
- GuessControlValues();
+ RestoreControlValues(restore);
- /*
- * Also look at existing segment files to set up newXlogSegNo
- */
- FindEndOfXLOG();
+ if (!restored)
+ {
+ /*
+ * Also look at existing segment files to set up newXlogSegNo
+ */
+ FindEndOfXLOG();
+ }
/*
* Adjust fields if required by switches. (Do this now so that printout,
@@ -324,9 +412,9 @@ main(int argc, char *argv[])
* If we had to guess anything, and -f was not given, just print the
* guessed values and exit. Also print if -n is given.
*/
- if ((guessed && !force) || noupdate)
+ if (((guessed || restored) && !force) || noupdate)
{
- PrintControlValues(guessed);
+ PrintControlValues();
if (!noupdate)
{
printf(_("\nIf these values seem acceptable, use -f to force reset.\n"));
@@ -351,11 +439,17 @@ main(int argc, char *argv[])
* Else, do the dirty deed.
*/
RewriteControlFile();
- KillExistingXLOG();
- KillExistingArchiveStatus();
- WriteEmptyXLOG();
+ if (!restored)
+ {
+ KillExistingXLOG();
+ KillExistingArchiveStatus();
+ WriteEmptyXLOG();
+
+ printf(_("Transaction log reset\n"));
+ }
+ else
+ printf(_("Restore is successful\n"));
- printf(_("Transaction log reset\n"));
return 0;
}
@@ -434,7 +528,6 @@ ReadControlFile(void)
return false;
}
-
/*
* Guess at pg_control values when we can't read the old ones.
*/
@@ -481,7 +574,6 @@ GuessControlValues(void)
ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
/* minRecoveryPoint, backupStartPoint and backupEndPoint can be left zero */
-
ControlFile.wal_level = WAL_LEVEL_MINIMAL;
ControlFile.MaxConnections = 100;
ControlFile.max_prepared_xacts = 0;
@@ -518,13 +610,15 @@ GuessControlValues(void)
* reset by RewriteControlFile().
*/
static void
-PrintControlValues(bool guessed)
+PrintControlValues()
{
char sysident_str[32];
char fname[MAXFNAMELEN];
if (guessed)
printf(_("Guessed pg_control values:\n\n"));
+ else if (restored)
+ printf(_("Restored pg_control values:\n\n"));
else
printf(_("pg_control values:\n\n"));
@@ -535,10 +629,23 @@ PrintControlValues(bool guessed)
snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
ControlFile.system_identifier);
- XLogFileName(fname, ControlFile.checkPointCopy.ThisTimeLineID, newXlogSegNo);
+ if (restored)
+ {
+ XLogFileName(fname, ControlFile.checkPointCopy.ThisTimeLineID,
+ ControlFile.checkPointCopy.redo);
+
+ printf(_("First log segment after restore: %s\n"),
+ fname);
+ }
+ else
+ {
+ XLogFileName(fname, ControlFile.checkPointCopy.ThisTimeLineID,
+ newXlogSegNo);
+
+ printf(_("First log segment after reset: %s\n"),
+ fname);
+ }
- printf(_("First log segment after reset: %s\n"),
- fname);
printf(_("pg_control version number: %u\n"),
ControlFile.pg_control_version);
printf(_("Catalog version number: %u\n"),
@@ -599,22 +706,26 @@ RewriteControlFile(void)
int fd;
char buffer[PG_CONTROL_SIZE]; /* need not be aligned */
- /*
- * Adjust fields as needed to force an empty XLOG starting at
- * newXlogSegNo.
- */
- XLogSegNoOffsetToRecPtr(newXlogSegNo, SizeOfXLogLongPHD,
- ControlFile.checkPointCopy.redo);
- ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
+ if (!restored)
+ {
+ /*
+ * Adjust fields as needed to force an empty XLOG starting at
+ * newXlogSegNo.
+ */
+ XLogSegNoOffsetToRecPtr(newXlogSegNo, SizeOfXLogLongPHD,
+ ControlFile.checkPointCopy.redo);
+ ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
+
+ ControlFile.state = DB_SHUTDOWNED;
+ ControlFile.time = (pg_time_t) time(NULL);
+ ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
+ ControlFile.prevCheckPoint = 0;
+ ControlFile.minRecoveryPoint = 0;
+ ControlFile.backupStartPoint = 0;
+ ControlFile.backupEndPoint = 0;
+ ControlFile.backupEndRequired = false;
+ }
- ControlFile.state = DB_SHUTDOWNED;
- ControlFile.time = (pg_time_t) time(NULL);
- ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
- ControlFile.prevCheckPoint = 0;
- ControlFile.minRecoveryPoint = 0;
- ControlFile.backupStartPoint = 0;
- ControlFile.backupEndPoint = 0;
- ControlFile.backupEndRequired = false;
/*
* Force the defaults for max_* settings. The values don't really matter
@@ -985,6 +1096,895 @@ WriteEmptyXLOG(void)
close(fd);
}
+/*
+ * Restores the pg_control values by searching the xlog segment files
+ * or by guessing it.
+ */
+static void
+RestoreControlValues(bool mode)
+{
+ bool result = false;
+
+ GuessControlValues();
+
+ if (mode)
+ {
+ /*
+ * update the checkpoint value in control file, by searching xlog
+ * segment files.
+ */
+ result = SearchLastCheckpoint();
+ if (result) /* The last checkpoint had been found. */
+ {
+ restored = true;
+ guessed = false;
+
+ ControlFile.checkPointCopy = lastcheckpoint;
+ ControlFile.checkPoint = lastchkp;
+ ControlFile.prevCheckPoint = prevchkp;
+ ControlFile.time = lastcheckpoint.time;
+
+ /*
+ * Always set as in production, which makes the database to
+ * recover always if the redo present.
+ */
+ ControlFile.state = DB_IN_PRODUCTION;
+
+ newXlogSegNo = readSegNo;
+ }
+
+ /* Clean up the list. */
+ CleanUpList(xlogfilelist);
+ }
+}
+
+
+/*
+ * Search the lastest checkpoint in the lastest XLog segment file.
+ *
+ * The return value is the total checkpoints which had been found
+ * in the XLog segment file.
+ */
+static bool
+SearchLastCheckpoint(void)
+{
+ bool randAccess = true;
+ bool bContRec = false;
+ bool bFoundChkPoint = false;
+
+ /* First time through, permanently allocate readBuf. */
+ readBuf = (char *) malloc(XLOG_BLCKSZ);
+ if (NULL == readBuf)
+ {
+ fprintf(stderr, _("%s: Memory allocation failed for size: %d\n"),
+ progname, XLOG_BLCKSZ);
+ exit(1);
+ }
+
+ /*
+ * retrive all of the active xlog files from xlog direcotry into a list by
+ * increasing order, according their timeline and segmentno.
+ */
+ if (!PrepareXLogFileList())
+ goto READ_BUF_FREE;
+
+ /* Select the oldest segment file in the lastest time line. */
+ if (!GetStartXLogFile())
+ {
+ fprintf(stderr, _("%s: No xlog files are found in directory: %s\n"),
+ progname, XLOGDIR);
+ goto READ_BUF_FREE;
+ }
+
+ /* Form the first record address to read from xlog file */
+ XLogSegNoOffsetToRecPtr(CurXLogFile->segno, 0, EndRecPtr);
+ ReadRecPtr = EndRecPtr;
+
+ fprintf(stderr, _("%s: Trying to read checkpoint record from xlog files in"
+ " directory: %s\n"), progname, XLOGDIR);
+
+ /*
+ * Search the XLog segment file from beginning to end, if checkpoint
+ * record is found, then update checkpoint record later update the control
+ * file from these informations.
+ */
+ while (ReadRecord(&EndRecPtr, randAccess, &bContRec) != NULL)
+ {
+ /* Ignore the first record if it is a continuous record */
+ if (bContRec)
+ continue;
+
+ randAccess = false;
+
+ /* To see if the record is checkpoint record. */
+ if (((XLogRecord *) readRecordBuf)->xl_rmid == RM_XLOG_ID)
+ {
+ CheckPoint *chkpoint;
+ uint8 info
+ = ((XLogRecord *) readRecordBuf)->xl_info & ~XLR_INFO_MASK;
+
+ if ((info == XLOG_CHECKPOINT_SHUTDOWN)
+ || (info == XLOG_CHECKPOINT_ONLINE))
+ {
+ chkpoint
+ = (CheckPoint *) XLogRecGetData(((XLogRecord *) readRecordBuf));
+ prevchkp = lastchkp;
+ lastchkp = ReadRecPtr;
+ lastcheckpoint = *chkpoint;
+ bFoundChkPoint = true;
+ }
+ }
+ }
+
+ /* Check the checkpoint record is found or not */
+ if (!bFoundChkPoint)
+ {
+ fprintf(stderr, _("%s: Checkpoint redo record is not found\n"),
+ progname);
+ }
+
+ /*
+ * We can not know clearly if we had reached the end. But just check if we
+ * reach the last segment file, if it is not, then some problem there.
+ */
+ if (readSegNo != LastXLogFile->segno)
+ {
+ fprintf(stderr, _("%s: Finished reading xlog segno:%lu is not matching"
+ " with last xlog file from directory:%lu\n"),
+ progname, readSegNo, LastXLogFile->segno);
+ }
+
+ if (readFile >= 0)
+ {
+ close(readFile);
+ readFile = -1;
+ }
+
+ if (readRecordBuf)
+ {
+ free(readRecordBuf);
+ readRecordBufSize = 0;
+ }
+
+READ_BUF_FREE:
+ free(readBuf);
+ return bFoundChkPoint;
+}
+
+/*
+ * compare two xlog file from their name to see which one is latest.
+ * Return true for file 1 is the lastest file.
+ */
+static bool
+CompareXLogFileNames(XLogFileName * f1, XLogFileName * f2)
+{
+ if (f2->tli >= f1->tli)
+ {
+ if (f2->segno > f1->segno)
+ return false;
+ }
+
+ return true;
+}
+
+
+
+/*
+ * Add the file which had been found in the xlog folder into xlogfilelist.
+ * The xlogfile list is matained in a increasing order.
+ *
+ * The input parameter is the name of the xlog file, the name is assumpted
+ * valid.
+ */
+static void
+AddXLogFileIntoList(char *fname)
+{
+ XLogFileName *NewSegFile,
+ **currp;
+ char path[MAXPGPATH];
+
+ /* Allocate a new node for the new file. */
+ NewSegFile = (XLogFileName *) malloc(sizeof(XLogFileName));
+ if (NULL == NewSegFile)
+ {
+ fprintf(stderr, _("%s: Memory allocation failed for size:%lu\n"),
+ progname, sizeof(XLogFileName));
+ exit(1);
+ }
+
+ strcpy(NewSegFile->fname, fname); /* setup the name */
+
+ /* extract the time line, xlog segment number from the name. */
+ XLogFromFileName(fname, &(NewSegFile->tli), &(NewSegFile->segno));
+
+ NewSegFile->next = NULL;
+
+ /* Ensure the xlog file is active and valid. */
+ snprintf(path, MAXPGPATH, "%s/%s", XLOGDIR, NewSegFile->fname);
+ readFile = open(path, O_RDONLY | PG_BINARY, 0);
+ if (readFile < 0)
+ {
+ fprintf(stderr, _("%s: Can not open xlog file %s.\n"), progname, path);
+ free(NewSegFile);
+ return;
+ }
+
+ if (read(readFile, readBuf, XLOG_BLCKSZ) != XLOG_BLCKSZ)
+ {
+ fprintf(stderr, _("%s: Can not read xlog file %s.\n"), progname, path);
+ free(NewSegFile);
+ goto insert_failure;
+ }
+
+ if (!ValidXLOGPageHeader((XLogPageHeader) readBuf,
+ NewSegFile->tli,
+ NewSegFile->segno))
+ {
+ free(NewSegFile);
+ goto insert_failure;
+ }
+
+ /* try to search the list and find the insert point. */
+ currp = &xlogfilelist;
+
+ while (*currp && CompareXLogFileNames(NewSegFile, *currp))
+ currp = &((*currp)->next);
+
+ NewSegFile->next = *currp;
+ *currp = NewSegFile;
+
+insert_failure:
+ close(readFile);
+ readFile = -1;
+ return;
+}
+
+/*
+ * validates XLOG directory structure
+ */
+static void
+ValidateXLOGDirectoryStructure(void)
+{
+ char path[MAXPGPATH];
+ struct stat stat_buf;
+
+ /* Check for pg_xlog; if it doesn't exist, error out */
+ if (stat(XLOGDIR, &stat_buf) != 0
+ || !S_ISDIR(stat_buf.st_mode))
+ {
+ fprintf(stderr, _("%s: required WAL directory \"%s\" does not exist\n"),
+ progname, XLOGDIR);
+ exit(1);
+ }
+
+ /* Check for archive_status */
+ snprintf(path, MAXPGPATH, XLOGDIR "/archive_status");
+ if ((stat(path, &stat_buf) != 0)
+ || !S_ISDIR(stat_buf.st_mode))
+ {
+ fprintf(stderr, _("%s: required WAL directory \"%s\" does not exist\n"),
+ progname, path);
+ exit(1);
+ }
+}
+
+/*
+ * Check if the file is a valid xlog file.
+ * Return true for the input file is a valid xlog file.
+ * The input parameter is the name of the xlog file.
+ */
+static bool
+ValidXLogFileName(char *fname)
+{
+ uint32 logTLI,
+ logId,
+ logSeg;
+
+ if (strlen(fname) != 24 ||
+ strspn(fname, "0123456789ABCDEF") != 24 ||
+ sscanf(fname, "%8x%8x%8x", &logTLI, &logId, &logSeg) != 3)
+ return false;
+ return true;
+
+}
+
+/* Ensure the xlog page header is valid.*/
+static bool
+ValidXLOGPageHeader(XLogPageHeader hdr, uint32 tli, XLogSegNo segno)
+{
+ XLogRecPtr recaddr;
+
+ if (hdr->xlp_magic != XLOG_PAGE_MAGIC)
+ {
+ return false;
+ }
+
+ if ((hdr->xlp_info & ~XLP_ALL_FLAGS) != 0)
+ {
+ return false;
+ }
+
+ if (hdr->xlp_info & XLP_LONG_HEADER)
+ {
+ XLogLongPageHeader longhdr = (XLogLongPageHeader) hdr;
+
+ if (longhdr->xlp_seg_size != XLogSegSize)
+ {
+ return false;
+ }
+
+ if (longhdr->xlp_xlog_blcksz != XLOG_BLCKSZ)
+ {
+ return false;
+ }
+
+ /* Get the system identifier from the segment file header. */
+ ControlFile.system_identifier = ((XLogLongPageHeader) longhdr)->xlp_sysid;
+ }
+ else if (0 == readOff)
+ {
+ /* As first page of file doesn't have a long header */
+ return false;
+ }
+
+ XLogSegNoOffsetToRecPtr(segno, readOff, recaddr);
+
+ if (!XLByteEQ(hdr->xlp_pageaddr, recaddr))
+ {
+ return false;
+ }
+
+ if (hdr->xlp_tli != tli)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Validate an XLOG record header.
+ */
+static bool
+ValidXLogRecordHeader(XLogRecPtr *RecPtr, XLogRecord *record, bool randAccess)
+{
+ /*
+ * xl_len == 0 is bad data for everything except XLOG SWITCH, where it is
+ * required.
+ */
+ if (record->xl_rmid == RM_XLOG_ID && record->xl_info == XLOG_SWITCH)
+ {
+ if (record->xl_len != 0)
+ {
+ return false;
+ }
+ }
+ else if (record->xl_len == 0)
+ {
+ return false;
+ }
+
+ if (record->xl_tot_len < SizeOfXLogRecord + record->xl_len ||
+ record->xl_tot_len > SizeOfXLogRecord + record->xl_len +
+ XLR_MAX_BKP_BLOCKS * (sizeof(BkpBlock) + BLCKSZ))
+ {
+ return false;
+ }
+ if (record->xl_rmid > RM_MAX_ID)
+ {
+ return false;
+ }
+
+ if (randAccess)
+ {
+ /*
+ * We can't exactly verify the prev-link, but surely it should be less
+ * than the record's own address.
+ */
+ if (!XLByteLT(record->xl_prev, *RecPtr))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ /*
+ * Record's prev-link should exactly match our previous location. This
+ * check guards against torn WAL pages where a stale but valid-looking
+ * WAL record starts on a sector boundary.
+ */
+ if (!XLByteEQ(record->xl_prev, ReadRecPtr))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Prepare all the existing XLOG files into a list
+ */
+static bool
+PrepareXLogFileList(void)
+{
+ DIR *xldir;
+ struct dirent *xlde;
+
+ ValidateXLOGDirectoryStructure();
+
+ /* Open the xlog direcotry. */
+ xldir = opendir(XLOGDIR);
+ if (xldir == NULL)
+ {
+ fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
+ progname, XLOGDIR, strerror(errno));
+ return false;
+ }
+
+ /* Search the directory, insert the segment files into the xlogfilelist. */
+ errno = 0;
+ while ((xlde = readdir(xldir)) != NULL)
+ {
+ if (ValidXLogFileName(xlde->d_name))
+ {
+ /* XLog file is found, insert it into the xlogfilelist. */
+ AddXLogFileIntoList(xlde->d_name);
+ }
+ errno = 0;
+ }
+#ifdef WIN32
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ errno = 0;
+#endif
+
+ if (errno)
+ {
+ fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
+ progname, XLOGDIR, strerror(errno));
+ closedir(xldir);
+ return false;
+ }
+
+ closedir(xldir);
+ return true;
+}
+
+/* check is two segment file is continous.*/
+static bool
+IsNextSeg(XLogFileName * prev, XLogFileName * cur)
+{
+ uint32 logid;
+ uint32 logsegno;
+ XLogSegNo nextSegno;
+
+ if (prev->tli != cur->tli)
+ return false;
+
+ logid = prev->segno / XLogSegmentsPerXLogId;
+ logsegno = prev->segno % XLogSegmentsPerXLogId;
+
+ if ((logsegno + 1) < XLogSegmentsPerXLogId)
+ {
+ logsegno++;
+ }
+ else
+ {
+ logsegno = 0;
+ logid++;
+ }
+
+ nextSegno = (uint64) logid *XLogSegmentsPerXLogId + logsegno;
+
+ if (nextSegno == cur->segno)
+ return true;
+
+ return false;
+}
+
+/*
+ * Select the oldest xlog file in the latest time line to start
+ * the search for checkpoint redo record.
+ */
+static bool
+GetStartXLogFile(void)
+{
+ XLogFileName *tmp;
+
+ if (NULL == xlogfilelist)
+ {
+ return false;
+ }
+
+ tmp = CurXLogFile = xlogfilelist;
+
+ while (tmp->next != NULL)
+ {
+ /*
+ * we should ensure that from the first to the last segment file is
+ * continous.
+ */
+ if (!IsNextSeg(tmp, tmp->next))
+ {
+ CurXLogFile = tmp->next;
+ }
+
+ tmp = tmp->next;
+ }
+
+ LastXLogFile = tmp;
+ return true;
+}
+
+/*
+ * CRC-check an XLOG record. We do not believe the contents of an XLOG
+ * record (other than to the minimal extent of computing the amount of
+ * data to read in) until we've checked the CRCs.
+ *
+ * We assume all of the record has been read into memory at *record.
+ */
+static bool
+RecordIsValid(XLogRecord *record)
+{
+ pg_crc32 crc;
+ int i;
+ uint32 len = record->xl_len;
+ BkpBlock bkpb;
+ char *blk;
+
+ /* First the rmgr data */
+ INIT_CRC32(crc);
+ COMP_CRC32(crc, XLogRecGetData(record), len);
+
+ /* Add in the backup blocks, if any */
+ blk = (char *) XLogRecGetData(record) + len;
+ for (i = 0; i < XLR_MAX_BKP_BLOCKS; i++)
+ {
+ uint32 blen;
+
+ if (!(record->xl_info & XLR_SET_BKP_BLOCK(i)))
+ continue;
+
+ memcpy(&bkpb, blk, sizeof(BkpBlock));
+ if (bkpb.hole_offset + bkpb.hole_length > BLCKSZ)
+ {
+ return false;
+ }
+ blen = sizeof(BkpBlock) + BLCKSZ - bkpb.hole_length;
+ COMP_CRC32(crc, blk, blen);
+ blk += blen;
+ }
+
+ /* Check that xl_tot_len agrees with our calculation */
+ if (blk != (char *) record + record->xl_tot_len)
+ {
+ return false;
+ }
+
+ /* Finally include the record header */
+ COMP_CRC32(crc, (char *) record, offsetof(XLogRecord, xl_crc));
+ FIN_CRC32(crc);
+
+ if (!EQ_CRC32(record->xl_crc, crc))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+/* Clean up the allocated list.*/
+static void
+CleanUpList(XLogFileName * list)
+{
+ XLogFileName *tmp;
+
+ tmp = list;
+ while (list != NULL)
+ {
+ tmp = list->next;
+ free(list);
+ list = tmp;
+ }
+}
+
+/*
+ * Reads one XLOG page.
+ */
+static bool
+XLogPageRead(XLogRecPtr *RecPtr)
+{
+ bool switchlog = false;
+ uint32 targetPageOff;
+ uint32 targetRecOff;
+ XLogSegNo targetSegNo;
+ char path[MAXPGPATH];
+
+ XLByteToSeg(*RecPtr, targetSegNo);
+ targetPageOff = (((*RecPtr) % XLogSegSize) / XLOG_BLCKSZ) * XLOG_BLCKSZ;
+ targetRecOff = (*RecPtr) % XLOG_BLCKSZ;
+
+ /* Fast exit if we have read the record in the current buffer already */
+ if ((targetSegNo == readSegNo)
+ && (targetPageOff == readOff)
+ && (targetRecOff < XLOG_BLCKSZ))
+ return true;
+
+ /*
+ * See if we need to switch to a new segment because the requested record
+ * is not in the currently open one.
+ */
+ if (readFile >= 0 && !XLByteInSeg(*RecPtr, readSegNo))
+ {
+ close(readFile);
+ readFile = -1;
+ }
+
+ XLByteToSeg(*RecPtr, readSegNo);
+
+ if (readFile < 0)
+ {
+ if (!CurXLogFile)
+ {
+ fprintf(stderr, _("%s: No next file is found to read. "
+ "Incomplete xlog files.\n"), progname);
+ return false;
+ }
+
+ /* Open a Xlog segment file. */
+ snprintf(path, MAXPGPATH, "%s/%s", XLOGDIR, CurXLogFile->fname);
+
+ readFile = open(path, O_RDONLY | PG_BINARY, 0);
+ if (readFile < 0)
+ {
+ fprintf(stderr, _("%s: Can not open xlog file %s.\n"), progname, path);
+ return false;
+ }
+
+ readTli = CurXLogFile->tli;
+
+ switchlog = true;
+ CurXLogFile = CurXLogFile->next;
+ }
+
+ /*
+ * As the following case is not possible, because the search always starts
+ * from first page.
+ */
+ if (switchlog && targetPageOff != 0)
+ {
+ Assert(0);
+ }
+
+ /* Read the requested page */
+ readOff = targetPageOff;
+ if (lseek(readFile, (off_t) readOff, SEEK_SET) < 0)
+ return false;
+
+ if (read(readFile, readBuf, XLOG_BLCKSZ) != XLOG_BLCKSZ)
+ return false;
+
+ if (!ValidXLOGPageHeader((XLogPageHeader) readBuf, readTli, readSegNo))
+ return false;
+
+ Assert(targetSegNo == readSegNo);
+ Assert(targetPageOff == readOff);
+
+ return true;
+}
+
+/*
+ * Attempt to read an XLOG record.
+ */
+static XLogRecord *
+ReadRecord(XLogRecPtr *RecPtr, bool randAccess, bool *bContRec)
+{
+ uint32 targetRecOff;
+ XLogRecord *record;
+ uint32 len;
+ uint32 total_len;
+ uint32 pageHeaderSize;
+ bool gotheader = false;
+ XLogPageHeader pageHeader;
+
+ /* Read the page of the record */
+ if (!XLogPageRead(RecPtr))
+ return NULL;
+
+ pageHeaderSize = XLogPageHeaderSize((XLogPageHeader) readBuf);
+ targetRecOff = (*RecPtr) % XLOG_BLCKSZ;
+ if (targetRecOff == 0)
+ {
+ (*RecPtr) += pageHeaderSize;
+ targetRecOff = pageHeaderSize;
+ }
+ else if (targetRecOff < pageHeaderSize)
+ {
+ return NULL;
+ }
+
+ pageHeader = (XLogPageHeader) readBuf;
+
+ /*
+ * Check the first record of a page is a continuous record or not
+ */
+ if (!(*bContRec)
+ && randAccess
+ && (pageHeader->xlp_info & XLP_FIRST_IS_CONTRECORD))
+ {
+ *bContRec = true;
+ }
+ else
+ *bContRec = false;
+
+ /*
+ * NB: Even though we use an XLogRecord pointer here, the whole record
+ * header might not fit on this page. xl_tot_len is the first field in
+ * struct, so it must be on this page, but we cannot safely access any
+ * other fields yet.
+ */
+ record = (XLogRecord *) (readBuf + (*RecPtr) % XLOG_BLCKSZ);
+ if (!(*bContRec))
+ {
+ total_len = record->xl_tot_len;
+ }
+ else
+ {
+ total_len = pageHeader->xlp_rem_len;
+ }
+
+ /*
+ * If we got the whole header already, validate it immediately. Otherwise
+ * we validate it after reading the rest of the header from the next page.
+ */
+ if (*bContRec)
+ {
+ gotheader = true;
+ }
+ else if (targetRecOff <= (XLOG_BLCKSZ - SizeOfXLogRecord))
+ {
+ if (!ValidXLogRecordHeader(RecPtr, record, randAccess))
+ return NULL;
+ gotheader = true;
+ }
+
+ /*
+ * Allocate or enlarge readRecordBuf as needed. To avoid useless small
+ * increases, round its size to a multiple of BLCKSZ, and make sure it's
+ * at least 4*BLCKSZ + XLOG_BLCKSZ to start with. (That is enough for all
+ * "normal" records, but very large commit or abort records might need
+ * more space.)
+ */
+ if (total_len > readRecordBufSize)
+ {
+ uint32 newSize = total_len;
+
+ newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ);
+ newSize = Max(newSize, (4 * BLCKSZ) + XLOG_BLCKSZ);
+
+ if (readRecordBuf)
+ free(readRecordBuf);
+
+ readRecordBuf = (char *) malloc(newSize);
+ if (!readRecordBuf)
+ {
+ fprintf(stderr, _("%s: Memory allocation failed for size: %u\n"),
+ progname, newSize);
+ readRecordBufSize = 0;
+ exit(1);
+ }
+
+ readRecordBufSize = newSize;
+ }
+
+ len = XLOG_BLCKSZ - (*RecPtr) % XLOG_BLCKSZ; /* available in block */
+ if (total_len > len)
+ {
+ /* Need to reassemble record */
+ char *contrecord;
+ XLogRecPtr pagelsn;
+ char *buffer;
+ uint32 gotlen;
+
+ /* Initialize pagelsn to the beginning of the page this record is on */
+ pagelsn = ((*RecPtr) / XLOG_BLCKSZ) * XLOG_BLCKSZ;
+
+ /* Copy the first fragment of the record from the first page. */
+ memcpy(readRecordBuf, readBuf + (*RecPtr) % XLOG_BLCKSZ, len);
+ buffer = readRecordBuf + len;
+ gotlen = len;
+
+ do
+ {
+ /* Calculate pointer to beginning of next page */
+ XLByteAdvance(pagelsn, XLOG_BLCKSZ);
+
+ /* Wait for the next page to become available */
+ if (!XLogPageRead(&pagelsn))
+ return NULL;
+
+ /* Check that the continuation on next page looks valid */
+ pageHeader = (XLogPageHeader) readBuf;
+ if (!(pageHeader->xlp_info & XLP_FIRST_IS_CONTRECORD))
+ {
+ return NULL;
+ }
+
+ /*
+ * Cross-check that xlp_rem_len agrees with how much of the record
+ * we expect there to be left.
+ */
+ if (pageHeader->xlp_rem_len == 0 ||
+ total_len != (pageHeader->xlp_rem_len + gotlen))
+ {
+ return NULL;
+ }
+
+ /* Append the continuation from this page to the buffer */
+ pageHeaderSize = XLogPageHeaderSize(pageHeader);
+ contrecord = (char *) readBuf + pageHeaderSize;
+ len = XLOG_BLCKSZ - pageHeaderSize;
+
+ if (pageHeader->xlp_rem_len < len)
+ len = pageHeader->xlp_rem_len;
+
+ memcpy(buffer, (char *) contrecord, len);
+ buffer += len;
+ gotlen += len;
+
+ /* If we just reassembled the record header, validate it. */
+ if (!gotheader)
+ {
+ record = (XLogRecord *) readRecordBuf;
+ if (!ValidXLogRecordHeader(RecPtr, record, randAccess))
+ return NULL;
+
+ gotheader = true;
+ }
+ } while (pageHeader->xlp_rem_len > len);
+
+ record = (XLogRecord *) readRecordBuf;
+ if (!(*bContRec))
+ {
+ if (!RecordIsValid(record))
+ return NULL;
+ }
+
+ pageHeaderSize = XLogPageHeaderSize((XLogPageHeader) readBuf);
+ ReadRecPtr = *RecPtr;
+ XLogSegNoOffsetToRecPtr(
+ readSegNo,
+ readOff + pageHeaderSize + MAXALIGN(pageHeader->xlp_rem_len),
+ EndRecPtr);
+ }
+ else
+ {
+ /* Record does not cross a page boundary */
+ if (!(*bContRec))
+ {
+ if (!RecordIsValid(record))
+ return NULL;
+ }
+
+ ReadRecPtr = *RecPtr;
+ EndRecPtr = *RecPtr + MAXALIGN(total_len);
+ memcpy(readRecordBuf, record, total_len);
+ }
+
+ /*
+ * Special processing if it's an XLOG SWITCH record
+ */
+ if (record->xl_rmid == RM_XLOG_ID && record->xl_info == XLOG_SWITCH)
+ {
+ /* Pretend it extends to end of segment */
+ EndRecPtr += XLogSegSize - 1;
+ EndRecPtr -= EndRecPtr % XLogSegSize;
+
+ readOff = XLogSegSize - XLOG_BLCKSZ;
+ }
+
+ return record;
+}
static void
usage(void)
@@ -994,6 +1994,7 @@ usage(void)
printf(_("Options:\n"));
printf(_(" -e XIDEPOCH set next transaction ID epoch\n"));
printf(_(" -f force update to be done\n"));
+ printf(_(" -r restore the control values if possible\n"));
printf(_(" -l xlogfile force minimum WAL starting location for new transaction log\n"));
printf(_(" -m XID set next multitransaction ID\n"));
printf(_(" -n no update, just show extracted control values (for testing)\n"));