112 lines
3.8 KiB
PL/PgSQL
112 lines
3.8 KiB
PL/PgSQL
-- Operator-only rollback; refuses to discard any retirement or withdrawn publication.
|
|
BEGIN;
|
|
DO $$
|
|
BEGIN
|
|
IF current_database() <> 'booking_test' THEN
|
|
RAISE EXCEPTION 'ARR daily lifecycle rollback is allowed only in booking_test';
|
|
END IF;
|
|
IF EXISTS (SELECT 1 FROM ingestion.daily_run_retirements)
|
|
OR EXISTS (SELECT 1 FROM reporting.monthly_runs WHERE report_status = 'withdrawn' OR withdrawn_at IS NOT NULL) THEN
|
|
RAISE EXCEPTION 'refusing rollback: daily retirement or withdrawn monthly audit exists';
|
|
END IF;
|
|
END $$;
|
|
DROP TRIGGER daily_run_retirements_immutable ON ingestion.daily_run_retirements;
|
|
DROP FUNCTION ingestion.protect_daily_run_retirement();
|
|
DROP TABLE ingestion.daily_run_retirements;
|
|
DROP INDEX reporting.monthly_runs_live_snapshot_unique;
|
|
ALTER TABLE reporting.monthly_runs
|
|
DROP CONSTRAINT monthly_runs_report_status_check,
|
|
DROP CONSTRAINT monthly_runs_status_shape,
|
|
DROP COLUMN withdrawn_at;
|
|
ALTER TABLE reporting.monthly_runs
|
|
ADD CONSTRAINT monthly_runs_report_status_check CHECK (report_status IN ('reserved', 'active', 'superseded', 'failed')),
|
|
ADD CONSTRAINT monthly_runs_snapshot_unique UNIQUE (period_start, source_snapshot_sha256),
|
|
ADD CONSTRAINT monthly_runs_status_shape CHECK (
|
|
(
|
|
report_status = 'reserved'
|
|
AND workbook_artifact_id IS NULL
|
|
AND result_artifact_id IS NULL
|
|
AND semantic_sha256 IS NULL
|
|
AND failure_code IS NULL
|
|
AND failure_message IS NULL
|
|
AND published_at IS NULL
|
|
AND superseded_at IS NULL
|
|
AND failed_at IS NULL
|
|
)
|
|
OR (
|
|
report_status = 'active'
|
|
AND workbook_artifact_id IS NOT NULL
|
|
AND result_artifact_id IS NOT NULL
|
|
AND semantic_sha256 IS NOT NULL
|
|
AND failure_code IS NULL
|
|
AND failure_message IS NULL
|
|
AND published_at IS NOT NULL
|
|
AND superseded_at IS NULL
|
|
AND failed_at IS NULL
|
|
)
|
|
OR (
|
|
report_status = 'superseded'
|
|
AND workbook_artifact_id IS NOT NULL
|
|
AND result_artifact_id IS NOT NULL
|
|
AND semantic_sha256 IS NOT NULL
|
|
AND failure_code IS NULL
|
|
AND failure_message IS NULL
|
|
AND published_at IS NOT NULL
|
|
AND superseded_at IS NOT NULL
|
|
AND failed_at IS NULL
|
|
)
|
|
OR (
|
|
report_status = 'failed'
|
|
AND workbook_artifact_id IS NULL
|
|
AND result_artifact_id IS NULL
|
|
AND semantic_sha256 IS NULL
|
|
AND failure_code IS NOT NULL
|
|
AND btrim(failure_code) <> ''
|
|
AND published_at IS NULL
|
|
AND superseded_at IS NULL
|
|
AND failed_at IS NOT NULL
|
|
)
|
|
);
|
|
CREATE OR REPLACE FUNCTION finance.validate_current_daily_version()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1
|
|
FROM finance.daily_versions AS version
|
|
WHERE version.id = NEW.daily_version_id
|
|
AND version.business_date = NEW.business_date
|
|
AND version.version_status = 'active'
|
|
) THEN
|
|
RAISE EXCEPTION
|
|
'current business date must reference an active daily version';
|
|
END IF;
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION reporting.protect_published_monthly_child()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
DECLARE
|
|
parent_id bigint;
|
|
BEGIN
|
|
parent_id := COALESCE(OLD.report_id, NEW.report_id);
|
|
IF EXISTS (
|
|
SELECT 1
|
|
FROM reporting.monthly_runs AS run
|
|
WHERE run.id = parent_id
|
|
AND run.report_status IN ('active', 'superseded')
|
|
) THEN
|
|
RAISE EXCEPTION
|
|
'published monthly report lineage and manifest are immutable';
|
|
END IF;
|
|
RETURN COALESCE(NEW, OLD);
|
|
END;
|
|
$$;
|
|
|
|
|
|
COMMIT;
|