Re: Logical replication row filter loses unchanged toasted columns

From: Shinya Kato <shinya11(dot)kato(at)gmail(dot)com>
To: "Zhijie Hou (Fujitsu)" <houzj(dot)fnst(at)fujitsu(dot)com>
Cc: "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-08-28 07:15:40
Message-ID: CAOzEurTYcP-R2o6wzjm4WLBGuJisb_heAO9e4iEtiNsj=wFagg@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Thu, Aug 27, 2026 at 11:23 AM Zhijie Hou (Fujitsu)
<houzj(dot)fnst(at)fujitsu(dot)com> wrote:
> Thanks for the comments. They look good to me and I have addressed them
> in V4 patch.

Thanks for working on this! I reviewed v4 and have two comments for now.

1. The extra logging does not actually depend on the key changing.

```
+ old_key_tuple = BuildOldKeyTuple(relation, &oldtup, newtup,
+ bms_overlap(modified_attrs, id_attrs) ||
+ id_has_external,
+ log_unchanged_external &&
+ HeapTupleHasExternal(&oldtup) &&
+ HeapTupleHasExternal(newtup),
+ &old_key_copied);
```

The new columns are only kept when BuildOldKeyTuple() gets past its
`if (!key_required) return NULL`, and the key_required passed above is
`bms_overlap(modified_attrs, id_attrs) || id_has_external`, so
id_has_external alone gets it there. HeapDetermineColumnsInfo() sets
id_has_external when a replica identity column of the old tuple is
stored externally, with no key change at all. So on a table whose
replica identity covers a toasted column and which has another toasted
column outside it, an UPDATE that touches neither cannot be
transformed, yet still detoasts and flattens that column into the WAL
record.

2. The test never checks that val is actually stored out-of-line. If
that ever changed, the test would keep passing without exercising the
fix, since an inline value replicates fine anyway. Asserting
pg_column_toast_chunk_id(val) IS NOT NULL on the publisher before the
UPDATE would pin that down.

--
Shinya Kato
NTT OSS Center

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Masahiko Sawada 2026-08-28 07:17:21 Re: timeout value overflow in wait for lsn
Previous Message Ewan Young 2026-08-28 07:15:05 Re: Unsafe qual pushdown through DISTINCT with simple CASE expressions