EventDeadlineService.Calculate now reads source rows from paliad.deadline_rules directly (WHERE trigger_event_id IS NOT NULL), joining via UUID instead of title_de string. The legacy SELECTs against paliad.event_deadlines + paliad.event_deadline_rule_codes are gone. Migration 092: - Snapshots both legacy tables into _pre_092 audit anchors. - Adds paliad.deadline_rules.rule_codes text[] and backfills the 72 multi-code citations from event_deadline_rule_codes via the sequence_order = 1000 + ed.id convention from mig 085 (70 of 77 Pipeline-C deadlines carry codes; 7 are codeless). - Hard assertion ties source-junction-row count to backfilled text[]-element count — any sequence_order mismatch aborts the drop. - Drops the mig 086 read-only trigger (orphan once event_deadlines goes away). - Drops paliad.event_deadlines + paliad.event_deadline_rule_codes. - Final assertion: >=77 active deadline_rules with trigger_event_id NOT NULL — Slice 3 corpus must not have collapsed. - audit_reason wrapper at top so the deadline_rules UPDATE row-trigger records the reason in deadline_rule_audit. Verified via BEGIN..ROLLBACK against the live paliad DB: 72 codes backfilled into 70 rule_codes arrays, multi-code rules (RoP.029.a + RoP.030 for ed_id=6) preserve their ordering, composite rules (combine_op=max) remain intact, both tables drop cleanly, all assertions pass. Parity test rebound to deadline_rules — independent computation still re-runs applyDuration against raw column values for date/composite parity. EventDeadlineResult.ID stays int64 via the sequence_order - 1000 convention so the public /api/tools/event-deadlines wire shape is unchanged.
196 lines
9.2 KiB
SQL
196 lines
9.2 KiB
SQL
-- t-paliad-199 / Fristen Phase 3 Slice 9 follow-up A — drop the legacy
|
|
-- Pipeline-C source tables (paliad.event_deadlines +
|
|
-- paliad.event_deadline_rule_codes) and the read-only trigger from
|
|
-- mig 086, now that EventDeadlineService.Calculate has been rewritten
|
|
-- to read from paliad.deadline_rules.
|
|
--
|
|
-- Lorenz's Slice 9 (t-paliad-195) deferred this drop because the
|
|
-- legacy service still SELECTed event_deadlines.duration_value /
|
|
-- duration_unit / timing / notes / alt_* / combine_op. Slice 9
|
|
-- follow-up A refactors the service onto deadline_rules (the unified
|
|
-- source-of-truth since Slice 3 / mig 085) and frees us to remove the
|
|
-- old tables.
|
|
--
|
|
-- Sequencing — every step in this single migration is required for the
|
|
-- drop to be safe:
|
|
--
|
|
-- 1. Snapshot both source tables into paliad.event_deadlines_pre_092
|
|
-- + paliad.event_deadline_rule_codes_pre_092 (CREATE TABLE IF NOT
|
|
-- EXISTS — idempotent re-run). The snapshots persist after the
|
|
-- drop as audit anchors; the down migration restores from them.
|
|
-- 2. ADD COLUMN rule_codes text[] to paliad.deadline_rules and
|
|
-- backfill from paliad.event_deadline_rule_codes. Pipeline-C
|
|
-- deadlines carry multi-code rules (e.g. R.198 / R.213 carry
|
|
-- [RoP.029.a, RoP.030]) which don't fit deadline_rules.rule_code
|
|
-- (singular text); mig 085 left rule_code NULL on the 77
|
|
-- Pipeline-C rows. Without this backfill the drop would silently
|
|
-- lose 72 RoP citations.
|
|
-- 3. Hard assertion: every event_deadline_rule_codes row resolves to
|
|
-- a deadline_rules row via the sequence_order = 1000 +
|
|
-- event_deadlines.id convention from mig 085. If any row didn't
|
|
-- land, fail loudly before dropping the source.
|
|
-- 4. DROP TRIGGER + FUNCTION from mig 086 — orphan once the table is
|
|
-- gone.
|
|
-- 5. DROP TABLE paliad.event_deadline_rule_codes (FK side first).
|
|
-- 6. DROP TABLE paliad.event_deadlines.
|
|
-- 7. Final assertion: paliad.deadline_rules still carries >=77 active
|
|
-- rows with trigger_event_id IS NOT NULL (the Slice 3 corpus must
|
|
-- not have collapsed).
|
|
--
|
|
-- audit_reason wrapper at top — the mig 079 trigger on
|
|
-- paliad.deadline_rules logs every row-level edit. The ALTER TABLE +
|
|
-- UPDATE on rule_codes fires through that trigger, so the reason
|
|
-- persists in paliad.deadline_rule_audit for forever-grade audit.
|
|
|
|
SELECT set_config(
|
|
'paliad.audit_reason',
|
|
'mig 092: drop paliad.event_deadlines + event_deadline_rule_codes after backfilling rule_codes into deadline_rules (t-paliad-199, Slice 9 follow-up A, design §3.E)',
|
|
true);
|
|
|
|
-- =============================================================================
|
|
-- 1. Backup snapshots — full row copies so the down migration can
|
|
-- rebuild both tables byte-identically. CREATE TABLE IF NOT EXISTS
|
|
-- keeps the migration idempotent across reapplications; if the
|
|
-- snapshot already exists from a prior aborted run, we re-use it.
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS paliad.event_deadlines_pre_092 AS
|
|
SELECT *, now() AS snapshotted_at
|
|
FROM paliad.event_deadlines;
|
|
|
|
COMMENT ON TABLE paliad.event_deadlines_pre_092 IS
|
|
'Snapshot of paliad.event_deadlines before mig 092 dropped it. '
|
|
'Source-of-truth for the down migration; persists post-drop as the '
|
|
'permanent audit record of the 77 Pipeline-C source rows that '
|
|
'seeded paliad.deadline_rules via mig 085. Drop with a focused '
|
|
'follow-up after Slice 9 is verified in prod (pair with '
|
|
'paliad.deadline_rules_pre_091 cleanup).';
|
|
|
|
CREATE TABLE IF NOT EXISTS paliad.event_deadline_rule_codes_pre_092 AS
|
|
SELECT *, now() AS snapshotted_at
|
|
FROM paliad.event_deadline_rule_codes;
|
|
|
|
COMMENT ON TABLE paliad.event_deadline_rule_codes_pre_092 IS
|
|
'Snapshot of paliad.event_deadline_rule_codes before mig 092 dropped '
|
|
'it. Restored by the down migration; persists post-drop as the '
|
|
'permanent audit record of the legacy RoP citations attached to '
|
|
'Pipeline-C deadlines (72 rows across 70 of 77 deadlines).';
|
|
|
|
-- =============================================================================
|
|
-- 2. Add paliad.deadline_rules.rule_codes (text[]) and backfill it for
|
|
-- the 77 Pipeline-C rules. Mig 085 set rule_code = NULL on every
|
|
-- Pipeline-C row because deadline_rules.rule_code is singular and
|
|
-- Pipeline-C deadlines can carry multiple citations. rule_codes
|
|
-- holds the array form. Pipeline-A rules keep NULL here and continue
|
|
-- using rule_code; this column is a Pipeline-C-only field today.
|
|
-- =============================================================================
|
|
|
|
ALTER TABLE paliad.deadline_rules
|
|
ADD COLUMN IF NOT EXISTS rule_codes text[];
|
|
|
|
COMMENT ON COLUMN paliad.deadline_rules.rule_codes IS
|
|
'Array of legal-rule citations attached to this deadline, in '
|
|
'render order. Pipeline-C rules (event-rooted, trigger_event_id IS '
|
|
'NOT NULL) populate this column from the legacy '
|
|
'paliad.event_deadline_rule_codes junction (mig 092 backfill); '
|
|
'Pipeline-A rules use the singular rule_code column instead. NULL '
|
|
'on Pipeline-A rules + on the 7 Pipeline-C deadlines that had no '
|
|
'junction rows pre-mig.';
|
|
|
|
-- Aggregate junction rows into a text[] sorted by (sort_order,
|
|
-- rule_code) — matches the legacy ORDER BY contract that
|
|
-- EventDeadlineService.loadRuleCodes used.
|
|
--
|
|
-- Join key: the sequence_order = 1000 + event_deadlines.id convention
|
|
-- mig 085 anchored. Every active event_deadlines.id has a corresponding
|
|
-- deadline_rules row at sequence_order = 1000 + id; mig 085's hard
|
|
-- assertion guarantees that.
|
|
WITH agg AS (
|
|
SELECT event_deadline_id,
|
|
array_agg(rule_code ORDER BY sort_order, rule_code) AS codes
|
|
FROM paliad.event_deadline_rule_codes
|
|
GROUP BY event_deadline_id
|
|
)
|
|
UPDATE paliad.deadline_rules dr
|
|
SET rule_codes = agg.codes
|
|
FROM agg
|
|
WHERE dr.trigger_event_id IS NOT NULL
|
|
AND dr.sequence_order = 1000 + agg.event_deadline_id
|
|
AND dr.rule_codes IS DISTINCT FROM agg.codes;
|
|
|
|
-- =============================================================================
|
|
-- 3. Hard assertion: every junction row landed on a deadline_rules row.
|
|
-- Sums elements across all rule_codes arrays — if the count differs
|
|
-- from the source junction count, some event_deadline_id failed to
|
|
-- match any deadline_rules row (sequence_order convention broken).
|
|
-- Fail loudly here BEFORE dropping the source.
|
|
-- =============================================================================
|
|
|
|
DO $$
|
|
DECLARE
|
|
n_codes_src int;
|
|
n_codes_target int;
|
|
BEGIN
|
|
SELECT count(*) INTO n_codes_src
|
|
FROM paliad.event_deadline_rule_codes;
|
|
|
|
SELECT COALESCE(SUM(array_length(rule_codes, 1)), 0) INTO n_codes_target
|
|
FROM paliad.deadline_rules
|
|
WHERE rule_codes IS NOT NULL;
|
|
|
|
RAISE NOTICE 'mig 092: junction rows=%, backfilled rule_codes elements=%',
|
|
n_codes_src, n_codes_target;
|
|
|
|
IF n_codes_target < n_codes_src THEN
|
|
RAISE EXCEPTION 'mig 092: rule_codes backfill missed % junction rows '
|
|
'(source=%, target=%) — sequence_order = 1000 + ed.id '
|
|
'convention broken? Aborting before drop.',
|
|
n_codes_src - n_codes_target, n_codes_src, n_codes_target;
|
|
END IF;
|
|
END $$;
|
|
|
|
-- =============================================================================
|
|
-- 4. Drop the read-only trigger + function from mig 086. They're orphan
|
|
-- once paliad.event_deadlines goes away — explicit drop documents
|
|
-- that the wrapper's job is done, and keeps the symmetric reverse in
|
|
-- the down migration cleanly readable.
|
|
-- =============================================================================
|
|
|
|
DROP TRIGGER IF EXISTS event_deadlines_readonly ON paliad.event_deadlines;
|
|
DROP FUNCTION IF EXISTS paliad.event_deadlines_readonly_trigger();
|
|
|
|
-- =============================================================================
|
|
-- 5. Drop the legacy tables. Order: junction first (it has a FK to
|
|
-- event_deadlines), then the parent. Explicit ordering is clearer
|
|
-- than relying on CASCADE and mirrors the down migration's CREATE
|
|
-- sequence.
|
|
-- =============================================================================
|
|
|
|
DROP TABLE IF EXISTS paliad.event_deadline_rule_codes;
|
|
DROP TABLE IF EXISTS paliad.event_deadlines;
|
|
|
|
-- =============================================================================
|
|
-- 6. Final assertion: the unified Pipeline-C corpus is still intact.
|
|
-- Mig 085 moved 77 active rows; future hand-edited Pipeline-C rules
|
|
-- can only raise the count. A drop below 77 means the upstream
|
|
-- deadline_rules data was clobbered while this migration ran and
|
|
-- the deploy must abort.
|
|
-- =============================================================================
|
|
|
|
DO $$
|
|
DECLARE
|
|
n_unified int;
|
|
BEGIN
|
|
SELECT count(*) INTO n_unified
|
|
FROM paliad.deadline_rules
|
|
WHERE trigger_event_id IS NOT NULL AND is_active = true;
|
|
|
|
RAISE NOTICE 'mig 092: post-drop Pipeline-C rule count = %', n_unified;
|
|
|
|
IF n_unified < 77 THEN
|
|
RAISE EXCEPTION 'mig 092: Pipeline-C corpus collapsed — expected >=77 '
|
|
'active deadline_rules with trigger_event_id IS NOT NULL, got %',
|
|
n_unified;
|
|
END IF;
|
|
END $$;
|