summaryrefslogtreecommitdiff
path: root/src/backend/access/transam/xlogreader.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/transam/xlogreader.c')
-rw-r--r--src/backend/access/transam/xlogreader.c104
1 files changed, 75 insertions, 29 deletions
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index dcf747c633..c3b1371764 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -3,7 +3,7 @@
* xlogreader.c
* Generic XLog reading facility
*
- * Portions Copyright (c) 2013-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 2013-2017, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/backend/access/transam/xlogreader.c
@@ -462,7 +462,8 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
/*
* Special processing if it's an XLOG SWITCH record
*/
- if (record->xl_rmid == RM_XLOG_ID && record->xl_info == XLOG_SWITCH)
+ if (record->xl_rmid == RM_XLOG_ID &&
+ (record->xl_info & ~XLR_INFO_MASK) == XLOG_SWITCH)
{
/* Pretend it extends to end of segment */
state->EndRecPtr += XLogSegSize - 1;
@@ -866,46 +867,83 @@ XLogRecPtr
XLogFindNextRecord(XLogReaderState *state, XLogRecPtr RecPtr)
{
XLogReaderState saved_state = *state;
- XLogRecPtr targetPagePtr;
XLogRecPtr tmpRecPtr;
- int targetRecOff;
XLogRecPtr found = InvalidXLogRecPtr;
- uint32 pageHeaderSize;
XLogPageHeader header;
- int readLen;
char *errormsg;
Assert(!XLogRecPtrIsInvalid(RecPtr));
- targetRecOff = RecPtr % XLOG_BLCKSZ;
+ /*
+ * skip over potential continuation data, keeping in mind that it may span
+ * multiple pages
+ */
+ tmpRecPtr = RecPtr;
+ while (true)
+ {
+ XLogRecPtr targetPagePtr;
+ int targetRecOff;
+ uint32 pageHeaderSize;
+ int readLen;
- /* scroll back to page boundary */
- targetPagePtr = RecPtr - targetRecOff;
+ /*
+ * Compute targetRecOff. It should typically be equal or greater than
+ * short page-header since a valid record can't start anywhere before
+ * that, except when caller has explicitly specified the offset that
+ * falls somewhere there or when we are skipping multi-page
+ * continuation record. It doesn't matter though because
+ * ReadPageInternal() is prepared to handle that and will read at
+ * least short page-header worth of data
+ */
+ targetRecOff = tmpRecPtr % XLOG_BLCKSZ;
- /* Read the page containing the record */
- readLen = ReadPageInternal(state, targetPagePtr, targetRecOff);
- if (readLen < 0)
- goto err;
+ /* scroll back to page boundary */
+ targetPagePtr = tmpRecPtr - targetRecOff;
- header = (XLogPageHeader) state->readBuf;
+ /* Read the page containing the record */
+ readLen = ReadPageInternal(state, targetPagePtr, targetRecOff);
+ if (readLen < 0)
+ goto err;
- pageHeaderSize = XLogPageHeaderSize(header);
+ header = (XLogPageHeader) state->readBuf;
- /* make sure we have enough data for the page header */
- readLen = ReadPageInternal(state, targetPagePtr, pageHeaderSize);
- if (readLen < 0)
- goto err;
+ pageHeaderSize = XLogPageHeaderSize(header);
- /* skip over potential continuation data */
- if (header->xlp_info & XLP_FIRST_IS_CONTRECORD)
- {
- /* record headers are MAXALIGN'ed */
- tmpRecPtr = targetPagePtr + pageHeaderSize
- + MAXALIGN(header->xlp_rem_len);
- }
- else
- {
- tmpRecPtr = targetPagePtr + pageHeaderSize;
+ /* make sure we have enough data for the page header */
+ readLen = ReadPageInternal(state, targetPagePtr, pageHeaderSize);
+ if (readLen < 0)
+ goto err;
+
+ /* skip over potential continuation data */
+ if (header->xlp_info & XLP_FIRST_IS_CONTRECORD)
+ {
+ /*
+ * If the length of the remaining continuation data is more than
+ * what can fit in this page, the continuation record crosses over
+ * this page. Read the next page and try again. xlp_rem_len in the
+ * next page header will contain the remaining length of the
+ * continuation data
+ *
+ * Note that record headers are MAXALIGN'ed
+ */
+ if (MAXALIGN(header->xlp_rem_len) > (XLOG_BLCKSZ - pageHeaderSize))
+ tmpRecPtr = targetPagePtr + XLOG_BLCKSZ;
+ else
+ {
+ /*
+ * The previous continuation record ends in this page. Set
+ * tmpRecPtr to point to the first valid record
+ */
+ tmpRecPtr = targetPagePtr + pageHeaderSize
+ + MAXALIGN(header->xlp_rem_len);
+ break;
+ }
+ }
+ else
+ {
+ tmpRecPtr = targetPagePtr + pageHeaderSize;
+ break;
+ }
}
/*
@@ -959,6 +997,7 @@ ResetDecoder(XLogReaderState *state)
state->blocks[block_id].in_use = false;
state->blocks[block_id].has_image = false;
state->blocks[block_id].has_data = false;
+ state->blocks[block_id].apply_image = false;
}
state->max_block_id = -1;
}
@@ -1051,6 +1090,7 @@ DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
blk = &state->blocks[block_id];
blk->in_use = true;
+ blk->apply_image = false;
COPY_HEADER_FIELD(&fork_flags, sizeof(uint8));
blk->forknum = fork_flags & BKPBLOCK_FORK_MASK;
@@ -1082,6 +1122,9 @@ DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
COPY_HEADER_FIELD(&blk->bimg_len, sizeof(uint16));
COPY_HEADER_FIELD(&blk->hole_offset, sizeof(uint16));
COPY_HEADER_FIELD(&blk->bimg_info, sizeof(uint8));
+
+ blk->apply_image = ((blk->bimg_info & BKPIMAGE_APPLY) != 0);
+
if (blk->bimg_info & BKPIMAGE_IS_COMPRESSED)
{
if (blk->bimg_info & BKPIMAGE_HAS_HOLE)
@@ -1205,6 +1248,9 @@ DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
if (!blk->in_use)
continue;
+
+ Assert(blk->has_image || !blk->apply_image);
+
if (blk->has_image)
{
blk->bkp_image = ptr;