Repository navigation
postgres_scan: re-add the inheritance-parent member split with an explicit parent-only mode #735
Description
Background
#653 originally included "split inheritance parents per member table too" (353f475). It treated a plain table with inheritance children (relkind = 'r' with relhassubclass) like a partitioned table: postgres_scan listed the parent and its descendants through pg_inherits and scanned each one with its own COPY (SELECT ... FROM ONLY <member>) TO STDOUT. Without it, an inheritance parent takes the plain-table path. That path is sized from the parent's own pages, which are usually 0, so the scan runs as a single task with a near-zero row estimate.
The commit was removed from #653 because it breaks callers that need the scan to read exactly the relation they named.
The conflict
A downstream logical-replication extension snapshots a source table through postgres_scan over a dedicated connection that uses an exported snapshot. On that connection a ProcessUtility hook fails closed. A top-level COPY ... TO has to name the single expected relation OID, and the hook clears RangeVar.inh so the copy reads ONLY that relation. Each inheritance member gets its own snapshot, so the parent copy must not pick up child rows.
With the member split, scanning the parent issues copies for the children over that connection. The hook rejects every one:
ERROR: cannot restrict the snapshot copy of "parent" to its own rows
DETAIL: The copy reads relation "child" instead.
STATEMENT: COPY (SELECT "id", "value" FROM ONLY "<schema>"."child") TO STDOUT (FORMAT "binary");
The snapshot then never completes.
Proposal
Bring the inheritance-parent split back, plus an explicit way for a caller to ask for parent-only semantics, for example a postgres_scan named parameter or a session setting:
- Default (no option): behavior stays as it is now. The parent and all of its descendants are read, either as one parent scan or split per member once the split is back.
- Parent-only: do not enumerate
pg_inheritsmembers. Scan only the named relation, withFROM ONLY <relation>so child rows are excluded even on the plain path. Ctid-range parallelism within that one relation is still fine.
The replication extension would then set the option for its snapshot scans, and the hook's single-OID check can stay as strict as it is.
Partitioned tables (relkind = 'p') are not affected. The replication extension expands those to leaves itself and does not set the expected-OID restriction on them. It may still be worth defining what parent-only means for a partitioned root, either an error or a no-op.
Tests
- An inheritance parent with rows in the parent and in two children, one of which is reachable through two parents: the default scan returns every row exactly once, and with the split back it runs as a per-member parallel scan.
- The same tables with parent-only set: only the parent's own rows come back, and every issued
COPYnames the parent withONLY. This can be checked fromlog_statementor with a hook or trigger that records the relations. - A regression test on the replication side that snapshots an inheritance parent through the hook with a
postgres_scanthat has the split enabled.
Related
- duckdb_pglake: parallelize postgres_scan on partitioned tables #653: the inheritance split was dropped there.
- duckdb_pglake: harden parallel partition scans #733: stacked on duckdb_pglake: parallelize postgres_scan on partitioned tables #653 and still contains the inheritance-member code. It needs a rebase onto the new duckdb_pglake: parallelize postgres_scan on partitioned tables #653 head.
Activity
duckdb_pglake: fix partition scan edge cases