Re: Logical replication row filter loses unchanged toasted columns

From: Amit Kapila <amit(dot)kapila16(at)gmail(dot)com>
To: Matthias van de Meent <boekewurm+postgres(at)gmail(dot)com>
Cc: Shinya Kato <shinya11(dot)kato(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: Logical replication row filter loses unchanged toasted columns
Date: 2026-08-14 11:50:08
Message-ID: CAA4eK1JjZefzC-qtf6dOeMbfbnHPx8YXv-9Q0DobT_UH5fsJPg@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Thu, Aug 13, 2026 at 7:20 PM Matthias van de Meent
<boekewurm+postgres(at)gmail(dot)com> wrote:
>
> On Wed, 12 Aug 2026 at 05:23, Shinya Kato <shinya11(dot)kato(at)gmail(dot)com> wrote:
> >
> > I see three ways to deal with this.
> >
> > Option A: detect the missing value in pgoutput_row_filter() and raise
> > an error naming the table and the column, trading silent data loss for
> > a loud failure. [...]
> >
> > Option B: when a table belongs to a publication with a row filter,
> > make heap_update() log the whole old tuple, as it already does for
> > REPLICA IDENTITY FULL. [...]
> >
> > Option C: document the restriction and leave the behavior alone. [...]
>
> Or, an option D: Forbid the creation (and use) of filtered publication
> table definitions for tables which contain a non-identity
> varlena-typed column (i.e. the type's typlen is -1).
>

I think even if we want to block operations that can create such a
situation, we should reject only the specific updates that lead to the
problem, not every update on a table that merely has the potential for
it. We already do something similar: UPDATE/DELETE is rejected when
there's no replica identity and the table's publications publish those
operations. I'd like to apply the same principle here.

With that in mind, I could think of following two options:

Option 1
Check at DML time, inside heap_update(): Detect the problem per-row,
at the point where old/new tuple data is actually available when
following conditions are met: the relation is published and has
UPDATEs enabled, (b) some publication defines a row filter on it, (c)
the replica identity key changed value in this UPDATE, (d) the old
tuple has some externally-stored (TOASTed) attribute
(HeapTupleHasExternal()), and (e) some specific non-replica-identity
column's value is unchanged and still stored out-of-line.

Only an UPDATE that actually satisfies all five conditions is
rejected, with an error naming the offending column. All other UPDATEs
on the same table proceed normally, including ones that don't touch
the key, or ones where the TOASTed column did change.

Conditions (a), (b), (c) reuse state heap_update() already computed
for other purposes, so they add no real cost. Condition (d) gates
condition (e), so the per-attribute scan only runs when the old tuple
actually has a toasted value, which shouldn't be a hot code path as
such an update has other toast related overhead as well.

Option 2:
Check at statement time, inside CheckCmdReplicaIdentity(): Reject
upfront, before any row is touched, whenever: (a) the relation is
published and has UPDATEs enabled, (b) some publication defines a row
filter on it, (c) the relation has some toastable column outside the
replica identity, and (d) the relation has a TOAST table
(reltoastrelid is valid).

This is cheaper to check (no per-row work, no tuple deform) and fails
fast, but it's necessarily broader: toastability and the presence of a
toast table are static, table-wide properties, not row properties. A
table matching all four conditions would have every UPDATE rejected,
including ones that never touch the key column and ones where the
TOASTed column's current value happens to be short enough to be stored
inline.

I lean towards Option 1 (at least for master branch) for the reason
above. Thoughts?

--
With Regards,
Amit Kapila.

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message jian he 2026-08-14 11:56:05 Re: 2026-08-13 release announcement draft
Previous Message Andrey Borodin 2026-08-14 11:39:24 Re: Checkpointer write combining