diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c index 4b52719765f..b7678f3c144 100644 --- a/src/backend/access/gist/gistxlog.c +++ b/src/backend/access/gist/gistxlog.c @@ -177,6 +177,7 @@ gistRedoDeleteRecord(XLogReaderState *record) gistxlogDelete *xldata = (gistxlogDelete *) XLogRecGetData(record); Buffer buffer; Page page; + OffsetNumber *toDelete = xldata->offsets; /* * If we have any conflict processing to do, it must happen before we @@ -203,14 +204,7 @@ gistRedoDeleteRecord(XLogReaderState *record) { page = (Page) BufferGetPage(buffer); - if (XLogRecGetDataLen(record) > SizeOfGistxlogDelete) - { - OffsetNumber *todelete; - - todelete = (OffsetNumber *) ((char *) xldata + SizeOfGistxlogDelete); - - PageIndexMultiDelete(page, todelete, xldata->ntodelete); - } + PageIndexMultiDelete(page, toDelete, xldata->ntodelete); GistClearPageHasGarbage(page); GistMarkTuplesDeleted(page); @@ -609,6 +603,7 @@ gistXLogPageReuse(Relation rel, Relation heaprel, */ /* XLOG stuff */ + xlrec_reuse.isCatalogRel = RelationIsAccessibleInLogicalDecoding(heaprel); xlrec_reuse.locator = rel->rd_locator; xlrec_reuse.block = blkno; xlrec_reuse.snapshotConflictHorizon = deleteXid; @@ -678,6 +673,7 @@ gistXLogDelete(Buffer buffer, OffsetNumber *todelete, int ntodelete, gistxlogDelete xlrec; XLogRecPtr recptr; + xlrec.isCatalogRel = RelationIsAccessibleInLogicalDecoding(heaprel); xlrec.snapshotConflictHorizon = snapshotConflictHorizon; xlrec.ntodelete = ntodelete; diff --git a/src/backend/access/hash/hash_xlog.c b/src/backend/access/hash/hash_xlog.c index f38b42efb90..f2dd9be8d3f 100644 --- a/src/backend/access/hash/hash_xlog.c +++ b/src/backend/access/hash/hash_xlog.c @@ -980,8 +980,10 @@ hash_xlog_vacuum_one_page(XLogReaderState *record) Page page; XLogRedoAction action; HashPageOpaque pageopaque; + OffsetNumber *toDelete; xldata = (xl_hash_vacuum_one_page *) XLogRecGetData(record); + toDelete = xldata->offsets; /* * If we have any conflict processing to do, it must happen before we @@ -1010,14 +1012,7 @@ hash_xlog_vacuum_one_page(XLogReaderState *record) { page = (Page) BufferGetPage(buffer); - if (XLogRecGetDataLen(record) > SizeOfHashVacuumOnePage) - { - OffsetNumber *unused; - - unused = (OffsetNumber *) ((char *) xldata + SizeOfHashVacuumOnePage); - - PageIndexMultiDelete(page, unused, xldata->ntuples); - } + PageIndexMultiDelete(page, toDelete, xldata->ntuples); /* * Mark the page as not containing any LP_DEAD items. See comments in diff --git a/src/backend/access/hash/hashinsert.c b/src/backend/access/hash/hashinsert.c index a604e318919..22656b24e20 100644 --- a/src/backend/access/hash/hashinsert.c +++ b/src/backend/access/hash/hashinsert.c @@ -432,6 +432,7 @@ _hash_vacuum_one_page(Relation rel, Relation hrel, Buffer metabuf, Buffer buf) xl_hash_vacuum_one_page xlrec; XLogRecPtr recptr; + xlrec.isCatalogRel = RelationIsAccessibleInLogicalDecoding(hrel); xlrec.snapshotConflictHorizon = snapshotConflictHorizon; xlrec.ntuples = ndeletable; diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 9662e382549..f7d9ce59a47 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -6698,6 +6698,7 @@ heap_freeze_execute_prepared(Relation rel, Buffer buffer, nplans = heap_log_freeze_plan(tuples, ntuples, plans, offsets); xlrec.snapshotConflictHorizon = snapshotConflictHorizon; + xlrec.isCatalogRel = RelationIsAccessibleInLogicalDecoding(rel); xlrec.nplans = nplans; XLogBeginInsert(); @@ -8280,6 +8281,8 @@ log_heap_visible(Relation rel, Buffer heap_buffer, Buffer vm_buffer, xlrec.snapshotConflictHorizon = snapshotConflictHorizon; xlrec.flags = vmflags; + if (RelationIsAccessibleInLogicalDecoding(rel)) + xlrec.flags |= VISIBILITYMAP_XLOG_CATALOG_REL; XLogBeginInsert(); XLogRegisterData((char *) &xlrec, SizeOfHeapVisible); @@ -8870,6 +8873,8 @@ heap_xlog_visible(XLogReaderState *record) BlockNumber blkno; XLogRedoAction action; + Assert((xlrec->flags & VISIBILITYMAP_XLOG_VALID_BITS) == xlrec->flags); + XLogRecGetBlockTag(record, 1, &rlocator, NULL, &blkno); /* @@ -8956,11 +8961,15 @@ heap_xlog_visible(XLogReaderState *record) { Page vmpage = BufferGetPage(vmbuffer); Relation reln; + uint8 vmbits; /* initialize the page if it was read as zeros */ if (PageIsNew(vmpage)) PageInit(vmpage, BLCKSZ, 0); + /* remove VISIBILITYMAP_XLOG_* */ + vmbits = xlrec->flags & VISIBILITYMAP_VALID_BITS; + /* * XLogReadBufferForRedoExtended locked the buffer. But * visibilitymap_set will handle locking itself. @@ -8971,7 +8980,7 @@ heap_xlog_visible(XLogReaderState *record) visibilitymap_pin(reln, blkno, &vmbuffer); visibilitymap_set(reln, blkno, InvalidBuffer, lsn, vmbuffer, - xlrec->snapshotConflictHorizon, xlrec->flags); + xlrec->snapshotConflictHorizon, vmbits); ReleaseBuffer(vmbuffer); FreeFakeRelcacheEntry(reln); diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index 4e65cbcadf8..3f0342351fb 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -418,6 +418,7 @@ heap_page_prune(Relation relation, Buffer buffer, xl_heap_prune xlrec; XLogRecPtr recptr; + xlrec.isCatalogRel = RelationIsAccessibleInLogicalDecoding(relation); xlrec.snapshotConflictHorizon = prstate.snapshotConflictHorizon; xlrec.nredirected = prstate.nredirected; xlrec.ndead = prstate.ndead; diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index ee996b56602..151ad37a542 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -836,6 +836,7 @@ _bt_log_reuse_page(Relation rel, Relation heaprel, BlockNumber blkno, */ /* XLOG stuff */ + xlrec_reuse.isCatalogRel = RelationIsAccessibleInLogicalDecoding(heaprel); xlrec_reuse.locator = rel->rd_locator; xlrec_reuse.block = blkno; xlrec_reuse.snapshotConflictHorizon = safexid; @@ -1358,6 +1359,7 @@ _bt_delitems_delete(Relation rel, Relation heaprel, Buffer buf, XLogRecPtr recptr; xl_btree_delete xlrec_delete; + xlrec_delete.isCatalogRel = RelationIsAccessibleInLogicalDecoding(heaprel); xlrec_delete.snapshotConflictHorizon = snapshotConflictHorizon; xlrec_delete.ndeleted = ndeletable; xlrec_delete.nupdated = nupdatable; diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c index 3cff71e7203..2f4a4aad241 100644 --- a/src/backend/access/spgist/spgvacuum.c +++ b/src/backend/access/spgist/spgvacuum.c @@ -503,6 +503,7 @@ vacuumRedirectAndPlaceholder(Relation index, Relation heaprel, Buffer buffer) spgxlogVacuumRedirect xlrec; GlobalVisState *vistest; + xlrec.isCatalogRel = RelationIsAccessibleInLogicalDecoding(heaprel); xlrec.nToPlaceholder = 0; xlrec.snapshotConflictHorizon = InvalidTransactionId; diff --git a/src/include/access/gistxlog.h b/src/include/access/gistxlog.h index 2ce9366277d..aff2ffbdcc2 100644 --- a/src/include/access/gistxlog.h +++ b/src/include/access/gistxlog.h @@ -51,11 +51,14 @@ typedef struct gistxlogDelete { TransactionId snapshotConflictHorizon; uint16 ntodelete; /* number of deleted offsets */ + bool isCatalogRel; /* to handle recovery conflict during logical + * decoding on standby */ - /* TODELETE OFFSET NUMBER ARRAY FOLLOWS */ + /* TODELETE OFFSET NUMBERS */ + OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]; } gistxlogDelete; -#define SizeOfGistxlogDelete (offsetof(gistxlogDelete, ntodelete) + sizeof(uint16)) +#define SizeOfGistxlogDelete offsetof(gistxlogDelete, offsets) /* * Backup Blk 0: If this operation completes a page split, by inserting a @@ -98,9 +101,11 @@ typedef struct gistxlogPageReuse RelFileLocator locator; BlockNumber block; FullTransactionId snapshotConflictHorizon; + bool isCatalogRel; /* to handle recovery conflict during logical + * decoding on standby */ } gistxlogPageReuse; -#define SizeOfGistxlogPageReuse (offsetof(gistxlogPageReuse, snapshotConflictHorizon) + sizeof(FullTransactionId)) +#define SizeOfGistxlogPageReuse (offsetof(gistxlogPageReuse, isCatalogRel) + sizeof(bool)) extern void gist_redo(XLogReaderState *record); extern void gist_desc(StringInfo buf, XLogReaderState *record); diff --git a/src/include/access/hash_xlog.h b/src/include/access/hash_xlog.h index 9894ab9afee..b93619d1a81 100644 --- a/src/include/access/hash_xlog.h +++ b/src/include/access/hash_xlog.h @@ -251,13 +251,15 @@ typedef struct xl_hash_init_bitmap_page typedef struct xl_hash_vacuum_one_page { TransactionId snapshotConflictHorizon; - uint16 ntuples; + uint16 ntuples; + bool isCatalogRel; /* to handle recovery conflict during logical + * decoding on standby */ - /* TARGET OFFSET NUMBERS FOLLOW AT THE END */ + /* TARGET OFFSET NUMBERS */ + OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]; } xl_hash_vacuum_one_page; -#define SizeOfHashVacuumOnePage \ - (offsetof(xl_hash_vacuum_one_page, ntuples) + sizeof(uint16)) +#define SizeOfHashVacuumOnePage offsetof(xl_hash_vacuum_one_page, offsets) extern void hash_redo(XLogReaderState *record); extern void hash_desc(StringInfo buf, XLogReaderState *record); diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h index 42620bbdc9e..e71d8489522 100644 --- a/src/include/access/heapam_xlog.h +++ b/src/include/access/heapam_xlog.h @@ -245,10 +245,12 @@ typedef struct xl_heap_prune TransactionId snapshotConflictHorizon; uint16 nredirected; uint16 ndead; + bool isCatalogRel; /* to handle recovery conflict during logical + * decoding on standby */ /* OFFSET NUMBERS are in the block reference 0 */ } xl_heap_prune; -#define SizeOfHeapPrune (offsetof(xl_heap_prune, ndead) + sizeof(uint16)) +#define SizeOfHeapPrune (offsetof(xl_heap_prune, isCatalogRel) + sizeof(bool)) /* * The vacuum page record is similar to the prune record, but can only mark @@ -344,13 +346,15 @@ typedef struct xl_heap_freeze_page { TransactionId snapshotConflictHorizon; uint16 nplans; + bool isCatalogRel; /* to handle recovery conflict during logical + * decoding on standby */ /* * In payload of blk 0 : FREEZE PLANS and OFFSET NUMBER ARRAY */ } xl_heap_freeze_page; -#define SizeOfHeapFreezePage (offsetof(xl_heap_freeze_page, nplans) + sizeof(uint16)) +#define SizeOfHeapFreezePage (offsetof(xl_heap_freeze_page, isCatalogRel) + sizeof(bool)) /* * This is what we need to know about setting a visibility map bit diff --git a/src/include/access/nbtxlog.h b/src/include/access/nbtxlog.h index 7dd67257f29..a0310c96fb1 100644 --- a/src/include/access/nbtxlog.h +++ b/src/include/access/nbtxlog.h @@ -188,9 +188,11 @@ typedef struct xl_btree_reuse_page RelFileLocator locator; BlockNumber block; FullTransactionId snapshotConflictHorizon; + bool isCatalogRel; /* to handle recovery conflict during logical + * decoding on standby */ } xl_btree_reuse_page; -#define SizeOfBtreeReusePage (sizeof(xl_btree_reuse_page)) +#define SizeOfBtreeReusePage (offsetof(xl_btree_reuse_page, isCatalogRel) + sizeof(bool)) /* * xl_btree_vacuum and xl_btree_delete records describe deletion of index @@ -235,6 +237,8 @@ typedef struct xl_btree_delete TransactionId snapshotConflictHorizon; uint16 ndeleted; uint16 nupdated; + bool isCatalogRel; /* to handle recovery conflict during logical + * decoding on standby */ /*---- * In payload of blk 0 : @@ -245,7 +249,7 @@ typedef struct xl_btree_delete */ } xl_btree_delete; -#define SizeOfBtreeDelete (offsetof(xl_btree_delete, nupdated) + sizeof(uint16)) +#define SizeOfBtreeDelete (offsetof(xl_btree_delete, isCatalogRel) + sizeof(bool)) /* * The offsets that appear in xl_btree_update metadata are offsets into the diff --git a/src/include/access/spgxlog.h b/src/include/access/spgxlog.h index b9d6753533d..a7f10bf2d3a 100644 --- a/src/include/access/spgxlog.h +++ b/src/include/access/spgxlog.h @@ -240,6 +240,8 @@ typedef struct spgxlogVacuumRedirect uint16 nToPlaceholder; /* number of redirects to make placeholders */ OffsetNumber firstPlaceholder; /* first placeholder tuple to remove */ TransactionId snapshotConflictHorizon; /* newest XID of removed redirects */ + bool isCatalogRel; /* to handle recovery conflict during logical + * decoding on standby */ /* offsets of redirect tuples to make placeholders follow */ OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]; diff --git a/src/include/access/visibilitymapdefs.h b/src/include/access/visibilitymapdefs.h index 9165b9456b9..8dfdbfa71ef 100644 --- a/src/include/access/visibilitymapdefs.h +++ b/src/include/access/visibilitymapdefs.h @@ -21,5 +21,14 @@ #define VISIBILITYMAP_ALL_FROZEN 0x02 #define VISIBILITYMAP_VALID_BITS 0x03 /* OR of all valid visibilitymap * flags bits */ +/* + * To detect recovery conflicts during logical decoding on a standby, we need + * to know if a table is a user catalog table. For that we add an additional + * bit into xl_heap_visible.flags, in addition to the above. + * + * NB: VISIBILITYMAP_XLOG_* may not be passed to visibilitymap_set(). + */ +#define VISIBILITYMAP_XLOG_CATALOG_REL 0x04 +#define VISIBILITYMAP_XLOG_VALID_BITS (VISIBILITYMAP_VALID_BITS | VISIBILITYMAP_XLOG_CATALOG_REL) #endif /* VISIBILITYMAPDEFS_H */ diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 8edae7bb079..b0fd338a00c 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -31,7 +31,7 @@ /* * Each page of XLOG file has a header like this: */ -#define XLOG_PAGE_MAGIC 0xD112 /* can be used as WAL version indicator */ +#define XLOG_PAGE_MAGIC 0xD113 /* can be used as WAL version indicator */ typedef struct XLogPageHeaderData { diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index c0ddddb2f0d..31f84e90eb7 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -16,6 +16,7 @@ #include "access/tupdesc.h" #include "access/xlog.h" +#include "catalog/catalog.h" #include "catalog/pg_class.h" #include "catalog/pg_index.h" #include "catalog/pg_publication.h"