BUG #19556: Segmentation fault in test_decoding

Started by PG Bug reporting form2 months ago10 messagesbugs
Jump to latest
#1PG Bug reporting form
noreply@postgresql.org

The following bug has been logged on the website:

Bug reference: 19556
Logged by: Alexander Kozhemyakin
Email address: a.kozhemyakin@postgrespro.ru
PostgreSQL version: 19beta2
Operating system: ubuntu 26.04
Description:

Hi,

The following script causes the server to crash with a Segmentation fault.

initdb -D data
echo "
max_prepared_transactions = '15'
wal_level = logical " >> data/postgresql.auto.conf
pg_ctl -D data -l logfile start

psql <<EOF
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot',
'test_decoding', false, true);
CREATE TABLE test (id int PRIMARY KEY, data text);
INSERT INTO test VALUES (1, 'test data');
BEGIN;
SELECT * FROM test WHERE id = 1 FOR SHARE;
PREPARE TRANSACTION 'p1';
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL,
'include-xids', '0', 'skip-empty-xacts', '1');
EOF

backtrace
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0000749f6ee23f54 in pg_decode_prepare_txn (ctx=0x6344c3d7f3f0,
txn=0x6344c3d7d320, prepare_lsn=24628632) at
/pgpro/postgres/contrib/test_decoding/test_decoding.c:378
378 if (data->skip_empty_xacts && !txndata->xact_wrote_changes)
(gdb) bt
#0 0x0000749f6ee23f54 in pg_decode_prepare_txn (ctx=0x6344c3d7f3f0,
txn=0x6344c3d7d320, prepare_lsn=24628632) at
/pgpro/postgres/contrib/test_decoding/test_decoding.c:378
#1 0x00006344a8e8293c in prepare_cb_wrapper (cache=<optimized out>,
txn=<optimized out>, prepare_lsn=<optimized out>) at
/pgpro/postgres/src/backend/replication/logical/logical.c:985
#2 0x00006344a8e90c24 in ReorderBufferPrepare (rb=0x6344c3cd9e10,
xid=xid@entry=745, gid=gid@entry=0x7ffcb3620ef4 "p1") at
/pgpro/postgres/src/backend/replication/logical/reorderbuffer.c:2934
#3 0x00006344a8e7fa26 in DecodePrepare (ctx=0x6344c3d7f3f0,
buf=0x7ffcb3621030, parsed=0x7ffcb3620ea0) at
/pgpro/postgres/src/backend/replication/logical/decode.c:815
#4 xact_decode (ctx=0x6344c3d7f3f0, buf=0x7ffcb3621030) at
/pgpro/postgres/src/backend/replication/logical/decode.c:347
#5 0x00006344a8e7f223 in LogicalDecodingProcessRecord
(ctx=ctx@entry=0x6344c3d7f3f0, record=0x6344c3d7f788) at
/pgpro/postgres/src/backend/replication/logical/decode.c:116
#6 0x00006344a8e8577b in pg_logical_slot_get_changes_guts
(fcinfo=0x6344c3d83410, confirm=confirm@entry=true,
binary=binary@entry=false) at
/pgpro/postgres/src/backend/replication/logical/logicalfuncs.c:266
#7 0x00006344a8e858f4 in pg_logical_slot_get_changes (fcinfo=<optimized
out>) at /pgpro/postgres/src/backend/replication/logical/logicalfuncs.c:333
#8 0x00006344a8d3666d in ExecMakeTableFunctionResult
(setexpr=0x6344c3d5ffc8, econtext=0x6344c3d5fe18, argContext=<optimized
out>, expectedDesc=0x6344c3d49868, randomAccess=false)
at /pgpro/postgres/src/backend/executor/execSRF.c:234
#9 0x00006344a8d4a14b in FunctionNext (node=node@entry=0x6344c3d5fc08) at
/pgpro/postgres/src/backend/executor/nodeFunctionscan.c:94
#10 0x00006344a8d36f4c in ExecScanFetch (node=<optimized out>,
epqstate=<optimized out>, accessMtd=<optimized out>, recheckMtd=<optimized
out>) at /pgpro/postgres/src/include/executor/execScan.h:126
#11 ExecScanExtended (node=<optimized out>, accessMtd=0x6344a8d49e10
<FunctionNext>, recheckMtd=0x6344a8d49e00 <FunctionRecheck>, epqstate=0x0,
qual=0x0, projInfo=0x6344c3d49ea8)
at /pgpro/postgres/src/include/executor/execScan.h:187
#12 ExecScan (node=0x6344c3d5fc08, accessMtd=0x6344a8d49e10 <FunctionNext>,
recheckMtd=0x6344a8d49e00 <FunctionRecheck>) at
/pgpro/postgres/src/backend/executor/execScan.c:59
#13 0x00006344a8d2c17b in ExecProcNode (node=0x6344c3d5fc08) at
/pgpro/postgres/src/include/executor/executor.h:272
#14 ExecutePlan (queryDesc=0x6344c3cd55d0, operation=CMD_SELECT,
sendTuples=true, numberTuples=0, direction=<optimized out>,
dest=0x6344c3d8b7b8) at /pgpro/postgres/src/backend/executor/execMain.c:1675
#15 standard_ExecutorRun (queryDesc=0x6344c3cd55d0, direction=<optimized
out>, count=0) at /pgpro/postgres/src/backend/executor/execMain.c:364
#16 0x00006344a8f11478 in PortalRunSelect
(portal=portal@entry=0x6344c3d018b0, forward=forward@entry=true, count=0,
count@entry=9223372036854775807, dest=dest@entry=0x6344c3d8b7b8)
at /pgpro/postgres/src/backend/tcop/pquery.c:920
#17 0x00006344a8f12c5e in PortalRun (portal=portal@entry=0x6344c3d018b0,
count=count@entry=9223372036854775807, isTopLevel=isTopLevel@entry=true,
dest=dest@entry=0x6344c3d8b7b8, altdest=altdest@entry=0x6344c3d8b7b8,
qc=qc@entry=0x7ffcb3621640) at
/pgpro/postgres/src/backend/tcop/pquery.c:764
#18 0x00006344a8f0e95e in exec_simple_query (query_string=0x6344c3c80f60
"SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL,
'include-xids', '0', 'skip-empty-xacts', '1');")
at /pgpro/postgres/src/backend/tcop/postgres.c:1271
#19 0x00006344a8f103e5 in PostgresMain (dbname=<optimized out>,
username=<optimized out>) at
/pgpro/postgres/src/backend/tcop/postgres.c:4691
#20 0x00006344a8f0aaa3 in BackendMain (startup_data=<optimized out>,
startup_data_len=<optimized out>) at
/pgpro/postgres/src/backend/tcop/backend_startup.c:107
#21 0x00006344a8e6184f in postmaster_child_launch (child_type=<optimized
out>, child_slot=1, startup_data=startup_data@entry=0x7ffcb3621aec "",
startup_data_len=startup_data_len@entry=4,
client_sock=client_sock@entry=0x7ffcb3621af0) at
/pgpro/postgres/src/backend/postmaster/launch_backend.c:274
#22 0x00006344a8e653ef in BackendStartup (client_sock=0x7ffcb3621af0) at
/pgpro/postgres/src/backend/postmaster/postmaster.c:3519
#23 ServerLoop () at
/pgpro/postgres/src/backend/postmaster/postmaster.c:1688
#24 0x00006344a8e66cbf in PostmasterMain (argc=argc@entry=3,
argv=argv@entry=0x6344c3c7b440) at
/pgpro/postgres/src/backend/postmaster/postmaster.c:1386
#25 0x00006344a8b3b35e in main (argc=3, argv=0x6344c3c7b440) at
/pgpro/postgres/src/backend/main/main.c:230

first bad commit 072ee847ad4c3fb52

#2Masahiko Sawada
sawada.mshk@gmail.com
In reply to: PG Bug reporting form (#1)
Re: BUG #19556: Segmentation fault in test_decoding

On Fri, Jul 17, 2026 at 9:30 AM PG Bug reporting form
<noreply@postgresql.org> wrote:

The following bug has been logged on the website:

Bug reference: 19556
Logged by: Alexander Kozhemyakin
Email address: a.kozhemyakin@postgrespro.ru
PostgreSQL version: 19beta2
Operating system: ubuntu 26.04
Description:

Hi,

The following script causes the server to crash with a Segmentation fault.

initdb -D data
echo "
max_prepared_transactions = '15'
wal_level = logical " >> data/postgresql.auto.conf
pg_ctl -D data -l logfile start

psql <<EOF
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot',
'test_decoding', false, true);
CREATE TABLE test (id int PRIMARY KEY, data text);
INSERT INTO test VALUES (1, 'test data');
BEGIN;
SELECT * FROM test WHERE id = 1 FOR SHARE;
PREPARE TRANSACTION 'p1';
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL,
'include-xids', '0', 'skip-empty-xacts', '1');
EOF

backtrace
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0000749f6ee23f54 in pg_decode_prepare_txn (ctx=0x6344c3d7f3f0,
txn=0x6344c3d7d320, prepare_lsn=24628632) at
/pgpro/postgres/contrib/test_decoding/test_decoding.c:378
378 if (data->skip_empty_xacts && !txndata->xact_wrote_changes)
(gdb) bt
#0 0x0000749f6ee23f54 in pg_decode_prepare_txn (ctx=0x6344c3d7f3f0,
txn=0x6344c3d7d320, prepare_lsn=24628632) at
/pgpro/postgres/contrib/test_decoding/test_decoding.c:378
#1 0x00006344a8e8293c in prepare_cb_wrapper (cache=<optimized out>,
txn=<optimized out>, prepare_lsn=<optimized out>) at
/pgpro/postgres/src/backend/replication/logical/logical.c:985
#2 0x00006344a8e90c24 in ReorderBufferPrepare (rb=0x6344c3cd9e10,
xid=xid@entry=745, gid=gid@entry=0x7ffcb3620ef4 "p1") at
/pgpro/postgres/src/backend/replication/logical/reorderbuffer.c:2934
#3 0x00006344a8e7fa26 in DecodePrepare (ctx=0x6344c3d7f3f0,
buf=0x7ffcb3621030, parsed=0x7ffcb3620ea0) at
/pgpro/postgres/src/backend/replication/logical/decode.c:815
#4 xact_decode (ctx=0x6344c3d7f3f0, buf=0x7ffcb3621030) at
/pgpro/postgres/src/backend/replication/logical/decode.c:347
#5 0x00006344a8e7f223 in LogicalDecodingProcessRecord
(ctx=ctx@entry=0x6344c3d7f3f0, record=0x6344c3d7f788) at
/pgpro/postgres/src/backend/replication/logical/decode.c:116
#6 0x00006344a8e8577b in pg_logical_slot_get_changes_guts
(fcinfo=0x6344c3d83410, confirm=confirm@entry=true,
binary=binary@entry=false) at
/pgpro/postgres/src/backend/replication/logical/logicalfuncs.c:266
#7 0x00006344a8e858f4 in pg_logical_slot_get_changes (fcinfo=<optimized
out>) at /pgpro/postgres/src/backend/replication/logical/logicalfuncs.c:333
#8 0x00006344a8d3666d in ExecMakeTableFunctionResult
(setexpr=0x6344c3d5ffc8, econtext=0x6344c3d5fe18, argContext=<optimized
out>, expectedDesc=0x6344c3d49868, randomAccess=false)
at /pgpro/postgres/src/backend/executor/execSRF.c:234
#9 0x00006344a8d4a14b in FunctionNext (node=node@entry=0x6344c3d5fc08) at
/pgpro/postgres/src/backend/executor/nodeFunctionscan.c:94
#10 0x00006344a8d36f4c in ExecScanFetch (node=<optimized out>,
epqstate=<optimized out>, accessMtd=<optimized out>, recheckMtd=<optimized
out>) at /pgpro/postgres/src/include/executor/execScan.h:126
#11 ExecScanExtended (node=<optimized out>, accessMtd=0x6344a8d49e10
<FunctionNext>, recheckMtd=0x6344a8d49e00 <FunctionRecheck>, epqstate=0x0,
qual=0x0, projInfo=0x6344c3d49ea8)
at /pgpro/postgres/src/include/executor/execScan.h:187
#12 ExecScan (node=0x6344c3d5fc08, accessMtd=0x6344a8d49e10 <FunctionNext>,
recheckMtd=0x6344a8d49e00 <FunctionRecheck>) at
/pgpro/postgres/src/backend/executor/execScan.c:59
#13 0x00006344a8d2c17b in ExecProcNode (node=0x6344c3d5fc08) at
/pgpro/postgres/src/include/executor/executor.h:272
#14 ExecutePlan (queryDesc=0x6344c3cd55d0, operation=CMD_SELECT,
sendTuples=true, numberTuples=0, direction=<optimized out>,
dest=0x6344c3d8b7b8) at /pgpro/postgres/src/backend/executor/execMain.c:1675
#15 standard_ExecutorRun (queryDesc=0x6344c3cd55d0, direction=<optimized
out>, count=0) at /pgpro/postgres/src/backend/executor/execMain.c:364
#16 0x00006344a8f11478 in PortalRunSelect
(portal=portal@entry=0x6344c3d018b0, forward=forward@entry=true, count=0,
count@entry=9223372036854775807, dest=dest@entry=0x6344c3d8b7b8)
at /pgpro/postgres/src/backend/tcop/pquery.c:920
#17 0x00006344a8f12c5e in PortalRun (portal=portal@entry=0x6344c3d018b0,
count=count@entry=9223372036854775807, isTopLevel=isTopLevel@entry=true,
dest=dest@entry=0x6344c3d8b7b8, altdest=altdest@entry=0x6344c3d8b7b8,
qc=qc@entry=0x7ffcb3621640) at
/pgpro/postgres/src/backend/tcop/pquery.c:764
#18 0x00006344a8f0e95e in exec_simple_query (query_string=0x6344c3c80f60
"SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL,
'include-xids', '0', 'skip-empty-xacts', '1');")
at /pgpro/postgres/src/backend/tcop/postgres.c:1271
#19 0x00006344a8f103e5 in PostgresMain (dbname=<optimized out>,
username=<optimized out>) at
/pgpro/postgres/src/backend/tcop/postgres.c:4691
#20 0x00006344a8f0aaa3 in BackendMain (startup_data=<optimized out>,
startup_data_len=<optimized out>) at
/pgpro/postgres/src/backend/tcop/backend_startup.c:107
#21 0x00006344a8e6184f in postmaster_child_launch (child_type=<optimized
out>, child_slot=1, startup_data=startup_data@entry=0x7ffcb3621aec "",
startup_data_len=startup_data_len@entry=4,
client_sock=client_sock@entry=0x7ffcb3621af0) at
/pgpro/postgres/src/backend/postmaster/launch_backend.c:274
#22 0x00006344a8e653ef in BackendStartup (client_sock=0x7ffcb3621af0) at
/pgpro/postgres/src/backend/postmaster/postmaster.c:3519
#23 ServerLoop () at
/pgpro/postgres/src/backend/postmaster/postmaster.c:1688
#24 0x00006344a8e66cbf in PostmasterMain (argc=argc@entry=3,
argv=argv@entry=0x6344c3c7b440) at
/pgpro/postgres/src/backend/postmaster/postmaster.c:1386
#25 0x00006344a8b3b35e in main (argc=3, argv=0x6344c3c7b440) at
/pgpro/postgres/src/backend/main/main.c:230

Thank you for the report!

first bad commit 072ee847ad4c3fb52

Right. I've confirmed that this issue can happen PG18 or newer. In
ReorderBufferPrepare() it sends a prepare message if
ReorderBufferReplay() didn't send it, but ISTM missed the case where
the transaction is empty. I'll investigate the issue further and work
on it.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

#3Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Masahiko Sawada (#2)
Re: BUG #19556: Segmentation fault in test_decoding

(CC'ing Amit as commit a271a1b50e might be related)

On Sun, Jul 19, 2026 at 12:30 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:

On Fri, Jul 17, 2026 at 9:30 AM PG Bug reporting form
<noreply@postgresql.org> wrote:

The following bug has been logged on the website:

Bug reference: 19556
Logged by: Alexander Kozhemyakin
Email address: a.kozhemyakin@postgrespro.ru
PostgreSQL version: 19beta2
Operating system: ubuntu 26.04
Description:

Hi,

The following script causes the server to crash with a Segmentation fault.

initdb -D data
echo "
max_prepared_transactions = '15'
wal_level = logical " >> data/postgresql.auto.conf
pg_ctl -D data -l logfile start

psql <<EOF
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot',
'test_decoding', false, true);
CREATE TABLE test (id int PRIMARY KEY, data text);
INSERT INTO test VALUES (1, 'test data');
BEGIN;
SELECT * FROM test WHERE id = 1 FOR SHARE;
PREPARE TRANSACTION 'p1';
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL,
'include-xids', '0', 'skip-empty-xacts', '1');
EOF

backtrace
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0000749f6ee23f54 in pg_decode_prepare_txn (ctx=0x6344c3d7f3f0,
txn=0x6344c3d7d320, prepare_lsn=24628632) at
/pgpro/postgres/contrib/test_decoding/test_decoding.c:378
378 if (data->skip_empty_xacts && !txndata->xact_wrote_changes)
(gdb) bt
#0 0x0000749f6ee23f54 in pg_decode_prepare_txn (ctx=0x6344c3d7f3f0,
txn=0x6344c3d7d320, prepare_lsn=24628632) at
/pgpro/postgres/contrib/test_decoding/test_decoding.c:378
#1 0x00006344a8e8293c in prepare_cb_wrapper (cache=<optimized out>,
txn=<optimized out>, prepare_lsn=<optimized out>) at
/pgpro/postgres/src/backend/replication/logical/logical.c:985
#2 0x00006344a8e90c24 in ReorderBufferPrepare (rb=0x6344c3cd9e10,
xid=xid@entry=745, gid=gid@entry=0x7ffcb3620ef4 "p1") at
/pgpro/postgres/src/backend/replication/logical/reorderbuffer.c:2934
#3 0x00006344a8e7fa26 in DecodePrepare (ctx=0x6344c3d7f3f0,
buf=0x7ffcb3621030, parsed=0x7ffcb3620ea0) at
/pgpro/postgres/src/backend/replication/logical/decode.c:815
#4 xact_decode (ctx=0x6344c3d7f3f0, buf=0x7ffcb3621030) at
/pgpro/postgres/src/backend/replication/logical/decode.c:347
#5 0x00006344a8e7f223 in LogicalDecodingProcessRecord
(ctx=ctx@entry=0x6344c3d7f3f0, record=0x6344c3d7f788) at
/pgpro/postgres/src/backend/replication/logical/decode.c:116
#6 0x00006344a8e8577b in pg_logical_slot_get_changes_guts
(fcinfo=0x6344c3d83410, confirm=confirm@entry=true,
binary=binary@entry=false) at
/pgpro/postgres/src/backend/replication/logical/logicalfuncs.c:266
#7 0x00006344a8e858f4 in pg_logical_slot_get_changes (fcinfo=<optimized
out>) at /pgpro/postgres/src/backend/replication/logical/logicalfuncs.c:333
#8 0x00006344a8d3666d in ExecMakeTableFunctionResult
(setexpr=0x6344c3d5ffc8, econtext=0x6344c3d5fe18, argContext=<optimized
out>, expectedDesc=0x6344c3d49868, randomAccess=false)
at /pgpro/postgres/src/backend/executor/execSRF.c:234
#9 0x00006344a8d4a14b in FunctionNext (node=node@entry=0x6344c3d5fc08) at
/pgpro/postgres/src/backend/executor/nodeFunctionscan.c:94
#10 0x00006344a8d36f4c in ExecScanFetch (node=<optimized out>,
epqstate=<optimized out>, accessMtd=<optimized out>, recheckMtd=<optimized
out>) at /pgpro/postgres/src/include/executor/execScan.h:126
#11 ExecScanExtended (node=<optimized out>, accessMtd=0x6344a8d49e10
<FunctionNext>, recheckMtd=0x6344a8d49e00 <FunctionRecheck>, epqstate=0x0,
qual=0x0, projInfo=0x6344c3d49ea8)
at /pgpro/postgres/src/include/executor/execScan.h:187
#12 ExecScan (node=0x6344c3d5fc08, accessMtd=0x6344a8d49e10 <FunctionNext>,
recheckMtd=0x6344a8d49e00 <FunctionRecheck>) at
/pgpro/postgres/src/backend/executor/execScan.c:59
#13 0x00006344a8d2c17b in ExecProcNode (node=0x6344c3d5fc08) at
/pgpro/postgres/src/include/executor/executor.h:272
#14 ExecutePlan (queryDesc=0x6344c3cd55d0, operation=CMD_SELECT,
sendTuples=true, numberTuples=0, direction=<optimized out>,
dest=0x6344c3d8b7b8) at /pgpro/postgres/src/backend/executor/execMain.c:1675
#15 standard_ExecutorRun (queryDesc=0x6344c3cd55d0, direction=<optimized
out>, count=0) at /pgpro/postgres/src/backend/executor/execMain.c:364
#16 0x00006344a8f11478 in PortalRunSelect
(portal=portal@entry=0x6344c3d018b0, forward=forward@entry=true, count=0,
count@entry=9223372036854775807, dest=dest@entry=0x6344c3d8b7b8)
at /pgpro/postgres/src/backend/tcop/pquery.c:920
#17 0x00006344a8f12c5e in PortalRun (portal=portal@entry=0x6344c3d018b0,
count=count@entry=9223372036854775807, isTopLevel=isTopLevel@entry=true,
dest=dest@entry=0x6344c3d8b7b8, altdest=altdest@entry=0x6344c3d8b7b8,
qc=qc@entry=0x7ffcb3621640) at
/pgpro/postgres/src/backend/tcop/pquery.c:764
#18 0x00006344a8f0e95e in exec_simple_query (query_string=0x6344c3c80f60
"SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL,
'include-xids', '0', 'skip-empty-xacts', '1');")
at /pgpro/postgres/src/backend/tcop/postgres.c:1271
#19 0x00006344a8f103e5 in PostgresMain (dbname=<optimized out>,
username=<optimized out>) at
/pgpro/postgres/src/backend/tcop/postgres.c:4691
#20 0x00006344a8f0aaa3 in BackendMain (startup_data=<optimized out>,
startup_data_len=<optimized out>) at
/pgpro/postgres/src/backend/tcop/backend_startup.c:107
#21 0x00006344a8e6184f in postmaster_child_launch (child_type=<optimized
out>, child_slot=1, startup_data=startup_data@entry=0x7ffcb3621aec "",
startup_data_len=startup_data_len@entry=4,
client_sock=client_sock@entry=0x7ffcb3621af0) at
/pgpro/postgres/src/backend/postmaster/launch_backend.c:274
#22 0x00006344a8e653ef in BackendStartup (client_sock=0x7ffcb3621af0) at
/pgpro/postgres/src/backend/postmaster/postmaster.c:3519
#23 ServerLoop () at
/pgpro/postgres/src/backend/postmaster/postmaster.c:1688
#24 0x00006344a8e66cbf in PostmasterMain (argc=argc@entry=3,
argv=argv@entry=0x6344c3c7b440) at
/pgpro/postgres/src/backend/postmaster/postmaster.c:1386
#25 0x00006344a8b3b35e in main (argc=3, argv=0x6344c3c7b440) at
/pgpro/postgres/src/backend/main/main.c:230

Thank you for the report!

first bad commit 072ee847ad4c3fb52

Right. I've confirmed that this issue can happen PG18 or newer. In
ReorderBufferPrepare() it sends a prepare message if
ReorderBufferReplay() didn't send it, but ISTM missed the case where
the transaction is empty. I'll investigate the issue further and work
on it.

While researching this bug, I found another one that is related and
probably should be fixed first: even in PG17 and earlier, logical
decoding calls the commit_prepared callback without first calling the
prepare callback:

BEGIN;
SELECT * FROM test WHERE id = 1 FOR SHARE;
PREPARE TRANSACTION 'p1';
COMMIT PREPARED 'p1';

In logical replication, the subscriber ends up with an error because
the prepared transaction doesn't exist on it.

In summary, with the above scenario, logical decoding calls:

- the prepare and commit_prepared callbacks (PG18+)
- the commit_prepared callback (PG17-)

Neither is correct.

I think we shouldn't call the commit_prepared callback for an empty
transaction (one that has no base snapshot), so in this case we
shouldn't call any of the begin_prepare, prepare, or commit_prepared
callbacks. Thoughts?

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

#4Amit Kapila
amit.kapila16@gmail.com
In reply to: Masahiko Sawada (#3)
Re: BUG #19556: Segmentation fault in test_decoding

On Wed, Jul 22, 2026 at 8:22 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:

While researching this bug, I found another one that is related and
probably should be fixed first: even in PG17 and earlier, logical
decoding calls the commit_prepared callback without first calling the
prepare callback:

BEGIN;
SELECT * FROM test WHERE id = 1 FOR SHARE;
PREPARE TRANSACTION 'p1';
COMMIT PREPARED 'p1';

In logical replication, the subscriber ends up with an error because
the prepared transaction doesn't exist on it.

In summary, with the above scenario, logical decoding calls:

- the prepare and commit_prepared callbacks (PG18+)
- the commit_prepared callback (PG17-)

Neither is correct.

I think we shouldn't call the commit_prepared callback for an empty
transaction (one that has no base snapshot), so in this case we
shouldn't call any of the begin_prepare, prepare, or commit_prepared
callbacks. Thoughts?

I agree that when base_snapshot is not set, we shouldn't send these
transaction commands. I checked HEAD and it seems below change in
commit 072ee847ad lead to sending prepare:
-   if (txn->concurrent_abort && !rbtxn_is_streamed(txn))
+   if (!rbtxn_sent_prepare(txn))
+   {
        rb->prepare(rb, txn, txn->final_lsn);
+       txn->txn_flags |= RBTXN_SENT_PREPARE;
+   }

Why did we remove the check of the aborted xact?

I'll check PG17 and share my findings with you.

--
With Regards,
Amit Kapila.

#5Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Kapila (#4)
Re: BUG #19556: Segmentation fault in test_decoding

On Wed, Jul 22, 2026 at 11:37 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Jul 22, 2026 at 8:22 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:

While researching this bug, I found another one that is related and
probably should be fixed first: even in PG17 and earlier, logical
decoding calls the commit_prepared callback without first calling the
prepare callback:

BEGIN;
SELECT * FROM test WHERE id = 1 FOR SHARE;
PREPARE TRANSACTION 'p1';
COMMIT PREPARED 'p1';

In logical replication, the subscriber ends up with an error because
the prepared transaction doesn't exist on it.

In summary, with the above scenario, logical decoding calls:

- the prepare and commit_prepared callbacks (PG18+)
- the commit_prepared callback (PG17-)

Neither is correct.

I think we shouldn't call the commit_prepared callback for an empty
transaction (one that has no base snapshot), so in this case we
shouldn't call any of the begin_prepare, prepare, or commit_prepared
callbacks. Thoughts?

I agree that when base_snapshot is not set, we shouldn't send these
transaction commands. I checked HEAD and it seems below change in
commit 072ee847ad lead to sending prepare:
-   if (txn->concurrent_abort && !rbtxn_is_streamed(txn))
+   if (!rbtxn_sent_prepare(txn))
+   {
rb->prepare(rb, txn, txn->final_lsn);
+       txn->txn_flags |= RBTXN_SENT_PREPARE;
+   }

Why did we remove the check of the aborted xact?

I'll check PG17 and share my findings with you.

For PG17 and before, I think we can skip replaying commit if
base_snapshot is not set similar to ReorderBufferReplay(). See
attached. The other possibility is to update ReorderBufferReplay() to
retrun a special value so that callers can skip sending commit or
prepare.

--
With Regards,
Amit Kapila.

Attachments:

fix_commit_prepare_1.patchapplication/octet-stream; name=fix_commit_prepare_1.patchDownload+18-0
#6Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Amit Kapila (#5)
Re: BUG #19556: Segmentation fault in test_decoding

On Wed, Jul 22, 2026 at 2:13 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Jul 22, 2026 at 11:37 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Jul 22, 2026 at 8:22 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:

While researching this bug, I found another one that is related and
probably should be fixed first: even in PG17 and earlier, logical
decoding calls the commit_prepared callback without first calling the
prepare callback:

BEGIN;
SELECT * FROM test WHERE id = 1 FOR SHARE;
PREPARE TRANSACTION 'p1';
COMMIT PREPARED 'p1';

In logical replication, the subscriber ends up with an error because
the prepared transaction doesn't exist on it.

In summary, with the above scenario, logical decoding calls:

- the prepare and commit_prepared callbacks (PG18+)
- the commit_prepared callback (PG17-)

Neither is correct.

I think we shouldn't call the commit_prepared callback for an empty
transaction (one that has no base snapshot), so in this case we
shouldn't call any of the begin_prepare, prepare, or commit_prepared
callbacks. Thoughts?

I agree that when base_snapshot is not set, we shouldn't send these
transaction commands. I checked HEAD and it seems below change in
commit 072ee847ad lead to sending prepare:
-   if (txn->concurrent_abort && !rbtxn_is_streamed(txn))
+   if (!rbtxn_sent_prepare(txn))
+   {
rb->prepare(rb, txn, txn->final_lsn);
+       txn->txn_flags |= RBTXN_SENT_PREPARE;
+   }

Why did we remove the check of the aborted xact?

Commit 072ee847 replaced txn->concurrent_abort flag with
RBTXN_IS_ABORTED and this flag was used to check if we have sent a
prepare message for the transaction. We thought it can be achieved by
directly checking the RBTXN_SENT_PREPARE instead.

I'll check PG17 and share my findings with you.

For PG17 and before, I think we can skip replaying commit if
base_snapshot is not set similar to ReorderBufferReplay(). See
attached. The other possibility is to update ReorderBufferReplay() to
retrun a special value so that callers can skip sending commit or
prepare.

Thank you for the patch. I like the approach the proposed patch does.
I've made a patch for PG18+ that fixes both issues with regression
tests. For PG17 or earlier, the patch doesn't need the changes in
ReorderBufferPrepare() but has the same regression tests.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

Attachments:

v1-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchtext/x-patch; charset=US-ASCII; name=v1-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchDownload+101-4
#7Amit Kapila
amit.kapila16@gmail.com
In reply to: Masahiko Sawada (#6)
Re: BUG #19556: Segmentation fault in test_decoding

On Thu, Jul 23, 2026 at 7:47 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:

Thank you for the patch. I like the approach the proposed patch does.
I've made a patch for PG18+ that fixes both issues with regression
tests. For PG17 or earlier, the patch doesn't need the changes in
ReorderBufferPrepare() but has the same regression tests.

The patch LGTM. One minor point: Shall we retain the earlier comments
(We send the prepare for the concurrently aborted xacts so that later
when rollback prepared is decoded and sent, the downstream should be
able to rollback such a xact. See comments atop DecodePrepare.)? This
makes it clear why we are sending prepare for concurrent abort cases.

--
With Regards,
Amit Kapila.

#8Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Amit Kapila (#7)
Re: BUG #19556: Segmentation fault in test_decoding

On Wed, Jul 22, 2026 at 11:50 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Jul 23, 2026 at 7:47 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:

Thank you for the patch. I like the approach the proposed patch does.
I've made a patch for PG18+ that fixes both issues with regression
tests. For PG17 or earlier, the patch doesn't need the changes in
ReorderBufferPrepare() but has the same regression tests.

The patch LGTM. One minor point: Shall we retain the earlier comments
(We send the prepare for the concurrently aborted xacts so that later
when rollback prepared is decoded and sent, the downstream should be
able to rollback such a xact. See comments atop DecodePrepare.)? This
makes it clear why we are sending prepare for concurrent abort cases.

Thank you for reviewing the 0001 patch! Yes, I agree to retain the
comment. I'll update the patch and push it early next week.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

#9Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Masahiko Sawada (#8)
Re: BUG #19556: Segmentation fault in test_decoding

On Thu, Jul 23, 2026 at 11:24 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:

On Wed, Jul 22, 2026 at 11:50 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Jul 23, 2026 at 7:47 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:

Thank you for the patch. I like the approach the proposed patch does.
I've made a patch for PG18+ that fixes both issues with regression
tests. For PG17 or earlier, the patch doesn't need the changes in
ReorderBufferPrepare() but has the same regression tests.

The patch LGTM. One minor point: Shall we retain the earlier comments
(We send the prepare for the concurrently aborted xacts so that later
when rollback prepared is decoded and sent, the downstream should be
able to rollback such a xact. See comments atop DecodePrepare.)? This
makes it clear why we are sending prepare for concurrent abort cases.

Thank you for reviewing the 0001 patch! Yes, I agree to retain the
comment. I'll update the patch and push it early next week.

I prepared the patches for all branches. I'm going to push them
tomorrow if there is no further comment.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

Attachments:

REL14_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchtext/x-patch; charset=US-ASCII; name=REL14_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchDownload+55-1
REL15_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchtext/x-patch; charset=US-ASCII; name=REL15_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchDownload+95-1
REL18_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchtext/x-patch; charset=US-ASCII; name=REL18_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchDownload+102-4
REL16_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchtext/x-patch; charset=US-ASCII; name=REL16_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchDownload+95-1
REL17_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchtext/x-patch; charset=US-ASCII; name=REL17_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchDownload+95-1
REL19_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchtext/x-patch; charset=US-ASCII; name=REL19_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchDownload+102-4
master_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchtext/x-patch; charset=US-ASCII; name=master_v2-0001-Fix-logical-decoding-of-empty-prepared-transactio.patchDownload+102-4
#10Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Masahiko Sawada (#9)
Re: BUG #19556: Segmentation fault in test_decoding

On Mon, Jul 27, 2026 at 2:28 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:

On Thu, Jul 23, 2026 at 11:24 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:

On Wed, Jul 22, 2026 at 11:50 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Jul 23, 2026 at 7:47 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:

Thank you for the patch. I like the approach the proposed patch does.
I've made a patch for PG18+ that fixes both issues with regression
tests. For PG17 or earlier, the patch doesn't need the changes in
ReorderBufferPrepare() but has the same regression tests.

The patch LGTM. One minor point: Shall we retain the earlier comments
(We send the prepare for the concurrently aborted xacts so that later
when rollback prepared is decoded and sent, the downstream should be
able to rollback such a xact. See comments atop DecodePrepare.)? This
makes it clear why we are sending prepare for concurrent abort cases.

Thank you for reviewing the 0001 patch! Yes, I agree to retain the
comment. I'll update the patch and push it early next week.

I prepared the patches for all branches. I'm going to push them
tomorrow if there is no further comment.

Pushed.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com