From bd770413e8dbfcd626ce624635cfa8b337def882 Mon Sep 17 00:00:00 2001 From: shihao zhong Date: Tue, 6 Oct 2026 21:44:11 -0400 Subject: [PATCH v2 1/2] Clear out dropped-column values from inserts replayed by REPACK e5d25959cf8 cleared values of dropped columns only when replaying an UPDATE. A concurrent INSERT can carry one too, for example when a trigger returns a copy of an existing row. REPACK (CONCURRENTLY) then kept it, or failed with "row is too big" if the new heap has no TOAST table. Clear them when replaying an INSERT as well. Discussion: https://postgr.es/m/CAJgoLkK2UBzB1J9buCsSUbjf7bOqz-o_0=CeiTruU789BhBw-Q@mail.gmail.com --- src/backend/commands/repack.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 899005609c2..c16ed5f4714 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -2789,6 +2789,24 @@ apply_concurrent_changes(BufFile *file, ChangeContext *chgcxt) if (kind == CHANGE_INSERT) { + TupleDesc desc = spilled_tuple->tts_tupleDescriptor; + + /* + * Dropped columns can still have values in the tuple. Mark them + * as null, like prepare_concurrent_update() does: the new heap + * might have no TOAST table to store them in. + */ + for (int i = 0; i < desc->natts; i++) + { + CompactAttribute *attr = TupleDescCompactAttr(desc, i); + + if (attr->attisdropped && !slot_attisnull(spilled_tuple, i + 1)) + { + slot_getsomeattrs(spilled_tuple, i + 1); + spilled_tuple->tts_isnull[i] = true; + } + } + apply_concurrent_insert(rel, spilled_tuple, chgcxt); } else if (kind == CHANGE_DELETE) -- 2.37.1 (Apple Git-137.1)