From 08edc80158627bd23018d437e31ee47e2fbd0553 Mon Sep 17 00:00:00 2001
From: John Naylor <john.naylor@postgresql.org>
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

