fix(migration): 0007 - ein op.execute() pro Statement (asyncpg-Limitierung)
asyncpg lehnt mehrere Kommandos in einem prepared statement ab ("cannot
insert multiple commands into a prepared statement") - beim echten Deploy-
Lauf gegen die Live-DB aufgefallen, sauber zurückgerollt (transaktionale DDL).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
@@ -11,6 +11,11 @@ Zwei unabhängige Fixes aus dem Agenten-Review dieser Session:
|
|||||||
fängt die resultierende IntegrityError bereits ab (lifecycle.py, mindermenge.py).
|
fängt die resultierende IntegrityError bereits ab (lifecycle.py, mindermenge.py).
|
||||||
2. Fehlende Indizes auf häufig gefilterten Fremdschlüsseln (Eskalations-Cron,
|
2. Fehlende Indizes auf häufig gefilterten Fremdschlüsseln (Eskalations-Cron,
|
||||||
Kontroll-Sperre-Check, Dashboard-Ablaufdatum-Abfrage u.a.).
|
Kontroll-Sperre-Check, Dashboard-Ablaufdatum-Abfrage u.a.).
|
||||||
|
|
||||||
|
Ein op.execute() pro Statement (nicht mehrere per Semikolon gebündelt) - der
|
||||||
|
asyncpg-Treiber lehnt mehrere Kommandos in einem prepared statement ab
|
||||||
|
("cannot insert multiple commands into a prepared statement", Fund beim
|
||||||
|
echten Deploy-Lauf gegen die Live-DB).
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from typing import Sequence, Union
|
||||||
|
|
||||||
@@ -21,43 +26,44 @@ down_revision: Union[str, None] = "0006_add_eskalation_felder"
|
|||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
_UPGRADE_STATEMENTS = [
|
||||||
|
"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",
|
||||||
|
]
|
||||||
|
|
||||||
|
_INDEX_NAMEN_ABWAERTS = [
|
||||||
|
"idx_objektposition_ablauf",
|
||||||
|
"idx_nachfuellung_fehlbestand",
|
||||||
|
"idx_objektposition_material",
|
||||||
|
"idx_benutzer_rolle_rolle",
|
||||||
|
"idx_zustaendigkeit_standort",
|
||||||
|
"idx_zustaendigkeit_objekt",
|
||||||
|
"idx_kontrollposition_kontrolle",
|
||||||
|
"idx_kontrolle_objekt_status",
|
||||||
|
"idx_fehlbestand_status_entstanden",
|
||||||
|
"idx_fehlbestand_material",
|
||||||
|
"idx_mindermenge_eine_aktive_je_fehlbestand",
|
||||||
|
"idx_kontrolle_eine_aktive_je_objekt",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
op.execute(
|
for statement in _UPGRADE_STATEMENTS:
|
||||||
"""
|
op.execute(statement)
|
||||||
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:
|
def downgrade() -> None:
|
||||||
op.execute(
|
for name in _INDEX_NAMEN_ABWAERTS:
|
||||||
"""
|
op.execute(f"DROP INDEX {name}")
|
||||||
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;
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user