Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vzLSH-000uv5-2r for pgsql-bugs@arkaria.postgresql.org; Sun, 08 Mar 2026 21:14:30 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vzLSD-00BbmD-2y for pgsql-bugs@arkaria.postgresql.org; Sun, 08 Mar 2026 21:14:26 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vzLSC-00Bbm4-28 for pgsql-bugs@lists.postgresql.org; Sun, 08 Mar 2026 21:14:26 +0000 Received: from sender-pp-e101.zoho.in ([103.117.158.101]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1vzLS7-00000001BOy-3zY9 for pgsql-bugs@lists.postgresql.org; Sun, 08 Mar 2026 21:14:22 +0000 ARC-Seal: i=1; a=rsa-sha256; t=1773004450; cv=none; d=zohomail.in; s=zohoarc; b=OcM4y4zbGrxb2efQPM9jdidi76Tx5oOUo8GBl7GHEnPguDJ3tIkAk0TlaiQdt0lgSiuShYUACkPGcv0joeF+3UEh43gvFwQ3wa1Ke/AlYre7EPt4HVUumadWFSMLJSYaxwLnH+3EYmHg32zFTV/J4QAluCBU2fLQ1R5aRHqc6wo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.in; s=zohoarc; t=1773004450; h=Content-Type:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=/Tqqgh8M0MxMP8dc8MWt25KYsrSDEcpw17s+NddePDk=; b=NO6uEEsOMBL5tBTYQOLA23hV1pxmANTR5bAH8ZJchn/ISN84huS955MuXPlV5BuXDAZXitPPfGYI4g48T2+GiBopbxxGxnKKZYDjLdexH2WulvzQDzSDML0L9pGgteQzfBm0xeY3pHduvOxcarWY4Z2eSqGkB07HKlEisK5XJlo= ARC-Authentication-Results: i=1; mx.zohomail.in; dkim=pass header.i=zohocorp.com; spf=pass smtp.mailfrom=vishal.g@zohocorp.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1773004450; s=admin1; d=zohocorp.com; i=vishal.g@zohocorp.com; h=Date:Date:From:From:To:To:Cc:Cc:Message-Id:Message-Id:In-Reply-To:References:Subject:Subject:MIME-Version:Content-Type:Reply-To; bh=/Tqqgh8M0MxMP8dc8MWt25KYsrSDEcpw17s+NddePDk=; b=CE2RoaL/RKIxB9DjIbvIlJS08ggX+MrXNZdgAnrHNkB3IUhNeir6+r1fBtRHYqgg gFlYnft70XTbJipeBqWqifGxrWLfA0JwpGgtkpb5KjoTdxaYnDVWzfWlKfHKT+UO5z8 f9P6IfoKhFeNasG2DJgMRLSfxq+7eypoFy9MZKX8= Received: from mail.zoho.com by mx.zoho.in with SMTP id 1773004444884358.4006827384484; Mon, 9 Mar 2026 02:44:04 +0530 (IST) Received: from mail.zoho.in by mx.zoho.in with SMTP id 1773004443093410.74238256682133; Mon, 9 Mar 2026 02:44:03 +0530 (IST) Date: Mon, 09 Mar 2026 02:44:03 +0530 From: Vishal Prasanna To: "Hayato Kuroda (Fujitsu)" Cc: =?UTF-8?Q?=22=27=C3=81lvaro_Herrera=27=22?= , "pgsql-bugs" Message-Id: <19ccf4ccdb3.2e31ab735232.2694179375476897261@zohocorp.com> In-Reply-To: References: <19c95a57600.599bb79483132.9142801067257165882@zohocorp.com> <202602251653.e66kyhiyntlm@alvherre.pgsql> <19c988c1266.2196acf489066.5725883611676009019@zohocorp.com> Subject: RE: [BUG] Assert failure in ReorderBufferReturnTXN during logical decoding due to leaked specinsert change MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_19194_1450465315.1773004443059" Importance: Medium User-Agent: Zoho Mail X-Mailer: Zoho Mail X-Zoho-Virus-Status: 1 X-Zoho-AV-Stamp: zmail-av-0.2.2.1.5.1/272.979.15 List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk ------=_Part_19194_1450465315.1773004443059 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 7bit Hi Hayato, >> Found a workload that can reproduce the issue across all supported versions, except PG 14. >> >> For PG 14, since row_filter is not supported, so we can go with the 'publication does not exist' error instead. >> Refer: PG14-Test-specinsert-cleanup-in-ReorderBufferProcessTXN-error-path.patch >> >> For PG 15 - 18, using the row_filter option we can cause an error in the logical decoder. > > Per my understanding, for PG16-, the provided workload cannot cause an assertion > failure because it misses the Assert() in the ReorderBufferReturnTXN(), right? > Adding the line is essential, otherwise the test could pass even without the fix. Right. Added `Assert(txn->size == 0)` in `ReorderBufferReturnTXN()` for PG14, 15, 16 patches. > 01. > I think it is better to combine tests actual code patches into one. Because they > would be done when patches are committed. combined the fix and test into a single patch. since some changes are version specific (like add assert check for PG14-16, different function names from PG17 -> 18, test case) separate patches are provided per version. PG15 and PG16 share the same patch. > 02. > This issue can happen even on HEAD, but PG18-Fix-specinsert... cannot be applied > atop the branch. Can you create it as well? created a separate patch for master. > 03. 100_bugs.pl > > Other tests start from the comment like "The bug...", but it does not follow. > Can we update? > > 04. 100_bugs.pl > > Can we just rotate a log instead of starting new instance? It might be faster. > > 05. 100_bugs.pl for PG > > ``` > # The publication row filter WHERE ((a / 0) > 0) will trigger a division by zero error. > ``` > > I think the comment can be improved like: > > Create a publication with the zero-division row filter. It always throws an > ERROR before publishing changes, when the filter is evaluated. > > Please see attached my top-up patch for PG18, it addresses comments 03-05. Applied your suggestions across all versions. Thanks. Overall: PG14: Assert check + fix + test (uses 'pub does not exist' error) PG15-16: Assert check + fix + test (uses row_filter error) PG17: fix + test (uses row_filter error) PG18, master: fix + test (uses row_filter error) Regards, Vishal Prasanna Zoho Corporation ------=_Part_19194_1450465315.1773004443059 Content-Type: application/octet-stream; name=master-Fix-specinsert-leak-in-ReorderBufferProcessTXN-error-path.patch Content-Transfer-Encoding: 7bit X-ZM_AttachId: 139922972430600000 Content-Disposition: attachment; filename=master-Fix-specinsert-leak-in-ReorderBufferProcessTXN-error-path.patch From 3bdb3b1afc0d8c1c647606c954023ad3552d305f Mon Sep 17 00:00:00 2001 From: Vishal Prasanna Date: Mon, 9 Mar 2026 01:11:30 +0530 Subject: [PATCH] Fix specinsert leak in ReorderBufferProcessTXN error path --- .../replication/logical/reorderbuffer.c | 20 ++++----- src/test/subscription/t/100_bugs.pl | 45 +++++++++++++++++++ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index 4c230bcc8e4..c20c2635ab3 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -2166,8 +2166,7 @@ static void ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, Snapshot snapshot_now, CommandId command_id, - XLogRecPtr last_lsn, - ReorderBufferChange *specinsert) + XLogRecPtr last_lsn) { /* Discard the changes that we just streamed */ ReorderBufferTruncateTXN(rb, txn, rbtxn_is_prepared(txn)); @@ -2175,13 +2174,6 @@ ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, /* Free all resources allocated for toast reconstruction */ ReorderBufferToastReset(rb, txn); - /* Return the spec insert change if it is not NULL */ - if (specinsert != NULL) - { - ReorderBufferFreeChange(rb, specinsert, true); - specinsert = NULL; - } - /* * For the streaming case, stop the stream and remember the command ID and * snapshot for the streaming run. @@ -2765,6 +2757,13 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, CurrentResourceOwner = cowner; } + /* Free the specinsert change before freeing the ReorderBufferTXN */ + if (specinsert != NULL) + { + ReorderBufferFreeChange(rb, specinsert, true); + specinsert = NULL; + } + /* * The error code ERRCODE_TRANSACTION_ROLLBACK indicates a concurrent * abort of the (sub)transaction we are streaming or preparing. We @@ -2798,8 +2797,7 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, /* Reset the TXN so that it is allowed to stream remaining data. */ ReorderBufferResetTXN(rb, txn, snapshot_now, - command_id, prev_lsn, - specinsert); + command_id, prev_lsn); } else { diff --git a/src/test/subscription/t/100_bugs.pl b/src/test/subscription/t/100_bugs.pl index a23035e23fe..31dc63ae8c4 100644 --- a/src/test/subscription/t/100_bugs.pl +++ b/src/test/subscription/t/100_bugs.pl @@ -605,4 +605,49 @@ $node_publisher->safe_psql('postgres', "DROP DATABASE regress_db"); $node_publisher->stop('fast'); +# https://postgr.es/m/19c7623e882.4080fd5426212.311756747309556767%40zohocorp.com + +# The bug was that when an ERROR was raised while processing an INSERT ... ON +# CONFLICT statement, the decoded change misses to be free'd. This can cause an +# assertion failure if enabled. + +$node_publisher->rotate_logfile(); +$node_publisher->start(); + +# Create a publication with the zero-division row filter. It always throws an +# ERROR before publishing changes, when the filter is evaluated. +$node_publisher->safe_psql( + 'postgres', qq( + CREATE TABLE tab_upsert (a INT PRIMARY KEY, b INT); + CREATE PUBLICATION pub_rowfilter_error FOR TABLE tab_upsert WHERE ((a / 0) > 0); + 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 with a publication whose row filter causes a +# division by zero error, 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_rowfilter_error' + ); +)); + +ok( $stderr =~ qr/division by zero/, + 'peek logical changes with row filter causing division by zero throws error' +); + +# Clean up +$node_publisher->safe_psql('postgres', "SELECT pg_drop_replication_slot('upsert_slot')"); +$node_publisher->safe_psql('postgres', "DROP PUBLICATION pub_rowfilter_error"); +$node_publisher->safe_psql('postgres', "DROP TABLE tab_upsert"); + +$node_publisher->stop('fast'); + done_testing(); -- 2.50.1 (Apple Git-155) ------=_Part_19194_1450465315.1773004443059 Content-Type: application/octet-stream; name=PG14-Fix-specinsert-leak-in-ReorderBufferProcessTXN-error-path.patch Content-Transfer-Encoding: 7bit X-ZM_AttachId: 139922972430640040 Content-Disposition: attachment; filename=PG14-Fix-specinsert-leak-in-ReorderBufferProcessTXN-error-path.patch From b90143addb125dc180f23b65936999402591cc7b Mon Sep 17 00:00:00 2001 From: Vishal Prasanna Date: Sun, 8 Mar 2026 23:29:27 +0530 Subject: [PATCH] Fix specinsert leak in ReorderBufferProcessTXN error path --- .../replication/logical/reorderbuffer.c | 23 +++++----- src/test/subscription/t/100_bugs.pl | 43 ++++++++++++++++++- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index b708877d965..04d877d67ee 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -474,6 +474,9 @@ ReorderBufferReturnTXN(ReorderBuffer *rb, ReorderBufferTXN *txn) /* Reset the toast hash */ ReorderBufferToastReset(rb, txn); + /* All changes must be deallocated */ + Assert(txn->size == 0); + pfree(txn); } @@ -2029,8 +2032,7 @@ static void ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, Snapshot snapshot_now, CommandId command_id, - XLogRecPtr last_lsn, - ReorderBufferChange *specinsert) + XLogRecPtr last_lsn) { /* Discard the changes that we just streamed */ ReorderBufferTruncateTXN(rb, txn, rbtxn_prepared(txn)); @@ -2038,13 +2040,6 @@ ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, /* Free all resources allocated for toast reconstruction */ ReorderBufferToastReset(rb, txn); - /* Return the spec insert change if it is not NULL */ - if (specinsert != NULL) - { - ReorderBufferReturnChange(rb, specinsert, true); - specinsert = NULL; - } - /* * For the streaming case, stop the stream and remember the command ID and * snapshot for the streaming run. @@ -2589,6 +2584,13 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, if (using_subtxn) RollbackAndReleaseCurrentSubTransaction(); + /* Free the specinsert change before freeing the ReorderBufferTXN */ + if (specinsert != NULL) + { + ReorderBufferReturnChange(rb, specinsert, true); + specinsert = NULL; + } + /* * The error code ERRCODE_TRANSACTION_ROLLBACK indicates a concurrent * abort of the (sub)transaction we are streaming or preparing. We @@ -2615,8 +2617,7 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, /* Reset the TXN so that it is allowed to stream remaining data. */ ReorderBufferResetTXN(rb, txn, snapshot_now, - command_id, prev_lsn, - specinsert); + command_id, prev_lsn); } else { diff --git a/src/test/subscription/t/100_bugs.pl b/src/test/subscription/t/100_bugs.pl index 235227c7727..81ff79fce46 100644 --- a/src/test/subscription/t/100_bugs.pl +++ b/src/test/subscription/t/100_bugs.pl @@ -6,7 +6,7 @@ use strict; use warnings; use PostgresNode; use TestLib; -use Test::More tests => 11; +use Test::More tests => 12; # Bug #15114 @@ -392,3 +392,44 @@ like( $node_publisher->safe_psql('postgres', "DROP DATABASE regress_db"); $node_publisher->stop('fast'); + +# https://postgr.es/m/19c7623e882.4080fd5426212.311756747309556767%40zohocorp.com + +# The bug was that when an ERROR was raised while processing an INSERT ... ON +# CONFLICT statement, the decoded change misses to be free'd. This can cause an +# assertion failure if enabled. + +$node_publisher->rotate_logfile(); +$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'); -- 2.50.1 (Apple Git-155) ------=_Part_19194_1450465315.1773004443059 Content-Type: application/octet-stream; name=PG15-16-Fix-specinsert-leak-in-ReorderBufferProcessTXN-error-path.patch Content-Transfer-Encoding: 7bit X-ZM_AttachId: 139922972430660000 Content-Disposition: attachment; filename=PG15-16-Fix-specinsert-leak-in-ReorderBufferProcessTXN-error-path.patch From 6cfbfa82b8a31c365de29395758558e7a50d3f46 Mon Sep 17 00:00:00 2001 From: Vishal Prasanna Date: Sun, 8 Mar 2026 23:49:30 +0530 Subject: [PATCH] Fix specinsert leak in ReorderBufferProcessTXN error path --- .../replication/logical/reorderbuffer.c | 23 +++++----- src/test/subscription/t/100_bugs.pl | 45 +++++++++++++++++++ 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index 59efa73930f..2fad1c1cf38 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -478,6 +478,9 @@ ReorderBufferReturnTXN(ReorderBuffer *rb, ReorderBufferTXN *txn) /* Reset the toast hash */ ReorderBufferToastReset(rb, txn); + /* All changes must be deallocated */ + Assert(txn->size == 0); + pfree(txn); } @@ -2033,8 +2036,7 @@ static void ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, Snapshot snapshot_now, CommandId command_id, - XLogRecPtr last_lsn, - ReorderBufferChange *specinsert) + XLogRecPtr last_lsn) { /* Discard the changes that we just streamed */ ReorderBufferTruncateTXN(rb, txn, rbtxn_prepared(txn)); @@ -2042,13 +2044,6 @@ ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, /* Free all resources allocated for toast reconstruction */ ReorderBufferToastReset(rb, txn); - /* Return the spec insert change if it is not NULL */ - if (specinsert != NULL) - { - ReorderBufferReturnChange(rb, specinsert, true); - specinsert = NULL; - } - /* * For the streaming case, stop the stream and remember the command ID and * snapshot for the streaming run. @@ -2592,6 +2587,13 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, if (using_subtxn) RollbackAndReleaseCurrentSubTransaction(); + /* Free the specinsert change before freeing the ReorderBufferTXN */ + if (specinsert != NULL) + { + ReorderBufferReturnChange(rb, specinsert, true); + specinsert = NULL; + } + /* * The error code ERRCODE_TRANSACTION_ROLLBACK indicates a concurrent * abort of the (sub)transaction we are streaming or preparing. We @@ -2618,8 +2620,7 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, /* Reset the TXN so that it is allowed to stream remaining data. */ ReorderBufferResetTXN(rb, txn, snapshot_now, - command_id, prev_lsn, - specinsert); + command_id, prev_lsn); } else { diff --git a/src/test/subscription/t/100_bugs.pl b/src/test/subscription/t/100_bugs.pl index 061eb2a32c1..eee297efa2b 100644 --- a/src/test/subscription/t/100_bugs.pl +++ b/src/test/subscription/t/100_bugs.pl @@ -448,4 +448,49 @@ $node_publisher->safe_psql('postgres', "DROP DATABASE regress_db"); $node_publisher->stop('fast'); +# https://postgr.es/m/19c7623e882.4080fd5426212.311756747309556767%40zohocorp.com + +# The bug was that when an ERROR was raised while processing an INSERT ... ON +# CONFLICT statement, the decoded change misses to be free'd. This can cause an +# assertion failure if enabled. + +$node_publisher->rotate_logfile(); +$node_publisher->start(); + +# Create a publication with the zero-division row filter. It always throws an +# ERROR before publishing changes, when the filter is evaluated. +$node_publisher->safe_psql( + 'postgres', qq( + CREATE TABLE tab_upsert (a INT PRIMARY KEY, b INT); + CREATE PUBLICATION pub_rowfilter_error FOR TABLE tab_upsert WHERE ((a / 0) > 0); + 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 with a publication whose row filter causes a +# division by zero error, 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_rowfilter_error' + ); +)); + +ok( $stderr =~ qr/division by zero/, + 'peek logical changes with row filter causing division by zero throws error' +); + +# Clean up +$node_publisher->safe_psql('postgres', "SELECT pg_drop_replication_slot('upsert_slot')"); +$node_publisher->safe_psql('postgres', "DROP PUBLICATION pub_rowfilter_error"); +$node_publisher->safe_psql('postgres', "DROP TABLE tab_upsert"); + +$node_publisher->stop('fast'); + done_testing(); -- 2.50.1 (Apple Git-155) ------=_Part_19194_1450465315.1773004443059 Content-Type: application/octet-stream; name=PG17-Fix-specinsert-leak-in-ReorderBufferProcessTXN-error-path.patch Content-Transfer-Encoding: 7bit X-ZM_AttachId: 139922972430670250 Content-Disposition: attachment; filename=PG17-Fix-specinsert-leak-in-ReorderBufferProcessTXN-error-path.patch From dd95d7488fc6b6dcb2964504ea5e28d566839060 Mon Sep 17 00:00:00 2001 From: Vishal Prasanna Date: Mon, 9 Mar 2026 00:19:29 +0530 Subject: [PATCH] Fix specinsert leak in ReorderBufferProcessTXN error path --- .../replication/logical/reorderbuffer.c | 20 ++++----- src/test/subscription/t/100_bugs.pl | 45 +++++++++++++++++++ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index 4bd1f7af061..5134abe9359 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -2081,8 +2081,7 @@ static void ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, Snapshot snapshot_now, CommandId command_id, - XLogRecPtr last_lsn, - ReorderBufferChange *specinsert) + XLogRecPtr last_lsn) { /* Discard the changes that we just streamed */ ReorderBufferTruncateTXN(rb, txn, rbtxn_prepared(txn)); @@ -2090,13 +2089,6 @@ ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, /* Free all resources allocated for toast reconstruction */ ReorderBufferToastReset(rb, txn); - /* Return the spec insert change if it is not NULL */ - if (specinsert != NULL) - { - ReorderBufferReturnChange(rb, specinsert, true); - specinsert = NULL; - } - /* * For the streaming case, stop the stream and remember the command ID and * snapshot for the streaming run. @@ -2663,6 +2655,13 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, if (using_subtxn) RollbackAndReleaseCurrentSubTransaction(); + /* Free the specinsert change before freeing the ReorderBufferTXN */ + if (specinsert != NULL) + { + ReorderBufferReturnChange(rb, specinsert, true); + specinsert = NULL; + } + /* * The error code ERRCODE_TRANSACTION_ROLLBACK indicates a concurrent * abort of the (sub)transaction we are streaming or preparing. We @@ -2689,8 +2688,7 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, /* Reset the TXN so that it is allowed to stream remaining data. */ ReorderBufferResetTXN(rb, txn, snapshot_now, - command_id, prev_lsn, - specinsert); + command_id, prev_lsn); } else { diff --git a/src/test/subscription/t/100_bugs.pl b/src/test/subscription/t/100_bugs.pl index 17accd11d93..7d804e5ff9c 100644 --- a/src/test/subscription/t/100_bugs.pl +++ b/src/test/subscription/t/100_bugs.pl @@ -597,4 +597,49 @@ $node_publisher->safe_psql('postgres', "DROP DATABASE regress_db"); $node_publisher->stop('fast'); +# https://postgr.es/m/19c7623e882.4080fd5426212.311756747309556767%40zohocorp.com + +# The bug was that when an ERROR was raised while processing an INSERT ... ON +# CONFLICT statement, the decoded change misses to be free'd. This can cause an +# assertion failure if enabled. + +$node_publisher->rotate_logfile(); +$node_publisher->start(); + +# Create a publication with the zero-division row filter. It always throws an +# ERROR before publishing changes, when the filter is evaluated. +$node_publisher->safe_psql( + 'postgres', qq( + CREATE TABLE tab_upsert (a INT PRIMARY KEY, b INT); + CREATE PUBLICATION pub_rowfilter_error FOR TABLE tab_upsert WHERE ((a / 0) > 0); + 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 with a publication whose row filter causes a +# division by zero error, 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_rowfilter_error' + ); +)); + +ok( $stderr =~ qr/division by zero/, + 'peek logical changes with row filter causing division by zero throws error' +); + +# Clean up +$node_publisher->safe_psql('postgres', "SELECT pg_drop_replication_slot('upsert_slot')"); +$node_publisher->safe_psql('postgres', "DROP PUBLICATION pub_rowfilter_error"); +$node_publisher->safe_psql('postgres', "DROP TABLE tab_upsert"); + +$node_publisher->stop('fast'); + done_testing(); -- 2.50.1 (Apple Git-155) ------=_Part_19194_1450465315.1773004443059 Content-Type: application/octet-stream; name=PG18-Fix-specinsert-leak-in-ReorderBufferProcessTXN-error-path.patch Content-Transfer-Encoding: 7bit X-ZM_AttachId: 139922972430690110 Content-Disposition: attachment; filename=PG18-Fix-specinsert-leak-in-ReorderBufferProcessTXN-error-path.patch From 64deae15dc57ac7083fd5d1967f0b8a83e648b56 Mon Sep 17 00:00:00 2001 From: Vishal Prasanna Date: Mon, 9 Mar 2026 00:46:12 +0530 Subject: [PATCH] Fix specinsert leak in ReorderBufferProcessTXN error path --- .../replication/logical/reorderbuffer.c | 20 ++++----- src/test/subscription/t/100_bugs.pl | 45 +++++++++++++++++++ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index 11139a910b8..57e1ae2441b 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -2164,8 +2164,7 @@ static void ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, Snapshot snapshot_now, CommandId command_id, - XLogRecPtr last_lsn, - ReorderBufferChange *specinsert) + XLogRecPtr last_lsn) { /* Discard the changes that we just streamed */ ReorderBufferTruncateTXN(rb, txn, rbtxn_is_prepared(txn)); @@ -2173,13 +2172,6 @@ ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, /* Free all resources allocated for toast reconstruction */ ReorderBufferToastReset(rb, txn); - /* Return the spec insert change if it is not NULL */ - if (specinsert != NULL) - { - ReorderBufferFreeChange(rb, specinsert, true); - specinsert = NULL; - } - /* * For the streaming case, stop the stream and remember the command ID and * snapshot for the streaming run. @@ -2753,6 +2745,13 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, if (using_subtxn) RollbackAndReleaseCurrentSubTransaction(); + /* Free the specinsert change before freeing the ReorderBufferTXN */ + if (specinsert != NULL) + { + ReorderBufferFreeChange(rb, specinsert, true); + specinsert = NULL; + } + /* * The error code ERRCODE_TRANSACTION_ROLLBACK indicates a concurrent * abort of the (sub)transaction we are streaming or preparing. We @@ -2786,8 +2785,7 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, /* Reset the TXN so that it is allowed to stream remaining data. */ ReorderBufferResetTXN(rb, txn, snapshot_now, - command_id, prev_lsn, - specinsert); + command_id, prev_lsn); } else { diff --git a/src/test/subscription/t/100_bugs.pl b/src/test/subscription/t/100_bugs.pl index 50223054918..df6b2e1296d 100644 --- a/src/test/subscription/t/100_bugs.pl +++ b/src/test/subscription/t/100_bugs.pl @@ -605,4 +605,49 @@ $node_publisher->safe_psql('postgres', "DROP DATABASE regress_db"); $node_publisher->stop('fast'); +# https://postgr.es/m/19c7623e882.4080fd5426212.311756747309556767%40zohocorp.com + +# The bug was that when an ERROR was raised while processing an INSERT ... ON +# CONFLICT statement, the decoded change misses to be free'd. This can cause an +# assertion failure if enabled. + +$node_publisher->rotate_logfile(); +$node_publisher->start(); + +# Create a publication with the zero-division row filter. It always throws an +# ERROR before publishing changes, when the filter is evaluated. +$node_publisher->safe_psql( + 'postgres', qq( + CREATE TABLE tab_upsert (a INT PRIMARY KEY, b INT); + CREATE PUBLICATION pub_rowfilter_error FOR TABLE tab_upsert WHERE ((a / 0) > 0); + 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 with a publication whose row filter causes a +# division by zero error, 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_rowfilter_error' + ); +)); + +ok( $stderr =~ qr/division by zero/, + 'peek logical changes with row filter causing division by zero throws error' +); + +# Clean up +$node_publisher->safe_psql('postgres', "SELECT pg_drop_replication_slot('upsert_slot')"); +$node_publisher->safe_psql('postgres', "DROP PUBLICATION pub_rowfilter_error"); +$node_publisher->safe_psql('postgres', "DROP TABLE tab_upsert"); + +$node_publisher->stop('fast'); + done_testing(); -- 2.50.1 (Apple Git-155) ------=_Part_19194_1450465315.1773004443059--