]> 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:46:43 +0000 (14:46 +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 2344ec25bcf538c818f917e6ff034455492aa5ea..de0a2ed7961fb37be7ae6b1b84811b7fd3e9c106 100644 (file)
@@ -650,25 +650,24 @@ ginRedoDeleteListPages(XLogRecPtr lsn, XLogRecord *record)
        PageSetTLI(metapage, ThisTimeLineID);
        MarkBufferDirty(metabuffer);
 
+       /*
+        * No full-page images are taken of the deleted pages. Instead, they are
+        * re-initialized as empty, deleted pages.
+        */
        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);
 }