summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomas Vondra2023-02-22 14:24:09 +0000
committerTomas Vondra2023-02-22 14:24:18 +0000
commit7fe1aa991b622feaabfac5ed9c918fe8a1d598c9 (patch)
tree6829021e1b81febeb9945d6122dc3ea164eb7176
parentd0460a31de6acc8bbb86e9e3d646f1113ebb0c20 (diff)
Fix snapshot handling in logicalmsg_decode
Whe decoding a transactional logical message, logicalmsg_decode called SnapBuildGetOrBuildSnapshot. But we may not have a consistent snapshot yet at that point. We don't actually need the snapshot in this case (during replay we'll have the snapshot from the transaction), so in practice this is harmless. 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 Reviewed-by: Andres Freund Discussion: https://postgr.es/m/[email protected]
-rw-r--r--src/backend/replication/logical/decode.c14
-rw-r--r--src/backend/replication/logical/reorderbuffer.c10
2 files changed, 22 insertions, 2 deletions
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index a53e23c679..8fe7bb65f1 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,17 @@ logicalmsg_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
SnapBuildXactNeedsSkip(builder, buf->origptr)))
return;
- snapshot = SnapBuildGetOrBuildSnapshot(builder);
+ /*
+ * If this is a non-transactional change, get the snapshot we're expected
+ * to use. We only get here when the snapshot is consistent, and the
+ * change is not meant to be skipped.
+ *
+ * For transactional changes we don't need a snapshot, we'll use the
+ * regular snapshot maintained by ReorderBuffer. We just leave it NULL.
+ */
+ 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 d5f90a5f5d..2d17c551a8 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -856,6 +856,13 @@ ReorderBufferQueueMessage(ReorderBuffer *rb, TransactionId xid,
Assert(xid != InvalidTransactionId);
+ /*
+ * We don't expect snapshots for transactional changes - we'll use the
+ * snapshot derived later during apply (unless the change gets
+ * skipped).
+ */
+ Assert(!snap);
+
oldcontext = MemoryContextSwitchTo(rb->context);
change = ReorderBufferGetChange(rb);
@@ -874,6 +881,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);