]> granicus.if.org Git - postgresql/commitdiff
Fix WAL format incompatibility introduced by backpatching of 52ac6cd2d0
authorAlexander Korotkov <akorotkov@postgresql.org>
Sun, 24 Mar 2019 12:26:45 +0000 (15:26 +0300)
committerAlexander Korotkov <akorotkov@postgresql.org>
Sun, 24 Mar 2019 12:41:32 +0000 (15:41 +0300)
52ac6cd2d0 added new field to ginxlogDeletePage and was backpatched to 9.4.
That led to problems when patched postgres instance applies WAL records
generated by non-patched one.  WAL records generated by non-patched instance
don't contain new field, which patched one is expecting to see.

Thankfully, we can distinguish patched and non-patched WAL records by their data
size.  If we see that WAL record is generated by non-patched instance, we skip
processing of new field.  This commit comes with some assertions.  In
particular, if it appears that on some platform struct data size didn't change
then static assertion will trigger.

Reported-by: Simon Riggs
Discussion: https://postgr.es/m/CANP8%2Bj%2BK4whxf7ET7%2BgO%2BG-baC3-WxqqH%3DnV4X2CgfEPA3Yu3g%40mail.gmail.com
Author: Alexander Korotkov
Reviewed-by: Simon Riggs, Alvaro Herrera
Backpatch-through: 9.4

src/backend/access/gin/ginxlog.c
src/include/access/ginxlog.h

index 291f22ef77faf406085a1e75209e74720573d977..b86990f3ac7af6fafeb7ba33d63cf131e4a78922 100644 (file)
@@ -531,7 +531,24 @@ ginRedoDeletePage(XLogReaderState *record)
                page = BufferGetPage(dbuffer);
                Assert(GinPageIsData(page));
                GinPageGetOpaque(page)->flags = GIN_DELETED;
-               GinPageSetDeleteXid(page, data->deleteXid);
+
+               /*
+                * deleteXid field of ginxlogDeletePage was added during backpatching.
+                * But, non-backpatched instances will continue generate WAL without
+                * this field.  We should be able to correctly apply that.  We can
+                * distinguish new WAL records by size their data, because
+                * ginxlogDeletePage changes its size on both 32-bit and 64-bit
+                * platforms.
+                */
+               StaticAssertStmt(sizeof(ginxlogDeletePage) !=
+                                                sizeof(ginxlogDeletePageOld),
+                                                "ginxlogDeletePage size should be changed "
+                                                "with addition of deleteXid field");
+               Assert(XLogRecGetDataLen(record) == sizeof(ginxlogDeletePage) ||
+                          XLogRecGetDataLen(record) == sizeof(ginxlogDeletePageOld));
+               if (XLogRecGetDataLen(record) == sizeof(ginxlogDeletePage))
+                       GinPageSetDeleteXid(page, data->deleteXid);
+
                PageSetLSN(page, lsn);
                MarkBufferDirty(dbuffer);
        }
index a86735b7ed80f414c73ab20b06d49456d9c494ac..e05cf42c9ec9cf9c95014c5b8b7b195831a99272 100644 (file)
@@ -161,6 +161,16 @@ typedef struct ginxlogDeletePage
        TransactionId deleteXid;        /* last Xid which could see this page in scan */
 } ginxlogDeletePage;
 
+/*
+ * Previous version of ginxlogDeletePage struct, which didn't have deleteXid
+ * field.  Used for size comparison (see ginRedoDeletePage()).
+ */
+typedef struct ginxlogDeletePageOld
+{
+       OffsetNumber parentOffset;
+       BlockNumber rightLink;
+} ginxlogDeletePageOld;
+
 #define XLOG_GIN_UPDATE_META_PAGE 0x60
 
 /*