-- mig 113 — t-paliad-222 / m/paliad#50 — auto-derived project codes. -- -- Adds an opponent-code slug field on litigation projects. Used as -- the middle segment when BuildProjectCode assembles an auto-derived -- project code from the ancestor tree (e.g. EXMPL.OPNT.567.INF.CFI). -- -- NULL = segment skipped silently. Existing litigation rows yield -- codes without an opponent segment until the user fills the field. -- No backfill from `title` — the litigation title is free-text -- ("Siemens AG ./. Huawei", "Mandant vs Gegner") and any regex would -- be brittle; the user enters the slug once at project creation / -- next edit. -- -- Slug shape: uppercase letters / digits / dashes, max 16 chars. -- Constraint also gates on type='litigation' so a stray value on a -- non-litigation row is rejected at the DB level (defence in depth; -- the form already hides the field on other types). -- -- Idempotent. BEGIN; ALTER TABLE paliad.projects ADD COLUMN IF NOT EXISTS opponent_code text; DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'projects_opponent_code_check' AND conrelid = 'paliad.projects'::regclass ) THEN ALTER TABLE paliad.projects ADD CONSTRAINT projects_opponent_code_check CHECK (opponent_code IS NULL OR (opponent_code ~ '^[A-Z0-9-]{1,16}$' AND type = 'litigation')); END IF; END $$; COMMENT ON COLUMN paliad.projects.opponent_code IS 'Short slug for the opposing party on a litigation project ' '(uppercase letters, digits, dashes, max 16 chars). Used as the ' 'middle segment when BuildProjectCode walks the ancestor tree to ' 'assemble a dotted project code — e.g. EXMPL.OPNT.567.INF.CFI ' '(t-paliad-222 / m/paliad#50). NULL = segment skipped silently. ' 'Only meaningful on type=''litigation'' rows; the CHECK enforces ' 'that pairing.'; COMMIT;