feat(timeline): add V77 migration for timeline_events table
Creates timeline_events plus the timeline_event_persons and timeline_event_documents join tables, all FK columns ON DELETE CASCADE (a person/document delete drops the join row, the event survives — V71-class hardening). Two CHECK constraints push integrity to Postgres: chk_timeline_event_range enforces event_date_end non-null IFF RANGE (a strict biconditional, intentionally tighter than Document's open-ended ranges), and chk_timeline_event_precision forbids exactly UNKNOWN while keeping SEASON/APPROX legal. FK and query-column indexes added up-front to avoid the V62 retrofit debt. Forward-only, additive DDL. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
|||||||
|
-- V77: timeline domain foundation (Zeitstrahl) — curated timeline events.
|
||||||
|
-- Forward-only, additive DDL. No rollback script: rollback requires manual DROP TABLE
|
||||||
|
-- (timeline_event_documents, timeline_event_persons, then timeline_events). See ADR-040.
|
||||||
|
--
|
||||||
|
-- The date block (event_date / date_precision / event_date_end) mirrors documents' so events
|
||||||
|
-- and letters share one rendering path. Two divergences from documents are INTENTIONAL and
|
||||||
|
-- enforced here in Postgres (ADR-040):
|
||||||
|
-- 1. The RANGE rule is a strict biconditional (event_date_end non-null IFF RANGE), unlike
|
||||||
|
-- documents' open-ended ranges — a curated event always has a known, closed end.
|
||||||
|
-- 2. date_precision <> 'UNKNOWN' — only OCR-inferred letters are ever undated; a curated
|
||||||
|
-- event always has at least a year. SEASON and APPROX stay legal (Sommer/ca. 1914).
|
||||||
|
|
||||||
|
CREATE TABLE timeline_events (
|
||||||
|
id UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||||
|
title VARCHAR(255) NOT NULL,
|
||||||
|
type VARCHAR(16) NOT NULL,
|
||||||
|
event_date DATE NOT NULL,
|
||||||
|
date_precision VARCHAR(16) NOT NULL DEFAULT 'YEAR',
|
||||||
|
event_date_end DATE,
|
||||||
|
description TEXT,
|
||||||
|
created_by UUID NOT NULL,
|
||||||
|
created_at TIMESTAMP,
|
||||||
|
updated_by UUID NOT NULL,
|
||||||
|
updated_at TIMESTAMP,
|
||||||
|
version BIGINT,
|
||||||
|
CONSTRAINT pk_timeline_events PRIMARY KEY (id),
|
||||||
|
-- event_date_end is non-null IFF precision is RANGE (both directions).
|
||||||
|
CONSTRAINT chk_timeline_event_range
|
||||||
|
CHECK ((date_precision = 'RANGE') = (event_date_end IS NOT NULL)),
|
||||||
|
-- Curated events are never undated. Forbids exactly UNKNOWN — every other
|
||||||
|
-- DatePrecision value (DAY, MONTH, SEASON, YEAR, RANGE, APPROX) stays legal.
|
||||||
|
CONSTRAINT chk_timeline_event_precision
|
||||||
|
CHECK (date_precision <> 'UNKNOWN')
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Join table: events ↔ persons involved.
|
||||||
|
CREATE TABLE timeline_event_persons (
|
||||||
|
timeline_event_id UUID NOT NULL,
|
||||||
|
person_id UUID NOT NULL,
|
||||||
|
CONSTRAINT pk_timeline_event_persons PRIMARY KEY (timeline_event_id, person_id),
|
||||||
|
CONSTRAINT fk_tep_event
|
||||||
|
FOREIGN KEY (timeline_event_id) REFERENCES timeline_events(id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_tep_person
|
||||||
|
FOREIGN KEY (person_id) REFERENCES persons(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Join table: events ↔ supporting letters.
|
||||||
|
CREATE TABLE timeline_event_documents (
|
||||||
|
timeline_event_id UUID NOT NULL,
|
||||||
|
document_id UUID NOT NULL,
|
||||||
|
CONSTRAINT pk_timeline_event_documents PRIMARY KEY (timeline_event_id, document_id),
|
||||||
|
CONSTRAINT fk_ted_event
|
||||||
|
FOREIGN KEY (timeline_event_id) REFERENCES timeline_events(id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_ted_document
|
||||||
|
FOREIGN KEY (document_id) REFERENCES documents(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Indexes added up-front (avoid the V62 FK-index retrofit debt): the two query columns plus
|
||||||
|
-- explicit indexes on all four FK columns.
|
||||||
|
CREATE INDEX idx_timeline_events_event_date ON timeline_events (event_date);
|
||||||
|
CREATE INDEX idx_timeline_events_type ON timeline_events (type);
|
||||||
|
CREATE INDEX idx_timeline_event_persons_person_id ON timeline_event_persons (person_id);
|
||||||
|
CREATE INDEX idx_timeline_event_persons_event_id ON timeline_event_persons (timeline_event_id);
|
||||||
|
CREATE INDEX idx_timeline_event_documents_document_id ON timeline_event_documents (document_id);
|
||||||
|
CREATE INDEX idx_timeline_event_documents_event_id ON timeline_event_documents (timeline_event_id);
|
||||||
Reference in New Issue
Block a user