From a365dfb595f60750776538dcb530a0eb1fd60e98 Mon Sep 17 00:00:00 2001 From: Vishal Prasanna Date: Wed, 18 Feb 2026 15:55:35 +0530 Subject: [PATCH] Fix specinsert leak in ReorderBufferProcessTXN error path --- .../replication/logical/reorderbuffer.c | 6 +++ src/test/subscription/t/100_bugs.pl | 38 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index 4bd1f7af061..eafec433d79 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -2694,6 +2694,12 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, } else { + /* Return the specinsert change before cleaning up the transaction */ + if (specinsert != NULL) + { + ReorderBufferReturnChange(rb, specinsert, true); + specinsert = NULL; + } ReorderBufferCleanupTXN(rb, txn); MemoryContextSwitchTo(ecxt); PG_RE_THROW(); diff --git a/src/test/subscription/t/100_bugs.pl b/src/test/subscription/t/100_bugs.pl index 17accd11d93..73dca25f0e5 100644 --- a/src/test/subscription/t/100_bugs.pl +++ b/src/test/subscription/t/100_bugs.pl @@ -597,4 +597,42 @@ $node_publisher->safe_psql('postgres', "DROP DATABASE regress_db"); $node_publisher->stop('fast'); +# Ensure logical decoder doesn't crash if error occurs +# while processing an INSERT ... ON CONFLICT statement +$node_publisher = PostgreSQL::Test::Cluster->new('logical_decoder'); +$node_publisher->init(allows_streaming => 'logical'); +$node_publisher->start; + +$node_publisher->safe_psql( + 'postgres', qq( + CREATE TABLE tab_upsert (a INT PRIMARY KEY, b INT); + SELECT * FROM pg_create_logical_replication_slot('upsert_slot', 'pgoutput'); + INSERT INTO tab_upsert (a, b) VALUES (1, 1) + ON CONFLICT(a) DO UPDATE SET b = excluded.b; +)); + +# Decode the changes without a publication and +# verify that the logical decoder doesn't crash. +($ret, $stdout, $stderr) = $node_publisher->psql( + 'postgres', qq( + SELECT * + FROM pg_logical_slot_peek_binary_changes( + 'upsert_slot', + NULL, + NULL, + 'proto_version', '1', + 'publication_names', 'pub_that_does_not_exist' + ); +)); + +ok( $stderr =~ qr/publication "pub_that_does_not_exist" does not exist/, + 'peek logical changes with non-existent publication throws error' +); + +# Clean up +$node_publisher->safe_psql('postgres', "SELECT pg_drop_replication_slot('upsert_slot')"); +$node_publisher->safe_psql('postgres', "DROP TABLE tab_upsert"); + +$node_publisher->stop('fast'); + done_testing(); -- 2.50.1 (Apple Git-155)