]> granicus.if.org Git - postgresql/commitdiff
Protect against torn pages when deleting GIN list pages.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 8 May 2014 11:43:04 +0000 (14:43 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 8 May 2014 11:43:39 +0000 (14:43 +0300)
To-be-deleted list pages contain no useful information, as they are being
deleted, but we must still protect the writes from being torn by a crash
after a partial write. To do that, re-initialize the pages on WAL replay.

Jeff Janes caught this with a test program to test partial writes.
Backpatch to all supported versions.

src/backend/access/gin/ginxlog.c

index 386bcb3af6fd1440ac6863e0cf81033e6eb40761..660364961533ab92e65bfd3d548fafcc654ef175 100644 (file)
@@ -698,26 +698,26 @@ ginRedoDeleteListPages(XLogRecPtr lsn, XLogRecord *record)
         * cannot get past a reader that is on, or due to visit, any page we are
         * going to delete.  New incoming readers will block behind our metapage
         * lock and then see a fully updated page list.
+        *
+        * No full-page images are taken of the deleted pages. Instead, they are
+        * re-initialized as empty, deleted pages. Their right-links don't need to
+        * be preserved, because no new readers can see the pages, as explained
+        * above.
         */
        for (i = 0; i < data->ndeleted; i++)
        {
-               Buffer          buffer = XLogReadBuffer(data->node, data->toDelete[i], false);
+               Buffer          buffer;
+               Page            page;
 
-               if (BufferIsValid(buffer))
-               {
-                       Page            page = BufferGetPage(buffer);
-
-                       if (!XLByteLE(lsn, PageGetLSN(page)))
-                       {
-                               GinPageGetOpaque(page)->flags = GIN_DELETED;
+               buffer = XLogReadBuffer(data->node, data->toDelete[i], true);
+               page = BufferGetPage(buffer);
+               GinInitBuffer(buffer, GIN_DELETED);
 
-                               PageSetLSN(page, lsn);
-                               PageSetTLI(page, ThisTimeLineID);
-                               MarkBufferDirty(buffer);
-                       }
+               PageSetLSN(page, lsn);
+               PageSetTLI(page, ThisTimeLineID);
+               MarkBufferDirty(buffer);
 
-                       UnlockReleaseBuffer(buffer);
-               }
+               UnlockReleaseBuffer(buffer);
        }
        UnlockReleaseBuffer(metabuffer);
 }