From bd1b63082b23b815d26ffe1551cc4ebb9ac8a160 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Fri, 6 Jan 2023 17:00:43 +0100
Subject: [PATCH 1/2] Fix snapshot handling in logicalmsg_decode

While decoding a transactional logical message, logicalmsg_decode might
have called SnapBuildGetOrBuildSnapshot - that's incorrect, we can't
build a snapshot in such case. We don't actually need the snapshot in
this case (during replay we'll have the snapshot from the transaction),
but in assert-enabled build this crashes.

Fixed by requesting the snapshot only in non-transactional case, where
we are guaranteed to have SNAPBUILD_CONSISTENT.

Backpatch to 11. The issue exists since 9.6.

Backpatch-through: 11
Discussion: https://postgr.es/m/84d60912-6eab-9b84-5de3-41765a5449e8@enterprisedb.com
---
 src/backend/replication/logical/decode.c        | 11 +++++++++--
 src/backend/replication/logical/reorderbuffer.c |  9 +++++++++
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index c3bc8ecc926..4adced3b9ce 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -564,7 +564,7 @@ logicalmsg_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 	TransactionId xid = XLogRecGetXid(r);
 	uint8		info = XLogRecGetInfo(r) & ~XLR_INFO_MASK;
 	RepOriginId origin_id = XLogRecGetOrigin(r);
-	Snapshot	snapshot;
+	Snapshot	snapshot = NULL;
 	xl_logical_message *message;
 
 	if (info != XLOG_LOGICAL_MESSAGE)
@@ -594,7 +594,14 @@ logicalmsg_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 			  SnapBuildXactNeedsSkip(builder, buf->origptr)))
 		return;
 
-	snapshot = SnapBuildGetOrBuildSnapshot(builder);
+	/*
+	 * Get the snapshot we are expected to use. We only get here when we
+	 * already have a consistent snapshot, and the change is not meant to
+	 * be skipped.
+	 */
+	if (!message->transactional)
+		snapshot = SnapBuildGetOrBuildSnapshot(builder);
+
 	ReorderBufferQueueMessage(ctx->reorder, xid, snapshot, buf->endptr,
 							  message->transactional,
 							  message->message, /* first part of message is
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 29a018c58af..dbefff0b173 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -856,6 +856,12 @@ ReorderBufferQueueMessage(ReorderBuffer *rb, TransactionId xid,
 
 		Assert(xid != InvalidTransactionId);
 
+		/*
+		 * No explicit snapshot for transactional changes - we'll use a
+		 * snapshot derived later during apply (if not skipped).
+		 */
+		Assert(!snap);
+
 		oldcontext = MemoryContextSwitchTo(rb->context);
 
 		change = ReorderBufferGetChange(rb);
@@ -874,6 +880,9 @@ ReorderBufferQueueMessage(ReorderBuffer *rb, TransactionId xid,
 		ReorderBufferTXN *txn = NULL;
 		volatile Snapshot snapshot_now = snap;
 
+		/* non-transactional changes require a valid snapshot */
+		Assert(snapshot_now);
+
 		if (xid != InvalidTransactionId)
 			txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
 
-- 
2.39.0

