agora inbox for pgsql-committers@postgresql.org
help / color / mirror / Atom feedFrom: Masahiko Sawada <msawada@postgresql.org>
To: pgsql-committers@lists.postgresql.org
Subject: pgsql: Fix logical decoding of empty prepared transactions.
Date: Tue, 28 Jul 2026 19:34:54 +0000
Message-ID: <E1wonZm-00000000hTu-43PN@gemulon.postgresql.org> (raw)
Fix logical decoding of empty prepared transactions.
A two-phase transaction that is assigned an XID but produces no change
to be decoded -- for example, one that only acquires row locks via
SELECT ... FOR SHARE -- has no base snapshot in the reorder
buffer. ReorderBufferReplay() already skips such a transaction at
PREPARE time and never invokes the begin_prepare/change/prepare
callbacks for it, but ReorderBufferFinishPrepared() still called the
commit_prepared (or rollback_prepared) callback. As a result a
spurious COMMIT/ROLLBACK PREPARED was sent to the output plugin with
no preceding PREPARE. For the built-in subscriber this breaks
replication (the apply worker fails to find the prepared transaction),
and test_decoding could even crash.
Fix this by detecting an empty transaction (base_snapshot == NULL) in
ReorderBufferFinishPrepared() and cleaning it up without invoking the
commit/rollback prepared callbacks, mirroring the existing empty
transaction handling in ReorderBufferReplay().
On v18 and newer versions, commit 072ee847ad4 changed
ReorderBufferPrepare() to send the prepare whenever it had not already
been sent, which also fires for empty transactions and emits a
spurious PREPARE. On those branches ReorderBufferPrepare() is
therefore additionally guarded with base_snapshot != NULL. This guard
and the Assert(!rbtxn_sent_prepare()) added in
ReorderBufferFinishPrepared(), are not necessary on v17 and older
versions: there ReorderBufferPrepare() only sends a prepare for
concurrently-aborted transactions (which never applies to an empty
transaction) and the RBTXN_SENT_PREPARE flag does not exist.
Back-patch to v14, where decoding of two-phase transactions was
introduced.
Bug: #19556
Reported-by: Alexander Kozhemyakin <a.kozhemyakin@postgrespro.ru>
Reviewed-by: Amit Kapila <amit.kapila16@gmail.com>
Discussion: https://postgr.es/m/19556-daa6d7ea65054d48@postgresql.org
Backpatch-through: 14
Branch
------
REL_16_STABLE
Details
-------
https://git.postgresql.org/pg/commitdiff/7446183463b45f0c9ec45ad1a9ad4cc08190218c
Modified Files
--------------
contrib/test_decoding/expected/twophase.out | 25 +++++++++++++++
contrib/test_decoding/sql/twophase.sql | 12 ++++++++
src/backend/replication/logical/reorderbuffer.c | 18 +++++++++++
src/test/subscription/t/021_twophase.pl | 41 +++++++++++++++++++++++++
4 files changed, 96 insertions(+)
view thread (7+ messages) latest in thread
Message-ID: <E1wonZm-00000000hTu-43PN@gemulon.postgresql.org>
Permalink: ../E1wonZm-00000000hTu-43PN@gemulon.postgresql.org/
Also on: postgresql.org/message-id/E1wonZm-00000000hTu-43PN@gemulon.postgresql.org
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-committers@postgresql.org
Cc: msawada@postgresql.org, pgsql-committers@lists.postgresql.org
Subject: Re: pgsql: Fix logical decoding of empty prepared transactions.
In-Reply-To: <E1wonZm-00000000hTu-43PN@gemulon.postgresql.org>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox