From 6f0a67ca2474498e22bfeefba68e1fd73edcc4bd Mon Sep 17 00:00:00 2001
From: ChangAo Chen <cca5507@qq.com>
Date: Wed, 10 Dec 2025 11:34:02 +0800
Subject: [PATCH v4] Handle XLOG_HEAP2_NEW_CID in heap2_decode() even if
 fast-forwarding.

We need to mark the transaction as containing catalog modifications
during decoding XLOG_HEAP2_NEW_CID even if fast-forwarding to maintain
the snapshot correctly. Otherwise, an incorrect historic snapshot may
be serialized to disk during fast-forwarding because the snapshot only
track catalog modified transaction.
---
 src/backend/replication/logical/decode.c    | 12 ++++++++++--
 src/backend/replication/logical/snapbuild.c |  9 ++-------
 2 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index cc03f0706e9..31d22c51290 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -429,15 +429,23 @@ heap2_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 				DecodeMultiInsert(ctx, buf);
 			break;
 		case XLOG_HEAP2_NEW_CID:
+			/*
+			 * We only log new_cid's if a catalog tuple was modified, so mark the
+			 * transaction as containing catalog modifications.
+			 *
+			 * Note: we do this even in fast-forward mode, as we need to maintain
+			 * the snapshot correctly.
+			 */
+			ReorderBufferXidSetCatalogChanges(ctx->reorder, xid, buf->origptr);
+
 			if (!ctx->fast_forward)
 			{
 				xl_heap_new_cid *xlrec;
 
 				xlrec = (xl_heap_new_cid *) XLogRecGetData(buf->record);
 				SnapBuildProcessNewCid(builder, xid, buf->origptr, xlrec);
-
-				break;
 			}
+			break;
 		case XLOG_HEAP2_REWRITE:
 
 			/*
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index d6ab1e017eb..ab5ca82ff9f 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -682,7 +682,8 @@ SnapBuildProcessChange(SnapBuild *builder, TransactionId xid, XLogRecPtr lsn)
 /*
  * Do CommandId/combo CID handling after reading an xl_heap_new_cid record.
  * This implies that a transaction has done some form of write to system
- * catalogs.
+ * catalogs. Caller must mark the transaction as containing catalog
+ * modifications in the reorder buffer.
  */
 void
 SnapBuildProcessNewCid(SnapBuild *builder, TransactionId xid,
@@ -690,12 +691,6 @@ SnapBuildProcessNewCid(SnapBuild *builder, TransactionId xid,
 {
 	CommandId	cid;
 
-	/*
-	 * we only log new_cid's if a catalog tuple was modified, so mark the
-	 * transaction as containing catalog modifications
-	 */
-	ReorderBufferXidSetCatalogChanges(builder->reorder, xid, lsn);
-
 	ReorderBufferAddNewTupleCids(builder->reorder, xlrec->top_xid, lsn,
 								 xlrec->target_locator, xlrec->target_tid,
 								 xlrec->cmin, xlrec->cmax,
-- 
2.34.1

