postgres.git / summary / log / commit / refs

commit    d1166af192ed0bb40ed9dc53d4e799117403233b
Author:   Alexander Korotkov <akorotkov@postgresql.org>
Date:     Sun Mar 24 12:26:45 2019 +0000
First released in: 9.4.22, 9.5.17, 9.6.13, 10.8, 11.3

    Fix WAL format incompatibility introduced by backpatching of 52ac6cd2d0
    
    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 | 18 ++++++++++++++++++ src/include/access/gin_private.h | 14 ++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c index 76dabf19736..11b2da16312 100644 --- a/src/backend/access/gin/ginxlog.c +++ b/src/backend/access/gin/ginxlog.c @@ -720,12 +720,30 @@ ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record) else { dbuffer = XLogReadBuffer(data->node, data->blkno, false); + + /* + * 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(record->xl_len == sizeof(ginxlogDeletePage) || + record->xl_len == sizeof(ginxlogDeletePageOld)); + if (BufferIsValid(dbuffer)) { page = BufferGetPage(dbuffer); if (lsn > PageGetLSN(page)) { Assert(GinPageIsData(page)); + if (record->xl_len == sizeof(ginxlogDeletePage)) + GinPageSetDeleteXid(page, data->deleteXid); GinPageGetOpaque(page)->flags = GIN_DELETED; PageSetLSN(page, lsn); MarkBufferDirty(dbuffer); diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h index 6b1ada5d006..54e55940eb0 100644 --- a/src/include/access/gin_private.h +++ b/src/include/access/gin_private.h @@ -586,6 +586,20 @@ 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 +{ + RelFileNode node; + BlockNumber blkno; + BlockNumber parentBlkno; + OffsetNumber parentOffset; + BlockNumber leftBlkno; + BlockNumber rightLink; +} ginxlogDeletePageOld; + #define XLOG_GIN_UPDATE_META_PAGE 0x60 typedef struct ginxlogUpdateMeta [parent: 432356a9def4]