Making the XLogRecord header nicer has been proposed several times
[1][2][3]. In [2], Robert wondered if with 2 bytes of xl_info, we
could get rid of the separate xl_xact_info. There could be other
possibilities as well, but I haven't done anything in that direction.
The attached just gets as far as the modest goal mentioned in the
subject.

0001: Split xl_info into xl_info for rmgr-specific info and xl_geninfo
for generic flags. I used the XLogInsertExtended() idea from one of
Matthias's patches in [3], but wrote the rest a different way to keep
churn small.

0002 and 0003: To simplify the rmgrs that have an opmask and separate
flag (I saw two obvious ones but I didn't make sure they were the only
ones), reserve the high 4 bits of xl_info for the "record type" (I see
xlogstats.c calls it a "recid", so that might be a better name) and
the lower 4 bits for the flag. Using the same scheme everywhere
simplifies things.

I've put the mask into these macros to reduce the places that know about them:

#define XLogRecGetRecType(decoder) ((decoder)->record->header.xl_info
& XLR_REC_TYPE_MASK)
#define XLogRecGetRecFlags(decoder) ((decoder)->record->header.xl_info
& XLR_REC_FLAGS_MASK)

The former is for callers that explicitly want to know about the
record type only, without flags. The latter is unused, since checking
a mask doesn't need to isolate all the masks first, so it may not end
up in the final version.

There are new XLR_* masks whose names reflect their new purpose
better, but they are the same values as the old ones. XLR_INFO_MASK is
kept around for compatibility, but it doesn't do anything useful since
the rmgr has the full byte available. In order for ~XLR_INFO_MASK to
become a no-op on a full byte, XLR_INFO_MASK has to be zero, which
looks very odd. Removing it would be clearer, at the cost of more
churn.

There is a small wrinkle in that heap_identify does different things
depending on presence of the  XLOG_HEAP_INIT_PAGE flag, but
xact_identify() doesn't do that with XLOG_XACT_HAS_INFO, so the latter
still masks out the flags.

0004: get rid of RM_HEAP2_ID. This was simple once the prerequisites
were in place. All of the HEAP2_* macros keep the same name and only
differ in value. Heap rmgr is completely full so it might be good to
increase to 5 bits for the record type to give it some breathing room.

I wasn't sure about this comment in heapam_xlog.c -- it seems strange
that this property would occur exactly at the point where the first
heap rmgr id ran out of bits, but check world passes without doing
anything additional:

/*
 * These operations don't overwrite MVCC data so no conflict processing is
 * required. The ones in heap2 rmgr do.
 */

[1] 
https://www.postgresql.org/message-id/20220715173731.6t3km5cww3f5ztfq%40awork3.anarazel.de
[2] 
https://www.postgresql.org/message-id/CA%2BTgmoa7pNxxe_K%3D3mTHHZGSmnrc_YgApArx3OFHN2g57nzLNw%40mail.gmail.com
[3] 
https://www.postgresql.org/message-id/CAEze2Wjd3jY_UhhOGdGGnC6NO%3D%2BNmtNOmd%3DJaYv-v-nwBAiXXA%40mail.gmail.com

--
John Naylor
Amazon Web Services
From 08edc80158627bd23018d437e31ee47e2fbd0553 Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Sun, 5 Oct 2025 13:40:15 +0700
Subject: [PATCH v1 2/4] Get rid of XLOG_XACT_OPMASK

Since we can now use the lower 4 bits of xl_info for rmgr-specific
flags, move XLOG_XACT_HAS_INFO into that space, making the op mask
unnecessary.
---
 src/backend/access/rmgrdesc/xactdesc.c    |  4 ++--
 src/backend/access/transam/twophase.c     |  2 +-
 src/backend/access/transam/xact.c         |  2 +-
 src/backend/access/transam/xlogrecovery.c | 17 ++++++++---------
 src/backend/access/transam/xlogstats.c    |  9 ---------
 src/backend/postmaster/walsummarizer.c    |  3 +--
 src/backend/replication/logical/decode.c  |  2 +-
 src/bin/pg_rewind/parsexlog.c             | 11 +++++------
 src/include/access/xact.h                 | 11 ++++-------
 src/include/access/xlogreader.h           |  2 ++
 src/include/access/xlogrecord.h           |  9 +++++++--
 11 files changed, 32 insertions(+), 40 deletions(-)

diff --git a/src/backend/access/rmgrdesc/xactdesc.c b/src/backend/access/rmgrdesc/xactdesc.c
index f0f696855b9..4fe4b0c1eab 100644
--- a/src/backend/access/rmgrdesc/xactdesc.c
+++ b/src/backend/access/rmgrdesc/xactdesc.c
@@ -439,7 +439,7 @@ void
 xact_desc(StringInfo buf, XLogReaderState *record)
 {
 	char	   *rec = XLogRecGetData(record);
-	uint8		info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
+	uint8		info = XLogRecGetRecType(record);
 
 	if (info == XLOG_XACT_COMMIT || info == XLOG_XACT_COMMIT_PREPARED)
 	{
@@ -488,7 +488,7 @@ xact_identify(uint8 info)
 {
 	const char *id = NULL;
 
-	switch (info & XLOG_XACT_OPMASK)
+	switch (info & XLR_REC_TYPE_MASK)
 	{
 		case XLOG_XACT_COMMIT:
 			id = "COMMIT";
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index d8e2fce2c99..9802f5fe944 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -1444,7 +1444,7 @@ XlogReadTwoPhaseData(XLogRecPtr lsn, char **buf, int *len)
 	}
 
 	if (XLogRecGetRmid(xlogreader) != RM_XACT_ID ||
-		(XLogRecGetInfo(xlogreader) & XLOG_XACT_OPMASK) != XLOG_XACT_PREPARE)
+		XLogRecGetRecType(xlogreader) != XLOG_XACT_PREPARE)
 		ereport(ERROR,
 				(errcode_for_file_access(),
 				 errmsg("expected two-phase state data is not present in WAL at %X/%08X",
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index fc06474fc75..29b9ddd3096 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -6376,7 +6376,7 @@ xact_redo_abort(xl_xact_parsed_abort *parsed, TransactionId xid,
 void
 xact_redo(XLogReaderState *record)
 {
-	uint8		info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
+	uint8		info = XLogRecGetRecType(record);
 
 	/* Backup blocks are not used in xact records */
 	Assert(!XLogRecHasAnyBlockRefs(record));
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index 3e3aae0e47c..d445851ac6f 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -2438,8 +2438,7 @@ checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI, TimeLineID prevTLI,
 static bool
 getRecordTimestamp(XLogReaderState *record, TimestampTz *recordXtime)
 {
-	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
-	uint8		xact_info = info & XLOG_XACT_OPMASK;
+	uint8		info = XLogRecGetRecType(record);
 	uint8		rmid = XLogRecGetRmid(record);
 
 	if (rmid == RM_XLOG_ID && info == XLOG_RESTORE_POINT)
@@ -2447,14 +2446,14 @@ getRecordTimestamp(XLogReaderState *record, TimestampTz *recordXtime)
 		*recordXtime = ((xl_restore_point *) XLogRecGetData(record))->rp_time;
 		return true;
 	}
-	if (rmid == RM_XACT_ID && (xact_info == XLOG_XACT_COMMIT ||
-							   xact_info == XLOG_XACT_COMMIT_PREPARED))
+	if (rmid == RM_XACT_ID && (info == XLOG_XACT_COMMIT ||
+							   info == XLOG_XACT_COMMIT_PREPARED))
 	{
 		*recordXtime = ((xl_xact_commit *) XLogRecGetData(record))->xact_time;
 		return true;
 	}
-	if (rmid == RM_XACT_ID && (xact_info == XLOG_XACT_ABORT ||
-							   xact_info == XLOG_XACT_ABORT_PREPARED))
+	if (rmid == RM_XACT_ID && (info == XLOG_XACT_ABORT ||
+							   info == XLOG_XACT_ABORT_PREPARED))
 	{
 		*recordXtime = ((xl_xact_abort *) XLogRecGetData(record))->xact_time;
 		return true;
@@ -2632,7 +2631,7 @@ recoveryStopsBefore(XLogReaderState *record)
 	if (XLogRecGetRmid(record) != RM_XACT_ID)
 		return false;
 
-	xact_info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
+	xact_info = XLogRecGetRecType(record);
 
 	if (xact_info == XLOG_XACT_COMMIT)
 	{
@@ -2799,7 +2798,7 @@ recoveryStopsAfter(XLogReaderState *record)
 	if (rmid != RM_XACT_ID)
 		return false;
 
-	xact_info = info & XLOG_XACT_OPMASK;
+	xact_info = info;
 
 	if (xact_info == XLOG_XACT_COMMIT ||
 		xact_info == XLOG_XACT_COMMIT_PREPARED ||
@@ -3022,7 +3021,7 @@ recoveryApplyDelay(XLogReaderState *record)
 	if (XLogRecGetRmid(record) != RM_XACT_ID)
 		return false;
 
-	xact_info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
+	xact_info = XLogRecGetRecType(record);
 
 	if (xact_info != XLOG_XACT_COMMIT &&
 		xact_info != XLOG_XACT_COMMIT_PREPARED)
diff --git a/src/backend/access/transam/xlogstats.c b/src/backend/access/transam/xlogstats.c
index 4ddd204bd29..aca9a378ce6 100644
--- a/src/backend/access/transam/xlogstats.c
+++ b/src/backend/access/transam/xlogstats.c
@@ -80,15 +80,6 @@ XLogRecStoreStats(XLogStats *stats, XLogReaderState *record)
 
 	recid = XLogRecGetInfo(record) >> 4;
 
-	/*
-	 * XACT records need to be handled differently. Those records use the
-	 * first bit of those four bits for an optional flag variable and the
-	 * following three bits for the opcode. We filter opcode out of xl_info
-	 * and use it as the identifier of the record.
-	 */
-	if (rmid == RM_XACT_ID)
-		recid &= 0x07;
-
 	stats->record_stats[rmid][recid].count++;
 	stats->record_stats[rmid][recid].rec_len += rec_len;
 	stats->record_stats[rmid][recid].fpi_len += fpi_len;
diff --git a/src/backend/postmaster/walsummarizer.c b/src/backend/postmaster/walsummarizer.c
index e1f142f20c7..f18456f48fb 100644
--- a/src/backend/postmaster/walsummarizer.c
+++ b/src/backend/postmaster/walsummarizer.c
@@ -1366,8 +1366,7 @@ SummarizeSmgrRecord(XLogReaderState *xlogreader, BlockRefTable *brtab)
 static void
 SummarizeXactRecord(XLogReaderState *xlogreader, BlockRefTable *brtab)
 {
-	uint8		info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
-	uint8		xact_info = info & XLOG_XACT_OPMASK;
+	uint8		xact_info = XLogRecGetRecType(xlogreader);
 
 	if (xact_info == XLOG_XACT_COMMIT ||
 		xact_info == XLOG_XACT_COMMIT_PREPARED)
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index cc03f0706e9..af7465e1bbf 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -203,7 +203,7 @@ xact_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 	SnapBuild  *builder = ctx->snapshot_builder;
 	ReorderBuffer *reorder = ctx->reorder;
 	XLogReaderState *r = buf->record;
-	uint8		info = XLogRecGetInfo(r) & XLOG_XACT_OPMASK;
+	uint8		info = XLogRecGetRecType(r);
 
 	/*
 	 * If the snapshot isn't yet fully built, we cannot decode anything, so
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index dbc1c50b100..4890f2413df 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -390,8 +390,7 @@ extractPageInfo(XLogReaderState *record)
 {
 	int			block_id;
 	RmgrId		rmid = XLogRecGetRmid(record);
-	uint8		info = XLogRecGetInfo(record);
-	uint8		rminfo = info & ~XLR_INFO_MASK;
+	uint8		rminfo = XLogRecGetRecType(record);
 	uint8		geninfo = XLogRecGetGeninfo(record);
 
 	/* Is this a special record type that I recognize? */
@@ -441,10 +440,10 @@ extractPageInfo(XLogReaderState *record)
 		 */
 	}
 	else if (rmid == RM_XACT_ID &&
-			 ((rminfo & XLOG_XACT_OPMASK) == XLOG_XACT_COMMIT ||
-			  (rminfo & XLOG_XACT_OPMASK) == XLOG_XACT_COMMIT_PREPARED ||
-			  (rminfo & XLOG_XACT_OPMASK) == XLOG_XACT_ABORT ||
-			  (rminfo & XLOG_XACT_OPMASK) == XLOG_XACT_ABORT_PREPARED))
+			 (rminfo == XLOG_XACT_COMMIT ||
+			  rminfo == XLOG_XACT_COMMIT_PREPARED ||
+			  rminfo == XLOG_XACT_ABORT ||
+			  rminfo == XLOG_XACT_ABORT_PREPARED))
 	{
 		/*
 		 * These records can include "dropped rels". We can safely ignore
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 4528e51829e..6eabc7bb40a 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -164,8 +164,9 @@ typedef struct SavedTransactionCharacteristics
  */
 
 /*
- * XLOG allows to store some information in high 4 bits of log record xl_info
- * field. We use 3 for the opcode, and one about an optional flag variable.
+ * XLOG allows to store record type in high 4 bits of log record xl_info
+ * field and the lower 4 bits for optional flags. We use the high bits
+ * for the opcode and the lower for the xinfo bit.
  */
 #define XLOG_XACT_COMMIT			0x00
 #define XLOG_XACT_PREPARE			0x10
@@ -174,13 +175,9 @@ typedef struct SavedTransactionCharacteristics
 #define XLOG_XACT_ABORT_PREPARED	0x40
 #define XLOG_XACT_ASSIGNMENT		0x50
 #define XLOG_XACT_INVALIDATIONS		0x60
-/* free opcode 0x70 */
-
-/* mask for filtering opcodes out of xl_info */
-#define XLOG_XACT_OPMASK			0x70
 
 /* does this record have a 'xinfo' field or not */
-#define XLOG_XACT_HAS_INFO			0x80
+#define XLOG_XACT_HAS_INFO			0x01
 
 /*
  * The following flags, stored in xinfo, determine which information is
diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h
index 6faf02e840f..21f156b6c04 100644
--- a/src/include/access/xlogreader.h
+++ b/src/include/access/xlogreader.h
@@ -408,6 +408,8 @@ extern bool DecodeXLogRecord(XLogReaderState *state,
 #define XLogRecGetTotalLen(decoder) ((decoder)->record->header.xl_tot_len)
 #define XLogRecGetPrev(decoder) ((decoder)->record->header.xl_prev)
 #define XLogRecGetInfo(decoder) ((decoder)->record->header.xl_info)
+#define XLogRecGetRecType(decoder) ((decoder)->record->header.xl_info & XLR_REC_TYPE_MASK)
+#define XLogRecGetRecFlags(decoder) ((decoder)->record->header.xl_info & XLR_REC_FLAGS_MASK)
 #define XLogRecGetGeninfo(decoder) ((decoder)->record->header.xl_geninfo)
 #define XLogRecGetRmid(decoder) ((decoder)->record->header.xl_rmid)
 #define XLogRecGetXid(decoder) ((decoder)->record->header.xl_xid)
diff --git a/src/include/access/xlogrecord.h b/src/include/access/xlogrecord.h
index 9a587bc5b01..a307945170b 100644
--- a/src/include/access/xlogrecord.h
+++ b/src/include/access/xlogrecord.h
@@ -66,8 +66,13 @@ typedef struct XLogRecord
  * All of xl_info may be used freely by rmgr. The high 4 bits are the
  * record type and the rest are optional flag bits.
  */
-#define XLR_INFO_MASK			0x0F
-#define XLR_RMGR_INFO_MASK		0xF0
+#define XLR_REC_TYPE_MASK		0xF0
+#define XLR_REC_FLAGS_MASK		0x0F
+
+// WIP: compatibility for sites that historically used
+// ~XLR_INFO_MASK to access rmgr info
+// It would be nice to get rid of this
+#define XLR_INFO_MASK			0x00
 
 /*
  * XLogReader needs to allocate all the data of a WAL record in a single
-- 
2.51.0

From 7b24fd5139507a80d0edba994abe0a80ae1ac243 Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Sun, 5 Oct 2025 14:17:44 +0700
Subject: [PATCH v1 3/4] Get rid of XLOG_HEAP_OPMASK

---
 src/backend/access/heap/heapam_xlog.c    |  8 ++++----
 src/backend/access/rmgrdesc/heapdesc.c   |  6 ++----
 src/backend/replication/logical/decode.c |  4 ++--
 src/include/access/heapam_xlog.h         | 11 +++++------
 4 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/src/backend/access/heap/heapam_xlog.c b/src/backend/access/heap/heapam_xlog.c
index cf843277938..e6cd0955818 100644
--- a/src/backend/access/heap/heapam_xlog.c
+++ b/src/backend/access/heap/heapam_xlog.c
@@ -1179,14 +1179,14 @@ heap_xlog_inplace(XLogReaderState *record)
 void
 heap_redo(XLogReaderState *record)
 {
-	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+	uint8		info = XLogRecGetRecType(record);
 
 	/*
 	 * These operations don't overwrite MVCC data so no conflict processing is
 	 * required. The ones in heap2 rmgr do.
 	 */
 
-	switch (info & XLOG_HEAP_OPMASK)
+	switch (info)
 	{
 		case XLOG_HEAP_INSERT:
 			heap_xlog_insert(record);
@@ -1225,9 +1225,9 @@ heap_redo(XLogReaderState *record)
 void
 heap2_redo(XLogReaderState *record)
 {
-	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+	uint8		info = XLogRecGetRecType(record);
 
-	switch (info & XLOG_HEAP_OPMASK)
+	switch (info)
 	{
 		case XLOG_HEAP2_PRUNE_ON_ACCESS:
 		case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
diff --git a/src/backend/access/rmgrdesc/heapdesc.c b/src/backend/access/rmgrdesc/heapdesc.c
index 82b62c95de5..dd5ae6b295b 100644
--- a/src/backend/access/rmgrdesc/heapdesc.c
+++ b/src/backend/access/rmgrdesc/heapdesc.c
@@ -184,9 +184,8 @@ void
 heap_desc(StringInfo buf, XLogReaderState *record)
 {
 	char	   *rec = XLogRecGetData(record);
-	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+	uint8		info = XLogRecGetRecType(record);
 
-	info &= XLOG_HEAP_OPMASK;
 	if (info == XLOG_HEAP_INSERT)
 	{
 		xl_heap_insert *xlrec = (xl_heap_insert *) rec;
@@ -264,9 +263,8 @@ void
 heap2_desc(StringInfo buf, XLogReaderState *record)
 {
 	char	   *rec = XLogRecGetData(record);
-	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+	uint8		info = XLogRecGetRecType(record);
 
-	info &= XLOG_HEAP_OPMASK;
 	if (info == XLOG_HEAP2_PRUNE_ON_ACCESS ||
 		info == XLOG_HEAP2_PRUNE_VACUUM_SCAN ||
 		info == XLOG_HEAP2_PRUNE_VACUUM_CLEANUP)
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index af7465e1bbf..cf298a215ee 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -404,7 +404,7 @@ standby_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 void
 heap2_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 {
-	uint8		info = XLogRecGetInfo(buf->record) & XLOG_HEAP_OPMASK;
+	uint8		info = XLogRecGetRecType(buf->record);
 	TransactionId xid = XLogRecGetXid(buf->record);
 	SnapBuild  *builder = ctx->snapshot_builder;
 
@@ -468,7 +468,7 @@ heap2_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 void
 heap_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 {
-	uint8		info = XLogRecGetInfo(buf->record) & XLOG_HEAP_OPMASK;
+	uint8		info = XLogRecGetRecType(buf->record);
 	TransactionId xid = XLogRecGetXid(buf->record);
 	SnapBuild  *builder = ctx->snapshot_builder;
 
diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h
index d4c0625b632..6ebfdb32369 100644
--- a/src/include/access/heapam_xlog.h
+++ b/src/include/access/heapam_xlog.h
@@ -27,8 +27,9 @@
 /*
  * WAL record definitions for heapam.c's WAL operations
  *
- * XLOG allows to store some information in high 4 bits of log
- * record xl_info field.  We use 3 for opcode and one for init bit.
+ * XLOG allows to store record type in high 4 bits of log record xl_info
+ * field and the lower 4 bits for optional flags. We use the high bits
+ * for the opcode and the lower for the init bit.
  */
 #define XLOG_HEAP_INSERT		0x00
 #define XLOG_HEAP_DELETE		0x10
@@ -39,17 +40,15 @@
 #define XLOG_HEAP_LOCK			0x60
 #define XLOG_HEAP_INPLACE		0x70
 
-#define XLOG_HEAP_OPMASK		0x70
 /*
  * When we insert 1st item on new page in INSERT, UPDATE, HOT_UPDATE,
  * or MULTI_INSERT, we can (and we do) restore entire page in redo
  */
-#define XLOG_HEAP_INIT_PAGE		0x80
+#define XLOG_HEAP_INIT_PAGE		0x01
 /*
  * We ran out of opcodes, so heapam.c now has a second RmgrId.  These opcodes
  * are associated with RM_HEAP2_ID, but are not logically different from
- * the ones above associated with RM_HEAP_ID.  XLOG_HEAP_OPMASK applies to
- * these, too.
+ * the ones above associated with RM_HEAP_ID.
  *
  * There's no difference between XLOG_HEAP2_PRUNE_ON_ACCESS,
  * XLOG_HEAP2_PRUNE_VACUUM_SCAN and XLOG_HEAP2_PRUNE_VACUUM_CLEANUP records.
-- 
2.51.0

From 448fabb2edefd714fad7f03cf0c445d53b74835e Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Sun, 5 Oct 2025 15:21:03 +0700
Subject: [PATCH v1 4/4] Get rid of RM_HEAP2_ID

---
 src/backend/access/heap/heapam.c         |  8 +-
 src/backend/access/heap/heapam_xlog.c    | 15 +---
 src/backend/access/heap/pruneheap.c      |  2 +-
 src/backend/access/heap/rewriteheap.c    |  2 +-
 src/backend/access/rmgrdesc/heapdesc.c   | 22 +-----
 src/backend/replication/logical/decode.c | 98 ++++++++----------------
 src/bin/pg_waldump/t/001_basic.pl        |  1 -
 src/include/access/heapam_xlog.h         | 35 ++++-----
 src/include/access/rmgrlist.h            |  1 -
 9 files changed, 60 insertions(+), 124 deletions(-)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index ed0c0c2dc9f..c8597417f15 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2634,7 +2634,7 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
 			/* filtering by origin on a row level is much more efficient */
 			XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
 
-			recptr = XLogInsert(RM_HEAP2_ID, info);
+			recptr = XLogInsert(RM_HEAP_ID, info);
 
 			PageSetLSN(page, recptr);
 		}
@@ -5981,7 +5981,7 @@ l4:
 
 			XLogRegisterData(&xlrec, SizeOfHeapLockUpdated);
 
-			recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
+			recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP2_LOCK_UPDATED);
 
 			PageSetLSN(page, recptr);
 		}
@@ -8825,7 +8825,7 @@ log_heap_visible(Relation rel, Buffer heap_buffer, Buffer vm_buffer,
 		flags |= REGBUF_NO_IMAGE;
 	XLogRegisterBuffer(1, heap_buffer, flags);
 
-	recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_VISIBLE);
+	recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP2_VISIBLE);
 
 	return recptr;
 }
@@ -9119,7 +9119,7 @@ log_heap_new_cid(Relation relation, HeapTuple tup)
 
 	/* will be looked at irrespective of origin */
 
-	recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_NEW_CID);
+	recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP2_NEW_CID);
 
 	return recptr;
 }
diff --git a/src/backend/access/heap/heapam_xlog.c b/src/backend/access/heap/heapam_xlog.c
index e6cd0955818..4817f55a5b9 100644
--- a/src/backend/access/heap/heapam_xlog.c
+++ b/src/backend/access/heap/heapam_xlog.c
@@ -1184,6 +1184,7 @@ heap_redo(XLogReaderState *record)
 	/*
 	 * These operations don't overwrite MVCC data so no conflict processing is
 	 * required. The ones in heap2 rmgr do.
+	 * WIP: Is this a blocker for merging into one rmgr?
 	 */
 
 	switch (info)
@@ -1217,18 +1218,8 @@ heap_redo(XLogReaderState *record)
 		case XLOG_HEAP_INPLACE:
 			heap_xlog_inplace(record);
 			break;
-		default:
-			elog(PANIC, "heap_redo: unknown op code %u", info);
-	}
-}
 
-void
-heap2_redo(XLogReaderState *record)
-{
-	uint8		info = XLogRecGetRecType(record);
-
-	switch (info)
-	{
+		/* former heap2 */
 		case XLOG_HEAP2_PRUNE_ON_ACCESS:
 		case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
 		case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
@@ -1254,7 +1245,7 @@ heap2_redo(XLogReaderState *record)
 			heap_xlog_logical_rewrite(record);
 			break;
 		default:
-			elog(PANIC, "heap2_redo: unknown op code %u", info);
+			elog(PANIC, "heap_redo: unknown op code %u", info);
 	}
 }
 
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index d8ea0c78f77..9411999ddab 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -2166,7 +2166,7 @@ log_heap_prune_and_freeze(Relation relation, Buffer buffer,
 			elog(ERROR, "unrecognized prune reason: %d", (int) reason);
 			break;
 	}
-	recptr = XLogInsert(RM_HEAP2_ID, info);
+	recptr = XLogInsert(RM_HEAP_ID, info);
 
 	PageSetLSN(BufferGetPage(buffer), recptr);
 }
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index e6d2b5fced1..a78ba42931c 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -891,7 +891,7 @@ logical_heap_rewrite_flush_mappings(RewriteState state)
 		XLogRegisterData(waldata_start, len);
 
 		/* write xlog record */
-		XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_REWRITE);
+		XLogInsert(RM_HEAP_ID, XLOG_HEAP2_REWRITE);
 
 		pfree(waldata_start);
 	}
diff --git a/src/backend/access/rmgrdesc/heapdesc.c b/src/backend/access/rmgrdesc/heapdesc.c
index dd5ae6b295b..a933e946ba6 100644
--- a/src/backend/access/rmgrdesc/heapdesc.c
+++ b/src/backend/access/rmgrdesc/heapdesc.c
@@ -257,15 +257,7 @@ heap_desc(StringInfo buf, XLogReaderState *record)
 								   xlrec->dbId, xlrec->tsId,
 								   xlrec->relcacheInitFileInval);
 	}
-}
-
-void
-heap2_desc(StringInfo buf, XLogReaderState *record)
-{
-	char	   *rec = XLogRecGetData(record);
-	uint8		info = XLogRecGetRecType(record);
-
-	if (info == XLOG_HEAP2_PRUNE_ON_ACCESS ||
+	else if (info == XLOG_HEAP2_PRUNE_ON_ACCESS ||
 		info == XLOG_HEAP2_PRUNE_VACUUM_SCAN ||
 		info == XLOG_HEAP2_PRUNE_VACUUM_CLEANUP)
 	{
@@ -423,18 +415,8 @@ heap_identify(uint8 info)
 		case XLOG_HEAP_INPLACE:
 			id = "INPLACE";
 			break;
-	}
 
-	return id;
-}
-
-const char *
-heap2_identify(uint8 info)
-{
-	const char *id = NULL;
-
-	switch (info & ~XLR_INFO_MASK)
-	{
+		/* former heap2 */
 		case XLOG_HEAP2_PRUNE_ON_ACCESS:
 			id = "PRUNE_ON_ACCESS";
 			break;
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index cf298a215ee..eb1648e2192 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -398,70 +398,6 @@ standby_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 	}
 }
 
-/*
- * Handle rmgr HEAP2_ID records for LogicalDecodingProcessRecord().
- */
-void
-heap2_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
-{
-	uint8		info = XLogRecGetRecType(buf->record);
-	TransactionId xid = XLogRecGetXid(buf->record);
-	SnapBuild  *builder = ctx->snapshot_builder;
-
-	ReorderBufferProcessXid(ctx->reorder, xid, buf->origptr);
-
-	/*
-	 * If we don't have snapshot or we are just fast-forwarding, there is no
-	 * point in decoding data changes. However, it's crucial to build the base
-	 * snapshot during fast-forward mode (as is done in
-	 * SnapBuildProcessChange()) because we require the snapshot's xmin when
-	 * determining the candidate catalog_xmin for the replication slot. See
-	 * SnapBuildProcessRunningXacts().
-	 */
-	if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT)
-		return;
-
-	switch (info)
-	{
-		case XLOG_HEAP2_MULTI_INSERT:
-			if (SnapBuildProcessChange(builder, xid, buf->origptr) &&
-				!ctx->fast_forward)
-				DecodeMultiInsert(ctx, buf);
-			break;
-		case XLOG_HEAP2_NEW_CID:
-			if (!ctx->fast_forward)
-			{
-				xl_heap_new_cid *xlrec;
-
-				xlrec = (xl_heap_new_cid *) XLogRecGetData(buf->record);
-				SnapBuildProcessNewCid(builder, xid, buf->origptr, xlrec);
-
-				break;
-			}
-		case XLOG_HEAP2_REWRITE:
-
-			/*
-			 * Although these records only exist to serve the needs of logical
-			 * decoding, all the work happens as part of crash or archive
-			 * recovery, so we don't need to do anything here.
-			 */
-			break;
-
-			/*
-			 * Everything else here is just low level physical stuff we're not
-			 * interested in.
-			 */
-		case XLOG_HEAP2_PRUNE_ON_ACCESS:
-		case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
-		case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
-		case XLOG_HEAP2_VISIBLE:
-		case XLOG_HEAP2_LOCK_UPDATED:
-			break;
-		default:
-			elog(ERROR, "unexpected RM_HEAP2_ID record type: %u", info);
-	}
-}
-
 /*
  * Handle rmgr HEAP_ID records for LogicalDecodingProcessRecord().
  */
@@ -546,6 +482,40 @@ heap_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 			/* we don't care about row level locks for now */
 			break;
 
+		case XLOG_HEAP2_MULTI_INSERT:
+			if (SnapBuildProcessChange(builder, xid, buf->origptr) &&
+				!ctx->fast_forward)
+				DecodeMultiInsert(ctx, buf);
+			break;
+		case XLOG_HEAP2_NEW_CID:
+			if (!ctx->fast_forward)
+			{
+				xl_heap_new_cid *xlrec;
+
+				xlrec = (xl_heap_new_cid *) XLogRecGetData(buf->record);
+				SnapBuildProcessNewCid(builder, xid, buf->origptr, xlrec);
+
+				break;
+			}
+		case XLOG_HEAP2_REWRITE:
+
+			/*
+			 * Although these records only exist to serve the needs of logical
+			 * decoding, all the work happens as part of crash or archive
+			 * recovery, so we don't need to do anything here.
+			 */
+			break;
+
+			/*
+			 * Everything else here is just low level physical stuff we're not
+			 * interested in.
+			 */
+		case XLOG_HEAP2_PRUNE_ON_ACCESS:
+		case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
+		case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
+		case XLOG_HEAP2_VISIBLE:
+		case XLOG_HEAP2_LOCK_UPDATED:
+			break;
 		default:
 			elog(ERROR, "unexpected RM_HEAP_ID record type: %u", info);
 			break;
diff --git a/src/bin/pg_waldump/t/001_basic.pl b/src/bin/pg_waldump/t/001_basic.pl
index f26d75e01cf..01a7d3276e9 100644
--- a/src/bin/pg_waldump/t/001_basic.pl
+++ b/src/bin/pg_waldump/t/001_basic.pl
@@ -61,7 +61,6 @@ Tablespace
 MultiXact
 RelMap
 Standby
-Heap2
 Heap
 Btree
 Hash
diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h
index 6ebfdb32369..3fcb9d5038d 100644
--- a/src/include/access/heapam_xlog.h
+++ b/src/include/access/heapam_xlog.h
@@ -39,30 +39,28 @@
 #define XLOG_HEAP_CONFIRM		0x50
 #define XLOG_HEAP_LOCK			0x60
 #define XLOG_HEAP_INPLACE		0x70
-
-/*
- * When we insert 1st item on new page in INSERT, UPDATE, HOT_UPDATE,
- * or MULTI_INSERT, we can (and we do) restore entire page in redo
- */
-#define XLOG_HEAP_INIT_PAGE		0x01
 /*
- * We ran out of opcodes, so heapam.c now has a second RmgrId.  These opcodes
- * are associated with RM_HEAP2_ID, but are not logically different from
- * the ones above associated with RM_HEAP_ID.
+ * The HEAP2 designation is historical.
  *
  * There's no difference between XLOG_HEAP2_PRUNE_ON_ACCESS,
  * XLOG_HEAP2_PRUNE_VACUUM_SCAN and XLOG_HEAP2_PRUNE_VACUUM_CLEANUP records.
  * They have separate opcodes just for debugging and analysis purposes, to
  * indicate why the WAL record was emitted.
  */
-#define XLOG_HEAP2_REWRITE		0x00
-#define XLOG_HEAP2_PRUNE_ON_ACCESS		0x10
-#define XLOG_HEAP2_PRUNE_VACUUM_SCAN	0x20
-#define XLOG_HEAP2_PRUNE_VACUUM_CLEANUP	0x30
-#define XLOG_HEAP2_VISIBLE		0x40
-#define XLOG_HEAP2_MULTI_INSERT 0x50
-#define XLOG_HEAP2_LOCK_UPDATED 0x60
-#define XLOG_HEAP2_NEW_CID		0x70
+#define XLOG_HEAP2_REWRITE		0x80
+#define XLOG_HEAP2_PRUNE_ON_ACCESS		0x90
+#define XLOG_HEAP2_PRUNE_VACUUM_SCAN	0xA0
+#define XLOG_HEAP2_PRUNE_VACUUM_CLEANUP	0xB0
+#define XLOG_HEAP2_VISIBLE		0xC0
+#define XLOG_HEAP2_MULTI_INSERT 0xD0
+#define XLOG_HEAP2_LOCK_UPDATED 0xE0
+#define XLOG_HEAP2_NEW_CID		0xF0
+
+/*
+ * When we insert 1st item on new page in INSERT, UPDATE, HOT_UPDATE,
+ * or MULTI_INSERT, we can (and we do) restore entire page in redo
+ */
+#define XLOG_HEAP_INIT_PAGE		0x01
 
 /*
  * xl_heap_insert/xl_heap_multi_insert flag values, 8 bits are available.
@@ -485,9 +483,6 @@ extern void heap_redo(XLogReaderState *record);
 extern void heap_desc(StringInfo buf, XLogReaderState *record);
 extern const char *heap_identify(uint8 info);
 extern void heap_mask(char *pagedata, BlockNumber blkno);
-extern void heap2_redo(XLogReaderState *record);
-extern void heap2_desc(StringInfo buf, XLogReaderState *record);
-extern const char *heap2_identify(uint8 info);
 extern void heap_xlog_logical_rewrite(XLogReaderState *r);
 
 extern XLogRecPtr log_heap_visible(Relation rel, Buffer heap_buffer,
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 8e7fc9db877..e2bc93468a4 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -34,7 +34,6 @@ PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, N
 PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL, NULL)
 PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL, NULL)
 PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL, standby_decode)
-PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask, heap2_decode)
 PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask, heap_decode)
 PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, btree_xlog_startup, btree_xlog_cleanup, btree_mask, NULL)
 PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL)
-- 
2.51.0

From 4c76d0a3a6242eae605f605f7ba229581832c0d1 Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Wed, 1 Oct 2025 17:09:06 +0700
Subject: [PATCH v1 1/4] Split XLogRecord.info into separate members

"info" is now rmgr-specific, including record type and optional flags.
"geninfo" is for XLR_SPECIAL_REL_UPDATE, XLR_CHECK_CONSISTENCY, and
anything set internally by XLogInsertExtended.

XLogInsert is now a thin wrapper for XLogInsertExtended, which only
needs to be called when pasing geninfo flags.
---
 contrib/pg_visibility/pg_visibility.c     |  4 +--
 contrib/pg_walinspect/pg_walinspect.c     |  2 +-
 src/backend/access/transam/xact.c         | 10 ++++---
 src/backend/access/transam/xloginsert.c   | 35 ++++++++++++++---------
 src/backend/access/transam/xlogrecovery.c |  4 +--
 src/backend/access/transam/xlogstats.c    |  3 +-
 src/backend/catalog/storage.c             |  6 ++--
 src/backend/commands/dbcommands.c         | 16 +++++------
 src/bin/pg_resetwal/pg_resetwal.c         |  1 +
 src/bin/pg_rewind/parsexlog.c             |  7 +++--
 src/bin/pg_waldump/pg_waldump.c           |  2 +-
 src/include/access/xloginsert.h           |  1 +
 src/include/access/xlogreader.h           |  1 +
 src/include/access/xlogrecord.h           | 15 +++++++---
 14 files changed, 64 insertions(+), 43 deletions(-)

diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index d79ef35006b..3c5ff6be79e 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -429,8 +429,8 @@ pg_truncate_visibility_map(PG_FUNCTION_ARGS)
 		XLogBeginInsert();
 		XLogRegisterData(&xlrec, sizeof(xlrec));
 
-		lsn = XLogInsert(RM_SMGR_ID,
-						 XLOG_SMGR_TRUNCATE | XLR_SPECIAL_REL_UPDATE);
+		lsn = XLogInsertExtended(RM_SMGR_ID,
+								 XLOG_SMGR_TRUNCATE, XLR_SPECIAL_REL_UPDATE);
 		XLogFlush(lsn);
 	}
 
diff --git a/contrib/pg_walinspect/pg_walinspect.c b/contrib/pg_walinspect/pg_walinspect.c
index 0398ad82cec..da6a4f247ff 100644
--- a/contrib/pg_walinspect/pg_walinspect.c
+++ b/contrib/pg_walinspect/pg_walinspect.c
@@ -716,7 +716,7 @@ GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
 
 				old_cxt = MemoryContextSwitchTo(tmp_cxt);
 
-				/* the upper four bits in xl_info are the rmgr's */
+				/* the upper four bits in xl_info are the record type */
 				id = desc.rm_identify(rj << 4);
 				if (id == NULL)
 					id = psprintf("UNKNOWN (%x)", rj << 4);
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 2cf3d4e92b7..fc06474fc75 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -5842,6 +5842,7 @@ XactLogCommitRecord(TimestampTz commit_time,
 	xl_xact_twophase xl_twophase;
 	xl_xact_origin xl_origin;
 	uint8		info;
+	uint8		geninfo = 0;
 
 	Assert(CritSectionCount > 0);
 
@@ -5892,7 +5893,7 @@ XactLogCommitRecord(TimestampTz commit_time,
 	{
 		xl_xinfo.xinfo |= XACT_XINFO_HAS_RELFILELOCATORS;
 		xl_relfilelocators.nrels = nrels;
-		info |= XLR_SPECIAL_REL_UPDATE;
+		geninfo |= XLR_SPECIAL_REL_UPDATE;
 	}
 
 	if (ndroppedstats > 0)
@@ -5985,7 +5986,7 @@ XactLogCommitRecord(TimestampTz commit_time,
 	/* we allow filtering by xacts */
 	XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
 
-	return XLogInsert(RM_XACT_ID, info);
+	return XLogInsertExtended(RM_XACT_ID, info, geninfo);
 }
 
 /*
@@ -6012,6 +6013,7 @@ XactLogAbortRecord(TimestampTz abort_time,
 	xl_xact_origin xl_origin;
 
 	uint8		info;
+	uint8		geninfo = 0;
 
 	Assert(CritSectionCount > 0);
 
@@ -6041,7 +6043,7 @@ XactLogAbortRecord(TimestampTz abort_time,
 	{
 		xl_xinfo.xinfo |= XACT_XINFO_HAS_RELFILELOCATORS;
 		xl_relfilelocators.nrels = nrels;
-		info |= XLR_SPECIAL_REL_UPDATE;
+		geninfo |= XLR_SPECIAL_REL_UPDATE;
 	}
 
 	if (ndroppedstats > 0)
@@ -6131,7 +6133,7 @@ XactLogAbortRecord(TimestampTz abort_time,
 	/* Include the replication origin */
 	XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
 
-	return XLogInsert(RM_XACT_ID, info);
+	return XLogInsertExtended(RM_XACT_ID, info, geninfo);
 }
 
 /*
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index c7571429e8e..2c0ffb46838 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -134,7 +134,7 @@ static bool begininsert_called = false;
 /* Memory context to hold the registered buffer and data references. */
 static MemoryContext xloginsert_cxt;
 
-static XLogRecData *XLogRecordAssemble(RmgrId rmid, uint8 info,
+static XLogRecData *XLogRecordAssemble(RmgrId rmid, uint8 info, uint8 geninfo,
 									   XLogRecPtr RedoRecPtr, bool doPageWrites,
 									   XLogRecPtr *fpw_lsn, int *num_fpi,
 									   bool *topxid_included);
@@ -460,7 +460,7 @@ XLogSetRecordFlags(uint8 flags)
 }
 
 /*
- * Insert an XLOG record having the specified RMID and info bytes, with the
+ * Insert an XLOG record having the specified RMID, info and flag bytes, with the
  * body of the record being the data and buffer references registered earlier
  * with XLogRegister* calls.
  *
@@ -471,7 +471,7 @@ XLogSetRecordFlags(uint8 flags)
  * WAL rule "write the log before the data".)
  */
 XLogRecPtr
-XLogInsert(RmgrId rmid, uint8 info)
+XLogInsertExtended(RmgrId rmid, uint8 info, uint8 geninfo)
 {
 	XLogRecPtr	EndPos;
 
@@ -480,14 +480,14 @@ XLogInsert(RmgrId rmid, uint8 info)
 		elog(ERROR, "XLogBeginInsert was not called");
 
 	/*
-	 * The caller can set rmgr bits, XLR_SPECIAL_REL_UPDATE and
-	 * XLR_CHECK_CONSISTENCY; the rest are reserved for use by me.
+	 * The caller can set XLR_SPECIAL_REL_UPDATE and XLR_CHECK_CONSISTENCY;
+	 * the rest are reserved for use by me.
 	 */
-	if ((info & ~(XLR_RMGR_INFO_MASK |
-				  XLR_SPECIAL_REL_UPDATE |
-				  XLR_CHECK_CONSISTENCY)) != 0)
-		elog(PANIC, "invalid xlog info mask %02X", info);
+	if ((geninfo & ~(XLR_SPECIAL_REL_UPDATE |
+					 XLR_CHECK_CONSISTENCY)) != 0)
+		elog(PANIC, "invalid xlog geninfo mask %02X", geninfo);
 
+	/* WIP: need geninfo here? */
 	TRACE_POSTGRESQL_WAL_INSERT(rmid, info);
 
 	/*
@@ -517,7 +517,7 @@ XLogInsert(RmgrId rmid, uint8 info)
 		 */
 		GetFullPageWriteInfo(&RedoRecPtr, &doPageWrites);
 
-		rdt = XLogRecordAssemble(rmid, info, RedoRecPtr, doPageWrites,
+		rdt = XLogRecordAssemble(rmid, info, geninfo, RedoRecPtr, doPageWrites,
 								 &fpw_lsn, &num_fpi, &topxid_included);
 
 		EndPos = XLogInsertRecord(rdt, fpw_lsn, curinsert_flags, num_fpi,
@@ -529,6 +529,14 @@ XLogInsert(RmgrId rmid, uint8 info)
 	return EndPos;
 }
 
+/* Convenience wrapper for callers that don't pass "geninfo" */
+XLogRecPtr
+XLogInsert(RmgrId rmid, uint8 info)
+{
+	return XLogInsertExtended(rmid, info, 0);
+}
+
+
 /*
  * Simple wrapper to XLogInsert to insert a WAL record with elementary
  * contents (only an int64 is supported as value currently).
@@ -557,7 +565,7 @@ XLogSimpleInsertInt64(RmgrId rmid, uint8 info, int64 value)
  * current subtransaction.
  */
 static XLogRecData *
-XLogRecordAssemble(RmgrId rmid, uint8 info,
+XLogRecordAssemble(RmgrId rmid, uint8 info, uint8 geninfo,
 				   XLogRecPtr RedoRecPtr, bool doPageWrites,
 				   XLogRecPtr *fpw_lsn, int *num_fpi, bool *topxid_included)
 {
@@ -590,7 +598,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 	 * a record.
 	 */
 	if (wal_consistency_checking[rmid])
-		info |= XLR_CHECK_CONSISTENCY;
+		geninfo |= XLR_CHECK_CONSISTENCY;
 
 	/*
 	 * Make an rdata chain containing all the data portions of all block
@@ -656,7 +664,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 		 * If needs_backup is true or WAL checking is enabled for current
 		 * resource manager, log a full-page write for the current block.
 		 */
-		include_image = needs_backup || (info & XLR_CHECK_CONSISTENCY) != 0;
+		include_image = needs_backup || (geninfo & XLR_CHECK_CONSISTENCY) != 0;
 
 		if (include_image)
 		{
@@ -938,6 +946,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 	rechdr->xl_xid = GetCurrentTransactionIdIfAny();
 	rechdr->xl_tot_len = (uint32) total_len;
 	rechdr->xl_info = info;
+	rechdr->xl_geninfo = geninfo;
 	rechdr->xl_rmid = rmid;
 	rechdr->xl_prev = InvalidXLogRecPtr;
 	rechdr->xl_crc = rdata_crc;
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index 52ff4d119e6..3e3aae0e47c 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -2006,7 +2006,7 @@ ApplyWalRecord(XLogReaderState *xlogreader, XLogRecord *record, TimeLineID *repl
 	 * record are consistent with the existing pages. This check is done only
 	 * if consistency check is enabled for this record.
 	 */
-	if ((record->xl_info & XLR_CHECK_CONSISTENCY) != 0)
+	if ((record->xl_geninfo & XLR_CHECK_CONSISTENCY) != 0)
 		verifyBackupPageConsistency(xlogreader);
 
 	/* Pop the error context stack */
@@ -2483,7 +2483,7 @@ verifyBackupPageConsistency(XLogReaderState *record)
 	if (!XLogRecHasAnyBlockRefs(record))
 		return;
 
-	Assert((XLogRecGetInfo(record) & XLR_CHECK_CONSISTENCY) != 0);
+	Assert((XLogRecGetGeninfo(record) & XLR_CHECK_CONSISTENCY) != 0);
 
 	for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
 	{
diff --git a/src/backend/access/transam/xlogstats.c b/src/backend/access/transam/xlogstats.c
index f92d9e13b17..4ddd204bd29 100644
--- a/src/backend/access/transam/xlogstats.c
+++ b/src/backend/access/transam/xlogstats.c
@@ -75,8 +75,7 @@ XLogRecStoreStats(XLogStats *stats, XLogReaderState *record)
 	/*
 	 * Update per-record statistics, where the record is identified by a
 	 * combination of the RmgrId and the four bits of the xl_info field that
-	 * are the rmgr's domain (resulting in sixteen possible entries per
-	 * RmgrId).
+	 * are the record type (resulting in sixteen possible entries per RmgrId).
 	 */
 
 	recid = XLogRecGetInfo(record) >> 4;
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index c58e9418ac3..829506296ed 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -196,7 +196,7 @@ log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
 
 	XLogBeginInsert();
 	XLogRegisterData(&xlrec, sizeof(xlrec));
-	XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE | XLR_SPECIAL_REL_UPDATE);
+	XLogInsertExtended(RM_SMGR_ID, XLOG_SMGR_CREATE, XLR_SPECIAL_REL_UPDATE);
 }
 
 /*
@@ -400,8 +400,8 @@ RelationTruncate(Relation rel, BlockNumber nblocks)
 		XLogBeginInsert();
 		XLogRegisterData(&xlrec, sizeof(xlrec));
 
-		lsn = XLogInsert(RM_SMGR_ID,
-						 XLOG_SMGR_TRUNCATE | XLR_SPECIAL_REL_UPDATE);
+		lsn = XLogInsertExtended(RM_SMGR_ID,
+								 XLOG_SMGR_TRUNCATE, XLR_SPECIAL_REL_UPDATE);
 
 		/*
 		 * Flush, because otherwise the truncation of the main relation might
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 2793fd83771..926c2d2e341 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -630,8 +630,8 @@ CreateDatabaseUsingFileCopy(Oid src_dboid, Oid dst_dboid, Oid src_tsid,
 			XLogRegisterData(&xlrec,
 							 sizeof(xl_dbase_create_file_copy_rec));
 
-			(void) XLogInsert(RM_DBASE_ID,
-							  XLOG_DBASE_CREATE_FILE_COPY | XLR_SPECIAL_REL_UPDATE);
+			(void) XLogInsertExtended(RM_DBASE_ID,
+									  XLOG_DBASE_CREATE_FILE_COPY, XLR_SPECIAL_REL_UPDATE);
 		}
 		pfree(srcpath);
 		pfree(dstpath);
@@ -2213,8 +2213,8 @@ movedb(const char *dbname, const char *tblspcname)
 			XLogRegisterData(&xlrec,
 							 sizeof(xl_dbase_create_file_copy_rec));
 
-			(void) XLogInsert(RM_DBASE_ID,
-							  XLOG_DBASE_CREATE_FILE_COPY | XLR_SPECIAL_REL_UPDATE);
+			(void) XLogInsertExtended(RM_DBASE_ID,
+									  XLOG_DBASE_CREATE_FILE_COPY, XLR_SPECIAL_REL_UPDATE);
 		}
 
 		/*
@@ -2309,8 +2309,8 @@ movedb(const char *dbname, const char *tblspcname)
 		XLogRegisterData(&xlrec, sizeof(xl_dbase_drop_rec));
 		XLogRegisterData(&src_tblspcoid, sizeof(Oid));
 
-		(void) XLogInsert(RM_DBASE_ID,
-						  XLOG_DBASE_DROP | XLR_SPECIAL_REL_UPDATE);
+		(void) XLogInsertExtended(RM_DBASE_ID,
+								  XLOG_DBASE_DROP, XLR_SPECIAL_REL_UPDATE);
 	}
 
 	/* Now it's safe to release the database lock */
@@ -3067,8 +3067,8 @@ remove_dbtablespaces(Oid db_id)
 		XLogRegisterData(&xlrec, MinSizeOfDbaseDropRec);
 		XLogRegisterData(tablespace_ids, ntblspc * sizeof(Oid));
 
-		(void) XLogInsert(RM_DBASE_ID,
-						  XLOG_DBASE_DROP | XLR_SPECIAL_REL_UPDATE);
+		(void) XLogInsertExtended(RM_DBASE_ID,
+								  XLOG_DBASE_DROP, XLR_SPECIAL_REL_UPDATE);
 	}
 
 	list_free(ltblspc);
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 7a4e4eb9570..ac4a7a7fe71 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -1132,6 +1132,7 @@ WriteEmptyXLOG(void)
 	record->xl_xid = InvalidTransactionId;
 	record->xl_tot_len = SizeOfXLogRecord + SizeOfXLogRecordDataHeaderShort + sizeof(CheckPoint);
 	record->xl_info = XLOG_CHECKPOINT_SHUTDOWN;
+	record->xl_geninfo = 0;
 	record->xl_rmid = RM_XLOG_ID;
 
 	recptr += SizeOfXLogRecord;
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 8f4b282c6b1..dbc1c50b100 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -392,6 +392,7 @@ extractPageInfo(XLogReaderState *record)
 	RmgrId		rmid = XLogRecGetRmid(record);
 	uint8		info = XLogRecGetInfo(record);
 	uint8		rminfo = info & ~XLR_INFO_MASK;
+	uint8		geninfo = XLogRecGetGeninfo(record);
 
 	/* Is this a special record type that I recognize? */
 
@@ -451,7 +452,7 @@ extractPageInfo(XLogReaderState *record)
 		 * source.
 		 */
 	}
-	else if (info & XLR_SPECIAL_REL_UPDATE)
+	else if (geninfo & XLR_SPECIAL_REL_UPDATE)
 	{
 		/*
 		 * This record type modifies a relation file in some special way, but
@@ -459,9 +460,9 @@ extractPageInfo(XLogReaderState *record)
 		 * track that change.
 		 */
 		pg_fatal("WAL record modifies a relation, but record type is not recognized:\n"
-				 "lsn: %X/%08X, rmid: %d, rmgr: %s, info: %02X",
+				 "lsn: %X/%08X, rmid: %d, rmgr: %s, geninfo: %02X",
 				 LSN_FORMAT_ARGS(record->ReadRecPtr),
-				 rmid, RmgrName(rmid), info);
+				 rmid, RmgrName(rmid), geninfo);
 	}
 
 	for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index 13d3ec2f5be..98cd57fa117 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -711,7 +711,7 @@ XLogDumpDisplayStats(XLogDumpConfig *config, XLogStats *stats)
 				if (count == 0)
 					continue;
 
-				/* the upper four bits in xl_info are the rmgr's */
+				/* the upper four bits in xl_info are the record type */
 				id = desc->rm_identify(rj << 4);
 				if (id == NULL)
 					id = psprintf("UNKNOWN (%x)", rj << 4);
diff --git a/src/include/access/xloginsert.h b/src/include/access/xloginsert.h
index d6a71415d4f..f2cb2c4bbac 100644
--- a/src/include/access/xloginsert.h
+++ b/src/include/access/xloginsert.h
@@ -44,6 +44,7 @@
 extern void XLogBeginInsert(void);
 extern void XLogSetRecordFlags(uint8 flags);
 extern XLogRecPtr XLogInsert(RmgrId rmid, uint8 info);
+extern XLogRecPtr XLogInsertExtended(RmgrId rmid, uint8 info, uint8 geninfo);
 extern XLogRecPtr XLogSimpleInsertInt64(RmgrId rmid, uint8 info, int64 value);
 extern void XLogEnsureRecordSpace(int max_block_id, int ndatas);
 extern void XLogRegisterData(const void *data, uint32 len);
diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h
index 9738462d3c9..6faf02e840f 100644
--- a/src/include/access/xlogreader.h
+++ b/src/include/access/xlogreader.h
@@ -408,6 +408,7 @@ extern bool DecodeXLogRecord(XLogReaderState *state,
 #define XLogRecGetTotalLen(decoder) ((decoder)->record->header.xl_tot_len)
 #define XLogRecGetPrev(decoder) ((decoder)->record->header.xl_prev)
 #define XLogRecGetInfo(decoder) ((decoder)->record->header.xl_info)
+#define XLogRecGetGeninfo(decoder) ((decoder)->record->header.xl_geninfo)
 #define XLogRecGetRmid(decoder) ((decoder)->record->header.xl_rmid)
 #define XLogRecGetXid(decoder) ((decoder)->record->header.xl_xid)
 #define XLogRecGetOrigin(decoder) ((decoder)->record->record_origin)
diff --git a/src/include/access/xlogrecord.h b/src/include/access/xlogrecord.h
index a06833ce0a3..9a587bc5b01 100644
--- a/src/include/access/xlogrecord.h
+++ b/src/include/access/xlogrecord.h
@@ -45,7 +45,15 @@ typedef struct XLogRecord
 	XLogRecPtr	xl_prev;		/* ptr to previous record in log */
 	uint8		xl_info;		/* flag bits, see below */
 	RmgrId		xl_rmid;		/* resource manager for this record */
-	/* 2 bytes of padding here, initialize to zero */
+
+	/*
+	 * The XLR_SPECIAL_REL_UPDATE and XLR_CHECK_CONSISTENCY bits can be passed
+	 * by XLogInsertExtended caller. The rest are set internally by
+	 * XLogInsertExtended.
+	 */
+	uint8		xl_geninfo;
+
+	/* 1 byte of padding here, initialize to zero */
 	pg_crc32c	xl_crc;			/* CRC for this record */
 
 	/* XLogRecordBlockHeaders and XLogRecordDataHeader follow, no padding */
@@ -55,9 +63,8 @@ typedef struct XLogRecord
 #define SizeOfXLogRecord	(offsetof(XLogRecord, xl_crc) + sizeof(pg_crc32c))
 
 /*
- * The high 4 bits in xl_info may be used freely by rmgr. The
- * XLR_SPECIAL_REL_UPDATE and XLR_CHECK_CONSISTENCY bits can be passed by
- * XLogInsert caller. The rest are set internally by XLogInsert.
+ * All of xl_info may be used freely by rmgr. The high 4 bits are the
+ * record type and the rest are optional flag bits.
  */
 #define XLR_INFO_MASK			0x0F
 #define XLR_RMGR_INFO_MASK		0xF0
-- 
2.51.0

Reply via email to