aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2016-04-12 11:13:52 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2016-04-12 11:14:00 -0400
commitbdf7db81921deb99fd9d489cbcc635906c89e215 (patch)
tree49fb1aa274a68b9a48dbfd1108a98263fd944758
parent813b456ea21d4cf57b124bf855ec019c7a8099a7 (diff)
downloadpostgresql-bdf7db81921deb99fd9d489cbcc635906c89e215.tar.gz
postgresql-bdf7db81921deb99fd9d489cbcc635906c89e215.zip
In generic WAL application and replay, ensure page "hole" is always zero.
The previous coding could allow the contents of the "hole" between pd_lower and pd_upper to diverge during replay from what it had been when the update was originally applied. This would pose a problem if checksums were in use, and in any case would complicate forensic comparisons between master and slave servers. So force the "hole" to contain zeroes, both at initial application of a generically-logged action, and at replay. Alexander Korotkov, adjusted slightly by me
-rw-r--r--src/backend/access/transam/generic_xlog.c40
1 files changed, 37 insertions, 3 deletions
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
index 072838a5e7a..e07bd836d2e 100644
--- a/src/backend/access/transam/generic_xlog.c
+++ b/src/backend/access/transam/generic_xlog.c
@@ -338,17 +338,31 @@ GenericXLogFinish(GenericXLogState *state)
{
PageData *pageData = &state->pages[i];
Page page;
+ PageHeader pageHeader;
if (BufferIsInvalid(pageData->buffer))
continue;
page = BufferGetPage(pageData->buffer, NULL, NULL,
BGP_NO_SNAPSHOT_TEST);
+ pageHeader = (PageHeader) pageData->image;
if (pageData->fullImage)
{
- /* A full page image does not require anything special */
- memcpy(page, pageData->image, BLCKSZ);
+ /*
+ * A full-page image does not require us to supply any xlog
+ * data. Just apply the image, being careful to zero the
+ * "hole" between pd_lower and pd_upper in order to avoid
+ * divergence between actual page state and what replay would
+ * produce.
+ */
+ memcpy(page, pageData->image, pageHeader->pd_lower);
+ memset(page + pageHeader->pd_lower, 0,
+ pageHeader->pd_upper - pageHeader->pd_lower);
+ memcpy(page + pageHeader->pd_upper,
+ pageData->image + pageHeader->pd_upper,
+ BLCKSZ - pageHeader->pd_upper);
+
XLogRegisterBuffer(i, pageData->buffer,
REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
}
@@ -359,7 +373,15 @@ GenericXLogFinish(GenericXLogState *state)
* associated with this page.
*/
computeDelta(pageData, page, (Page) pageData->image);
- memcpy(page, pageData->image, BLCKSZ);
+
+ /* Apply the image, with zeroed "hole" as above */
+ memcpy(page, pageData->image, pageHeader->pd_lower);
+ memset(page + pageHeader->pd_lower, 0,
+ pageHeader->pd_upper - pageHeader->pd_lower);
+ memcpy(page + pageHeader->pd_upper,
+ pageData->image + pageHeader->pd_upper,
+ BLCKSZ - pageHeader->pd_upper);
+
XLogRegisterBuffer(i, pageData->buffer, REGBUF_STANDARD);
XLogRegisterBufData(i, pageData->delta, pageData->deltaLen);
}
@@ -395,6 +417,7 @@ GenericXLogFinish(GenericXLogState *state)
BGP_NO_SNAPSHOT_TEST),
pageData->image,
BLCKSZ);
+ /* We don't worry about zeroing the "hole" in this case */
MarkBufferDirty(pageData->buffer);
}
END_CRIT_SECTION();
@@ -473,6 +496,7 @@ generic_redo(XLogReaderState *record)
if (action == BLK_NEEDS_REDO)
{
Page page;
+ PageHeader pageHeader;
char *blockDelta;
Size blockDeltaSize;
@@ -481,6 +505,16 @@ generic_redo(XLogReaderState *record)
blockDelta = XLogRecGetBlockData(record, block_id, &blockDeltaSize);
applyPageRedo(page, blockDelta, blockDeltaSize);
+ /*
+ * Since the delta contains no information about what's in the
+ * "hole" between pd_lower and pd_upper, set that to zero to
+ * ensure we produce the same page state that application of the
+ * logged action by GenericXLogFinish did.
+ */
+ pageHeader = (PageHeader) page;
+ memset(page + pageHeader->pd_lower, 0,
+ pageHeader->pd_upper - pageHeader->pd_lower);
+
PageSetLSN(page, lsn);
MarkBufferDirty(buffers[block_id]);
}