feat(person): V76 migration — birth/death year to date + precision columns

Pre-check aborts on corrupt year data, backfills YYYY-01-01/YEAR,
adds five named CHECK constraints, drops birth_year/death_year.
Staged-Flyway Testcontainers test covers pre-check aborts, backfill
shapes, and post-migration schema.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-12 17:47:29 +02:00
committed by marcel
parent f408f60631
commit ba7e8ca6f5
2 changed files with 286 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
-- V76: persons.birth_year/death_year (integer) → birth_date/death_date (date)
-- plus NOT NULL precision columns mirroring documents.meta_date_precision.
-- Existing years are backfilled as YYYY-01-01 at YEAR precision (ADR-039).
-- One-way migration: rollback is a targeted pg_restore -t persons from the
-- pre-deploy backup (see docs/DEPLOYMENT.md).
-- Pre-check (data quality gate — not a race guard): abort on corrupt year data
-- before any DDL runs. Single-writer family archive, so no race window matters.
DO $$ BEGIN
IF EXISTS (SELECT 1 FROM persons WHERE birth_year IS NOT NULL AND death_year IS NOT NULL AND birth_year > death_year)
THEN RAISE EXCEPTION 'V76 aborted: % persons have birth_year > death_year — fix data before migrating',
(SELECT COUNT(*) FROM persons WHERE birth_year IS NOT NULL AND death_year IS NOT NULL AND birth_year > death_year);
END IF;
IF EXISTS (SELECT 1 FROM persons WHERE birth_year = 0 OR death_year = 0)
THEN RAISE EXCEPTION 'V76 aborted: persons table contains birth_year=0 or death_year=0 rows — clean data before migrating';
END IF;
END $$;
ALTER TABLE persons ADD COLUMN birth_date date;
ALTER TABLE persons ADD COLUMN birth_date_precision varchar(16) NOT NULL DEFAULT 'UNKNOWN';
ALTER TABLE persons ADD COLUMN death_date date;
ALTER TABLE persons ADD COLUMN death_date_precision varchar(16) NOT NULL DEFAULT 'UNKNOWN';
UPDATE persons SET birth_date = make_date(birth_year, 1, 1), birth_date_precision = 'YEAR'
WHERE birth_year IS NOT NULL;
UPDATE persons SET death_date = make_date(death_year, 1, 1), death_date_precision = 'YEAR'
WHERE death_year IS NOT NULL;
-- Named constraints: readable Postgres error messages when violated.
ALTER TABLE persons ADD CONSTRAINT chk_person_birth_before_death
CHECK (death_date IS NULL OR birth_date IS NULL OR birth_date <= death_date);
ALTER TABLE persons ADD CONSTRAINT chk_person_birth_date_precision_coherence
CHECK ((birth_date IS NULL) = (birth_date_precision = 'UNKNOWN'));
ALTER TABLE persons ADD CONSTRAINT chk_person_birth_date_precision_values
CHECK (birth_date_precision IN ('DAY', 'MONTH', 'SEASON', 'YEAR', 'RANGE', 'APPROX', 'UNKNOWN'));
ALTER TABLE persons ADD CONSTRAINT chk_person_death_date_precision_coherence
CHECK ((death_date IS NULL) = (death_date_precision = 'UNKNOWN'));
ALTER TABLE persons ADD CONSTRAINT chk_person_death_date_precision_values
CHECK (death_date_precision IN ('DAY', 'MONTH', 'SEASON', 'YEAR', 'RANGE', 'APPROX', 'UNKNOWN'));
ALTER TABLE persons DROP COLUMN birth_year;
ALTER TABLE persons DROP COLUMN death_year;