]> granicus.if.org Git - postgresql/commitdiff
In generic WAL application and replay, ensure page "hole" is always zero.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 12 Apr 2016 15:13:52 +0000 (11:13 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 12 Apr 2016 15:14:00 +0000 (11:14 -0400)
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

src/backend/access/transam/generic_xlog.c

index 072838a5e7a82eecd1fd6cec0d044b6fa621bbe5..e07bd836d2ef5c2163a62344abb6ae93d0089d92 100644 (file)
@@ -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]);
                }