fix: make migration 144 idempotent

This commit is contained in:
CREDO23 2026-06-01 20:11:25 +02:00
parent c44263e3d8
commit b2b70cfa3e

View file

@ -25,34 +25,60 @@ depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
# ENUM types (PostgreSQL requires types created before tables that use them)
# Guard every object so the migration is safe to re-run after a partial
# apply (the types/tables outlive a failed run that never advanced
# alembic_version). Types must precede the tables that reference them.
op.execute(
"""
CREATE TYPE automation_status AS ENUM (
'active', 'paused', 'archived'
);
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_type WHERE typname = 'automation_status'
) THEN
CREATE TYPE automation_status AS ENUM (
'active', 'paused', 'archived'
);
END IF;
END
$$;
"""
)
op.execute(
"""
CREATE TYPE automation_trigger_type AS ENUM (
'schedule', 'manual'
);
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_type WHERE typname = 'automation_trigger_type'
) THEN
CREATE TYPE automation_trigger_type AS ENUM (
'schedule', 'manual'
);
END IF;
END
$$;
"""
)
op.execute(
"""
CREATE TYPE automation_run_status AS ENUM (
'pending', 'running', 'succeeded', 'failed',
'cancelled', 'timed_out'
);
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_type WHERE typname = 'automation_run_status'
) THEN
CREATE TYPE automation_run_status AS ENUM (
'pending', 'running', 'succeeded', 'failed',
'cancelled', 'timed_out'
);
END IF;
END
$$;
"""
)
# automations — the editable, versioned automation definition
op.execute(
"""
CREATE TABLE automations (
CREATE TABLE IF NOT EXISTS automations (
id SERIAL PRIMARY KEY,
search_space_id INTEGER NOT NULL
REFERENCES searchspaces(id) ON DELETE CASCADE,
@ -69,19 +95,25 @@ def upgrade() -> None:
"""
)
op.execute(
"CREATE INDEX ix_automations_search_space_id ON automations(search_space_id);"
"CREATE INDEX IF NOT EXISTS ix_automations_search_space_id ON automations(search_space_id);"
)
op.execute(
"CREATE INDEX ix_automations_created_by_user_id ON automations(created_by_user_id);"
"CREATE INDEX IF NOT EXISTS ix_automations_created_by_user_id ON automations(created_by_user_id);"
)
op.execute(
"CREATE INDEX IF NOT EXISTS ix_automations_status ON automations(status);"
)
op.execute(
"CREATE INDEX IF NOT EXISTS ix_automations_created_at ON automations(created_at);"
)
op.execute(
"CREATE INDEX IF NOT EXISTS ix_automations_updated_at ON automations(updated_at);"
)
op.execute("CREATE INDEX ix_automations_status ON automations(status);")
op.execute("CREATE INDEX ix_automations_created_at ON automations(created_at);")
op.execute("CREATE INDEX ix_automations_updated_at ON automations(updated_at);")
# automation_triggers — one row per (automation, trigger-instance) pair
op.execute(
"""
CREATE TABLE automation_triggers (
CREATE TABLE IF NOT EXISTS automation_triggers (
id SERIAL PRIMARY KEY,
automation_id INTEGER NOT NULL
REFERENCES automations(id) ON DELETE CASCADE,
@ -96,20 +128,22 @@ def upgrade() -> None:
"""
)
op.execute(
"CREATE INDEX ix_automation_triggers_automation_id ON automation_triggers(automation_id);"
)
op.execute("CREATE INDEX ix_automation_triggers_type ON automation_triggers(type);")
op.execute(
"CREATE INDEX ix_automation_triggers_enabled ON automation_triggers(enabled);"
"CREATE INDEX IF NOT EXISTS ix_automation_triggers_automation_id ON automation_triggers(automation_id);"
)
op.execute(
"CREATE INDEX ix_automation_triggers_created_at ON automation_triggers(created_at);"
"CREATE INDEX IF NOT EXISTS ix_automation_triggers_type ON automation_triggers(type);"
)
op.execute(
"CREATE INDEX IF NOT EXISTS ix_automation_triggers_enabled ON automation_triggers(enabled);"
)
op.execute(
"CREATE INDEX IF NOT EXISTS ix_automation_triggers_created_at ON automation_triggers(created_at);"
)
# Partial index for the schedule tick: only enabled schedule triggers
# with a scheduled next fire are ever scanned for due rows.
op.execute(
"""
CREATE INDEX ix_automation_triggers_due
CREATE INDEX IF NOT EXISTS ix_automation_triggers_due
ON automation_triggers (next_fire_at)
WHERE enabled = true
AND type = 'schedule'
@ -120,7 +154,7 @@ def upgrade() -> None:
# automation_runs — the immutable per-fire execution record
op.execute(
"""
CREATE TABLE automation_runs (
CREATE TABLE IF NOT EXISTS automation_runs (
id SERIAL PRIMARY KEY,
automation_id INTEGER NOT NULL
REFERENCES automations(id) ON DELETE CASCADE,
@ -140,14 +174,16 @@ def upgrade() -> None:
"""
)
op.execute(
"CREATE INDEX ix_automation_runs_automation_id ON automation_runs(automation_id);"
"CREATE INDEX IF NOT EXISTS ix_automation_runs_automation_id ON automation_runs(automation_id);"
)
op.execute(
"CREATE INDEX ix_automation_runs_trigger_id ON automation_runs(trigger_id);"
"CREATE INDEX IF NOT EXISTS ix_automation_runs_trigger_id ON automation_runs(trigger_id);"
)
op.execute("CREATE INDEX ix_automation_runs_status ON automation_runs(status);")
op.execute(
"CREATE INDEX ix_automation_runs_created_at ON automation_runs(created_at);"
"CREATE INDEX IF NOT EXISTS ix_automation_runs_status ON automation_runs(status);"
)
op.execute(
"CREATE INDEX IF NOT EXISTS ix_automation_runs_created_at ON automation_runs(created_at);"
)