Migrations are incompatible with PostgreSQL 17.12+ on fresh install
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 12/100
Research direction
The trigger is in src/kdive/db/schema/0112_capture_operation_supervision.sql (function at line 334, worker_incarnations hazard at 352) and 0113_capture_publication_fence.sql line 41, with the runner checksum check in src/kdive/db/migrate.py and scripts/guards/schema_immutable_guard.py. Reproduce with the three-line CREATE TABLE/CREATE FUNCTION/ALTER TABLE case on 17.12. Done means a fresh 17.12 database migrates the full chain, but this requires an ADR decision first, so it is not a good first task.
Written by the indexing model from the issue text.
Description
Problem
On a fresh install, the migration chain is incompatible with PostgreSQL 17.12 and later. 0113 fails with:
psycopg.errors.FeatureNotSupported: cannot alter table "capture_operations" because function capture_launch_abort_evidence_valid(capture_operations,jsonb) uses its row type
#3610 restores CI, compose and lab bring-ups by pinning the compose and fixture Postgres images by digest to 17.10. That pin defers the break; it does not fix it. Two cases still hit it:
- Any operator running their own 17.12+ server.
- Any future bump of the pin.
This issue tracks the cause-level fix. It relates to #3610 but does not block it. It came up during campaign work on #3583/#3584.
Evidence
Trigger
-
src/kdive/db/schema/0112_capture_operation_supervision.sql:334createspublic.capture_launch_abort_evidence_valid(p_operation public.capture_operations, p_evidence jsonb). It is aLANGUAGE sqlfunction with a new-style (SQL-standard)RETURNbody. The body reads row fields:p_operation.host_instanceandp_operation.launch_token, at lines 347-349. -
src/kdive/db/schema/0113_capture_publication_fence.sql:41runsALTER TABLE public.capture_operations ADD COLUMN publication_state text NOT NULL DEFAULT 'pending', ..., which 17.12 rejects. -
The narrowed trigger was reproduced on 17.12 (
postgres:17@sha256:846a7ddaa67a4b354ae1d21087b683901aa6113a4ed626d3787cfbf6c85e3e07). The failing case isADD COLUMNwith a non-null default on a table whose row type is a parameter of a new-style SQL function body that reads its fields:CREATE TABLE a (id int, s text); CREATE FUNCTION fa(p a) RETURNS boolean LANGUAGE sql RETURN p.s IS NOT NULL; ALTER TABLE a ADD COLUMN x text DEFAULT (1)::text; -- 17.12: ERROR: cannot alter table "a" because function fa(a) uses its row type -- 17.10: ALTER TABLE -
These cases still succeed on 17.12:
ADD COLUMNwithout a defaultADD CONSTRAINT ... CHECK- string-body (
AS $$ ... $$) SQL functions - plpgsql functions
- new-style bodies that do not read a row field
-
The same latent hazard exists on
worker_incarnations.0112:352declarespublic.capture_recovery_authorized(p_owner public.worker_incarnations, p_replacement public.worker_incarnations)with a new-style body that reads row fields (p_owner.state,p_owner.terminated_at, ...).- No current migration alters
capture_operations(after 0113) orworker_incarnations(after 0112). - Any future
ADD COLUMN ... DEFAULTon either table will fail on fresh installs and on existing 17.12+ databases.
- No current migration alters
-
The full chain on a fresh 17.12 database stops at 0113. On 17.10 it applies all 187 migrations.
-
Databases that already applied 0113 are unaffected by a minor upgrade. I migrated a 17.10 data directory through 0195 and restarted it on 17.12: the runner applied 0, the checksums verified, and
capture_operationswas readable.
Upstream change
The PostgreSQL 17.12 release notes (https://www.postgresql.org/docs/release/17.12/, released 2026-11-12) have an item under "Add defenses against problems caused by altering composite types concurrently with operations on data of those types". It reads: "Tighten the rules about whether ALTER COLUMN TYPE is allowed when dependencies exist on the composite type. For example, it's no longer allowed when a function's default expression or new-style SQL function body depends on the type." The note names only ALTER COLUMN TYPE. The reproduction above shows the same check also rejects ADD COLUMN ... DEFAULT.
Immutability blocks the obvious fix twice
src/kdive/db/migrate.py:125-132hard-fails on any applied file whose checksum changed:applied migration … checksum changed; applied migrations are immutable (ADR-0015).scripts/guards/schema_immutable_guard.pyforbids modifying, deleting or renaming any existingsrc/kdive/db/schema/*.sqlfile (#1218, #1723).- So 0112 and 0113 cannot be edited in place. Doing so would break every database that applied them, and the guard rejects the edit regardless.
Expected
- A fresh install migrates through the whole chain on PostgreSQL 17.12 and later.
- Databases that already applied 0112/0113 keep passing the ADR-0015 checksum check.
- The #3610 digest pin on
docker-compose.ymlandtests/compose/fixtures/worker-lifecycle-live.ymlis lifted, or moved to a 17.12+ digest, once the chain is compatible. The db fixture digest intests/db/conftest.pycan then move to 17.12+ as well. - A guard keeps the hazard from coming back.
Proposed approach
This needs an ADR before implementation, because each route changes or qualifies ADR-0015. The options are:
- ADR-0015 exception. Add a reviewed allowlist mapping
(version, old_sha256) → new_sha256, honoured by bothmigrate.pyandschema_immutable_guard.py. 0113 could then wrap itsALTERby dropping and recreatingcapture_launch_abort_evidence_validand re-issuing its grant (0112:1566). Its callers at0112:1033/:1113are plpgsql and resolve at run time. - Runner hook. Before applying 0113 on a fresh database, drop the dependent function, then recreate it afterwards. The files stay byte-identical, but the runner gains special-cased knowledge of specific migrations.
- Re-baseline. Squash the chain into a baseline migration for fresh installs, written so it never adds a defaulted column under a dependent new-style function. Existing databases keep their recorded history.
Whichever route is chosen, also:
- Pre-empt the
worker_incarnationshazard (0112:352), for example by redefining both functions with scalar parameters or string bodies in a new migration. - Add a guard that fails a new migration which runs
ALTER TABLE ... ADD COLUMN ... DEFAULToncapture_operationsorworker_incarnations. More generally, it should cover any table whose row type is a parameter of a new-style SQL function body. The guard is not needed for tables where the dependency has been removed. - Lift the #3610 digest pin, or move it to a 17.12+ digest. Run the full chain on a fresh 17.12+ database as the proof.
Related: #3610 (digest pin, the fast unblock). This issue does not block #3610.
- Dominant language
- Python
- Stars
- 0
- Forks
- 0
- Avg merge
- 9h 13m
- Merged PRs (30d)
- 197
Getting set up
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from randomparity/kdive
-
area:core-platform effort:S priority:P3 risk:night-safe status:ready type:test
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
randomparity/kdive#3620 ·
Maintainers usually reply within 1 day
-
area:providers effort:S priority:P3 risk:night-watch status:ready type:docs
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
randomparity/kdive#3618 ·
Maintainers usually reply within 1 day
-
area:core-platform effort:S priority:P3 risk:night-safe status:ready type:test
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
randomparity/kdive#3603 ·
Maintainers usually reply within 1 day
-
area:providers effort:S priority:P3 risk:night-safe status:ready type:docs
Difficulty 1/5 1-3 hours Newbie friendliness 82/100
randomparity/kdive#3593 ·
Maintainers usually reply within 1 day
-
area:providers effort:S priority:P3 risk:night-safe status:ready type:refactor
Difficulty 1/5 Under an hour Newbie friendliness 84/100
randomparity/kdive#3592 ·
Maintainers usually reply within 1 day
All issues in randomparity/kdive
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
dbt-labs/spark-utils#51 ·
-
bug pydanty:is-working
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
pydantic/pydantic-ai#13444 ·
Maintainers usually reply within 1 day
-
good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
good first issue help wanted
Difficulty 1/5 Under an hour Newbie friendliness 72/100
-
changelog investigate
Difficulty 1/5 Under an hour Newbie friendliness 68/100
ramnes/notion-sdk-py#502 ·