"""Review-Funde (postgres-/sql-expert): fehlende FK-Indizes + partielle Unique- Constraints gegen Race Conditions (doppelte aktive Kontrolle/Mindermenge) Revision ID: 0007_indizes_und_unique_constraints Revises: 0006_add_eskalation_felder Create Date: 2026-09-04 Zwei unabhängige Fixes aus dem Agenten-Review dieser Session: 1. Partielle Unique-Indizes als DB-Sicherheitsnetz gegen die TOCTOU-Race zwischen Check und Insert in starte_kontrolle()/genehmigen() - Anwendungscode fängt die resultierende IntegrityError bereits ab (lifecycle.py, mindermenge.py). 2. Fehlende Indizes auf häufig gefilterten Fremdschlüsseln (Eskalations-Cron, Kontroll-Sperre-Check, Dashboard-Ablaufdatum-Abfrage u.a.). """ from typing import Sequence, Union from alembic import op revision: str = "0007_indizes_und_unique_constraints" down_revision: Union[str, None] = "0006_add_eskalation_felder" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: op.execute( """ CREATE UNIQUE INDEX idx_kontrolle_eine_aktive_je_objekt ON kontrolle (objekt_id) WHERE status = 'in_bearbeitung'; CREATE UNIQUE INDEX idx_mindermenge_eine_aktive_je_fehlbestand ON mindermengen_genehmigung (fehlbestand_id) WHERE status = 'aktiv'; CREATE INDEX idx_fehlbestand_material ON fehlbestand (material_id); CREATE INDEX idx_fehlbestand_status_entstanden ON fehlbestand (status, entstanden_am); CREATE INDEX idx_kontrolle_objekt_status ON kontrolle (objekt_id, status); CREATE INDEX idx_kontrollposition_kontrolle ON kontrollposition (kontrolle_id); CREATE INDEX idx_zustaendigkeit_objekt ON zustaendigkeit (objekt_id); CREATE INDEX idx_zustaendigkeit_standort ON zustaendigkeit (standort_id); CREATE INDEX idx_benutzer_rolle_rolle ON benutzer_rolle (rolle); CREATE INDEX idx_objektposition_material ON objektposition (material_id); CREATE INDEX idx_nachfuellung_fehlbestand ON nachfuellung (fehlbestand_id); CREATE INDEX idx_objektposition_ablauf ON objektposition (ablaufdatum) WHERE ablaufdatum IS NOT NULL; """ ) def downgrade() -> None: op.execute( """ DROP INDEX idx_objektposition_ablauf; DROP INDEX idx_nachfuellung_fehlbestand; DROP INDEX idx_objektposition_material; DROP INDEX idx_benutzer_rolle_rolle; DROP INDEX idx_zustaendigkeit_standort; DROP INDEX idx_zustaendigkeit_objekt; DROP INDEX idx_kontrollposition_kontrolle; DROP INDEX idx_kontrolle_objekt_status; DROP INDEX idx_fehlbestand_status_entstanden; DROP INDEX idx_fehlbestand_material; DROP INDEX idx_mindermenge_eine_aktive_je_fehlbestand; DROP INDEX idx_kontrolle_eine_aktive_je_objekt; """ )