Fix bug of UPDATE/DELETE FOR PORTION OF with inheritance tables

First seen: 2026-05-07 03:40:12+00:00 · Messages: 7 · Participants: 2

Latest Update

2026-06-01 · claude-opus-4-6

Monthly Summary: Fix UPDATE/DELETE FOR PORTION OF with Inheritance Tables (May 2026)

Overview

This thread addresses a correctness bug in PostgreSQL's temporal DML (FOR PORTION OF) when applied to classical (non-partitioned) inheritance hierarchies. The bug involves two defects: (1) leftover tuples from range-splitting are incorrectly inserted into the parent table instead of the originating child, and (2) attribute number mismatches between parent and child relations cause tuple corruption. The month saw the thread briefly merged into a larger consolidated thread, then separated back out with a new standalone v2 patch and discussion toward v3.

Key Developments

Bug Identification and Impact

The core issue is that FOR PORTION OF implementation conflates "has children" with "is partitioned." In partitioned tables, routing leftovers through the root is correct because tuple routing dispatches to the right leaf. In plain inheritance, no such routing exists — leftovers silently land in the parent table, constituting data corruption from the user's perspective (affecting visibility under ONLY, triggers, constraints, RLS, and logical replication).

Thread Merge and Un-merge

Early in the month, the thread was administratively merged into a larger thread addressing updatedCols issues (message-id CAHg+QDcd=t69gLf9yQexO07EJ2mx0Z70NFHo6h94X1EDA=hM0g). Once the UPDATE permission checking issue was resolved independently on that thread, Paul Jungwirth restored this patch's independence with a v2 submission.

v2 Patch and Architectural Discussion

Paul Jungwirth posted a standalone v2 patch that:

Lazy vs. Eager Initialization Debate

Paul initially proposed moving FOR PORTION OF per-child initialization into ExecInitModifyTable() (eager), arguing the planner already prunes irrelevant partitions. He then reversed this position after recognizing that runtime filtering can eliminate child tables beyond what the planner prunes, making lazy initialization still valuable for avoiding unnecessary work.

Direction Toward v3

The preferred approach retains ExecInitForPortionOf() as a refactoring that reduces branching, consolidates attnum tracking, and makes the inheritance fix clearer. The earlier v1 patch (from jian he) initialized tuple conversion maps for all children but only used them for partitions — a code smell the refactored version avoids.

Open Questions

History (1 prior analysis)
2026-06-01 · claude-opus-4-6

Incremental Update: v3 Patch Confirmed Working by Original Reporter

Summary

Evan Li (the original bug reporter) has tested Paul Jungwirth's v3 patch and confirmed it resolves the inheritance bug. No new technical arguments, no new patch version, and no architectural debate — this is a validation/testing message.

Key Confirmation

Evan's test demonstrates the core fix working correctly:

  • A row in child table c is updated via FOR PORTION OF on parent p
  • The leftover tuples now correctly remain in child c (not teleported to parent p)
  • The extra column value is preserved across all split rows
  • SELECT * FROM ONLY p returns 0 rows — confirming no data leakage to parent

UPDATE OF Trigger Issue Acknowledged as Out-of-Scope

Evan explicitly notes that the UPDATE OF trigger bug is still reproducible with v3, but confirms this is expected — that fix lives in the separate thread's patch. The two patches are now cleanly decoupled, which was the goal of the refactoring.

Status

The patch appears ready for committer review. The reporter is satisfied, no outstanding objections have been raised, and the scope is well-defined (inheritance-only fix, independent of the updatedCols/trigger thread).