Monthly Summary: FOR PORTION OF and GENERATED STORED Columns (May 2026)
Overview
This thread underwent a dramatic arc in May 2026: a 12-version patch series addressing stale GENERATED STORED columns on FOR PORTION OF updates was ultimately rendered unnecessary by a fundamental design reversal decided at PGConf.dev. The original bug, the iterative fixes, and the final resolution all illuminate a deep architectural tension in how PostgreSQL's updatedCols bitmapset serves multiple purposes.
The Original Bug
PostgreSQL 18's FOR PORTION OF feature narrows a range column when performing temporal UPDATE/DELETE, inserting "leftover" rows for portions outside the targeted sub-range. The bug: GENERATED ALWAYS AS ... STORED columns referencing the range column produce stale values on the updated row because the range column is deliberately omitted from updatedCols, and the executor only recomputes generated columns whose dependencies intersect that bitmapset.
The omission was intentional — it was a permissions design choice (users shouldn't need UPDATE privilege on the range column since the system, not the user, is narrowing it). But updatedCols also drives generated column recomputation, UPDATE OF trigger firing, and HOT/index optimizations, all of which need to know the range column changed.
Patch Evolution (v1–v12)
The series went through increasingly sophisticated approaches:
- v1 (Satya): Narrow fix in
ExecInitGenerated— correct for generated columns but missed triggers and HOT. - v2 (Jian): Centralized fix in
ExecGetUpdatedCols— covers all consumers. - v3–v5 (Paul): Fixed partition attno-mapping bugs (parent vs. child coordinate space).
- v8: Introduced a NULL-deref crash (caught by Peter Eisentraut via injection_points tests).
- v9: Fixed the crash by reordering NULL check.
- v10–v11: Split into two patches (0001: generated columns/triggers; 0002: multi-level inheritance). Fixed a real
bms_add_memberaliasing bug whereperminfo->updatedColscould be mutated in place. - v12: Polish — conditional
bms_copyonly when needed, dropped incorrectes_query_cxtswitch, factored repeated expressions.
Key technical findings during this period:
bms_add_membercan mutate its input in place, so aliasingperminfo->updatedColswithout copying corrupts the permission bitmapset.- Memory context must remain consistent (current context, not
es_query_cxt) since the function recomputes rather than caches. - Virtual generated columns as the range column cause a segfault (spun off to separate CF #6764).
The Design Reversal (PGConf.dev)
After v12 achieved review consensus, Paul reported that an in-person discussion with Peter Eisentraut at PGConf.dev led to a complete reversal: the range column should require UPDATE permission, and therefore should be in updatedCols from the start.
This eliminates the entire ExecGetUpdatedCols centralization approach. The fix becomes trivial: add the range attno in transformForPortionOfClause at parse time. All downstream consumers (permissions, generated columns, triggers, HOT) work automatically via existing mechanisms.
The SQL Standard's apparent intent (no UPDATE privilege needed on the range column) was overridden as pragmatically incorrect for PostgreSQL's architecture.
Remaining Items
- INSERT permission is still not required for temporal leftovers (per SQL Standard).
- Multi-level inheritance fix (former 0002) to be submitted separately.
- Virtual generated columns as range columns (segfault) tracked in CF #6764.
- BEFORE INSERT triggers returning NULL on leftovers: confirmed as expected/documented behavior.