On Thu, Aug 13, 2026 at 7:20 PM Matthias van de Meent <[email protected]> wrote: > > On Wed, 12 Aug 2026 at 05:23, Shinya Kato <[email protected]> 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.
