| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- """Add generic deterministic quality profiling, findings and SLA events."""
- from alembic import op
- revision = "20260731_400"
- down_revision = "20260731_390"
- branch_labels = None
- depends_on = None
- def upgrade() -> None:
- op.execute(
- """
- CREATE TABLE public.quality_templates (
- uid UUID PRIMARY KEY,
- code VARCHAR(120) NOT NULL UNIQUE,
- name VARCHAR(300) NOT NULL,
- owner_uid UUID NOT NULL REFERENCES public.users(id),
- status VARCHAR(20) NOT NULL CHECK (
- status IN ('draft','published','retired')
- ),
- current_version INTEGER NOT NULL CHECK (current_version > 0),
- active_version_uid UUID,
- created_by UUID NOT NULL REFERENCES public.users(id),
- created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
- updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
- );
- CREATE TABLE public.quality_template_versions (
- uid UUID PRIMARY KEY,
- template_uid UUID NOT NULL
- REFERENCES public.quality_templates(uid),
- version INTEGER NOT NULL CHECK (version > 0),
- status VARCHAR(20) NOT NULL CHECK (
- status IN ('draft','published','superseded')
- ),
- definition JSONB NOT NULL,
- content_hash CHAR(64) NOT NULL,
- created_by UUID NOT NULL REFERENCES public.users(id),
- created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
- published_by UUID REFERENCES public.users(id),
- published_at TIMESTAMPTZ,
- UNIQUE (template_uid, version),
- UNIQUE (template_uid, content_hash),
- CHECK (jsonb_typeof(definition) = 'object')
- );
- ALTER TABLE public.quality_templates
- ADD CONSTRAINT fk_quality_template_active_version
- FOREIGN KEY (active_version_uid)
- REFERENCES public.quality_template_versions(uid);
- CREATE UNIQUE INDEX uq_quality_template_published_version
- ON public.quality_template_versions(template_uid)
- WHERE status = 'published';
- CREATE TABLE public.quality_profile_runs (
- uid UUID PRIMARY KEY,
- template_uid UUID NOT NULL
- REFERENCES public.quality_templates(uid),
- template_version_uid UUID NOT NULL
- REFERENCES public.quality_template_versions(uid),
- template_hash CHAR(64) NOT NULL,
- asset_uid UUID NOT NULL
- REFERENCES public.active_metadata_assets(uid),
- source_uid UUID NOT NULL
- REFERENCES public.ingestion_sources(uid),
- business_domain_uid VARCHAR(200),
- batch_key VARCHAR(160) NOT NULL,
- status VARCHAR(20) NOT NULL CHECK (status IN ('success','failed')),
- row_count INTEGER NOT NULL CHECK (row_count > 0),
- score NUMERIC(6,2) NOT NULL CHECK (
- score >= 0 AND score <= 100
- ),
- source_observed_at TIMESTAMPTZ NOT NULL,
- previous_run_uid UUID
- REFERENCES public.quality_profile_runs(uid),
- comparison JSONB NOT NULL,
- profile JSONB NOT NULL,
- field_bindings JSONB NOT NULL,
- finding_count INTEGER NOT NULL CHECK (finding_count >= 0),
- deterministic BOOLEAN NOT NULL DEFAULT TRUE CHECK (deterministic),
- created_by UUID NOT NULL REFERENCES public.users(id),
- created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
- UNIQUE (asset_uid, batch_key),
- CHECK (jsonb_typeof(comparison) = 'object'),
- CHECK (jsonb_typeof(profile) = 'object'),
- CHECK (jsonb_typeof(field_bindings) = 'object')
- );
- CREATE TABLE public.quality_profile_metrics (
- uid UUID PRIMARY KEY,
- run_uid UUID NOT NULL
- REFERENCES public.quality_profile_runs(uid) ON DELETE CASCADE,
- field_name VARCHAR(200) NOT NULL,
- metric_value JSONB NOT NULL,
- created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
- UNIQUE (run_uid, field_name),
- CHECK (jsonb_typeof(metric_value) = 'object')
- );
- CREATE TABLE public.quality_findings (
- uid UUID PRIMARY KEY,
- run_uid UUID NOT NULL
- REFERENCES public.quality_profile_runs(uid) ON DELETE CASCADE,
- asset_uid UUID NOT NULL
- REFERENCES public.active_metadata_assets(uid),
- finding_type VARCHAR(30) NOT NULL CHECK (
- finding_type IN (
- 'completeness','uniqueness','pattern','duplicate',
- 'outlier','volume','distribution','schema','freshness'
- )
- ),
- field_name VARCHAR(200),
- severity VARCHAR(20) NOT NULL CHECK (
- severity IN ('info','warning','error','critical')
- ),
- status VARCHAR(20) NOT NULL CHECK (
- status IN ('open','acknowledged','resolved')
- ),
- actual JSONB NOT NULL,
- expected JSONB NOT NULL,
- recurrence_key CHAR(64) NOT NULL,
- occurrence_number INTEGER NOT NULL CHECK (occurrence_number > 0),
- evidence JSONB NOT NULL,
- created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
- CHECK (jsonb_typeof(actual) IN ('object','number','string')),
- CHECK (jsonb_typeof(expected) IN ('object','number','string')),
- CHECK (jsonb_typeof(evidence) = 'object')
- );
- CREATE TABLE public.quality_sla_events (
- uid UUID PRIMARY KEY,
- run_uid UUID NOT NULL
- REFERENCES public.quality_profile_runs(uid) ON DELETE CASCADE,
- asset_uid UUID NOT NULL
- REFERENCES public.active_metadata_assets(uid),
- sla_type VARCHAR(30) NOT NULL CHECK (
- sla_type IN ('freshness','quality_score')
- ),
- status VARCHAR(20) NOT NULL CHECK (
- status IN ('met','violated','recovered')
- ),
- severity VARCHAR(20) NOT NULL CHECK (
- severity IN ('info','warning','error','critical')
- ),
- actual NUMERIC NOT NULL,
- threshold NUMERIC NOT NULL,
- owner_uid UUID NOT NULL REFERENCES public.users(id),
- escalation_level INTEGER NOT NULL CHECK (
- escalation_level BETWEEN 0 AND 3
- ),
- evidence JSONB NOT NULL,
- created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
- UNIQUE (run_uid, sla_type),
- CHECK (jsonb_typeof(evidence) = 'object')
- );
- CREATE INDEX idx_quality_runs_asset_created
- ON public.quality_profile_runs(asset_uid, created_at DESC);
- CREATE INDEX idx_quality_runs_domain_created
- ON public.quality_profile_runs(
- business_domain_uid, created_at DESC
- );
- CREATE INDEX idx_quality_findings_recurrence
- ON public.quality_findings(
- recurrence_key, occurrence_number DESC
- );
- CREATE INDEX idx_quality_findings_asset_status
- ON public.quality_findings(asset_uid, status, created_at DESC);
- CREATE INDEX idx_quality_sla_asset_status
- ON public.quality_sla_events(
- asset_uid, sla_type, status, created_at DESC
- );
- """
- )
- def downgrade() -> None:
- raise RuntimeError(
- "quality operation evidence is append-only; "
- "downgrade requires an approved archival migration"
- )
|