Re: Logical replication row filter loses unchanged toasted columns

From: vignesh C <vignesh21(at)gmail(dot)com>
To: "Zhijie Hou (Fujitsu)" <houzj(dot)fnst(at)fujitsu(dot)com>
Cc: Shinya Kato <shinya11(dot)kato(at)gmail(dot)com>, "Hayato Kuroda (Fujitsu)" <kuroda(dot)hayato(at)fujitsu(dot)com>, Amit Kapila <amit(dot)kapila16(at)gmail(dot)com>, Matthias van de Meent <boekewurm+postgres(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>, Masahiko Sawada <sawada(dot)mshk(at)gmail(dot)com>
Subject: Re: Logical replication row filter loses unchanged toasted columns
Date: 2026-09-02 06:10:34
Message-ID: CALDaNm2_ViEtbt8c_iPg3wXg773PLX4em-fOEfhkgdUFkjF53A@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Tue, 1 Sept 2026 at 19:13, vignesh C <vignesh21(at)gmail(dot)com> wrote:
>
> I found another issue with the UPDATE-to-INSERT transformation when an
> unchanged column is stored out-of-line, where the row-filter
> publication is added while the UPDATE is in progress.
> The relcache check in heap_update() can become stale before the WAL
> record is written. For example, ALTER PUBLICATION ... ADD TABLE can
> commit while the UPDATE is parked, since its ShareUpdateExclusiveLock
> does not conflict with the UPDATE's RowExclusiveLock. The UPDATE
> therefore does not preserve the unchanged TOAST value, but pgoutput
> later sees the row filter and transforms the UPDATE into an INSERT.

Following up on the concurrent 'ALTER PUBLICATION' problem I reported
yesterday: the race window itself is not specific to this patch. It
also exists on HEAD through a different scenario of the same stale
relcache state, and I think that issue can be fixed separately.

The relevant invariant is that a publication row filter may only
reference replica identity columns. This is not checked when the
filter is created — 'ALTER PUBLICATION' accepts a filter on any column
— but it is checked at DML time from the publication descriptor:
...
RelationBuildPublicationDesc(rel, &pubdesc);
if (cmd == CMD_UPDATE && !pubdesc.rf_valid_for_update)
ereport(ERROR,
errmsg("cannot update table \"%s\"", ...),
errdetail("Column used in the publication WHERE expression "
"is not part of the replica identity."));
...

Since 'ALTER PUBLICATION ... ADD TABLE' takes only
'ShareUpdateExclusiveLock', a filter on a non-replica-identity column
can be committed after 'CheckCmdReplicaIdentity()' has already allowed
the UPDATE.

I can reproduce this with the same injection point as before, without
any TOAST data:
CREATE TABLE t (id int PRIMARY KEY, val int);
CREATE PUBLICATION pub_sync FOR TABLE t;
CREATE PUBLICATION pub_filtered;
-- Subscribe to both, then:
INSERT INTO t VALUES (1, 1);
ALTER PUBLICATION pub_sync DROP TABLE t;

-- Session A:
SELECT injection_points_set_local();
SELECT injection_points_attach('heap_update-before-pin', 'wait');
UPDATE t SET val = 2 WHERE id = 1;

-- Session B, while Session A is waiting:
ALTER PUBLICATION pub_filtered ADD TABLE t WHERE (val = 1);

-- Wake Session A:
SELECT injection_points_wakeup('heap_update-before-pin');

I have attached 'row_filter_nonri_column_race.pl', which reproduces the issue.
I feel the issue reported at [1] is independent of the patch under
review and should be addressed separately, along with this issue, in a
separate thread.

[1] - https://www.postgresql.org/message-id/CALDaNm2MF18JPUz_rwMzbgeqy1PUwxVKwEU8E1cWPObq-yqxrg%40mail.gmail.com

Regards,
Vignesh

Attachment Content-Type Size
row_filter_nonri_column_race.pl text/x-perl-script 4.9 KB

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Etsuro Fujita 2026-09-02 06:24:26 Re: Remove fcinfo from statistics update internal functions
Previous Message Ewan Young 2026-09-02 06:07:57 Re: RANGE partition pruning can still exclude the default partition