fix(migrations): guard f0e9d8c7b6a5 per-table for create_all()-only tables
dataset_review_sessions (and potentially other tables in FK_DEFS) are created at runtime by init_db() → Base.metadata.create_all(), not by Alembic migrations. On fresh databases, these tables don't exist when migrations run, causing ALTER TABLE to crash. Fix: check table existence before operating on each FK. If a table doesn't exist, create_all() will create it with the correct FK definition (the model already has ondelete='CASCADE'). This is the same pattern used by other migrations (9f8e7d6c5b4a, ed28d34edde7, c9d8e7f6a5b4, 86c7b1d6a710) for create_all()-only tables like llm_providers, roles, validation_policies, llm_validation_results.
This commit is contained in:
@@ -1,17 +1,28 @@
|
||||
"""cascade ondelete for all remaining environments FK references
|
||||
|
||||
Revision ID: f0e9d8c7b6a5
|
||||
Revises: e1f2a3b4c5d6, e5f4d3c2b1a
|
||||
Revises: a5b6c7d8e9f0, e5f4d3c2b1a
|
||||
Create Date: 2026-05-21 19:00:00.000000
|
||||
|
||||
@RATIONALE The dataset_review_sessions table is created at runtime by
|
||||
init_db() → Base.metadata.create_all(), not by any Alembic migration.
|
||||
On a fresh database, it does not exist when Alembic runs (entrypoint
|
||||
runs alembic upgrade head BEFORE the backend starts). The model already
|
||||
defines the FK with ondelete='CASCADE', so on fresh databases the FK is
|
||||
correct. This migration only needs to alter the FK on databases that
|
||||
were upgraded from before the FK had CASCADE.
|
||||
|
||||
Guard: skip dataset_review_sessions FK if the table doesn't exist.
|
||||
"""
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
from sqlalchemy import inspect
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'f0e9d8c7b6a5'
|
||||
down_revision: str | Sequence[str] | None = ('e1f2a3b4c5d6', 'e5f4d3c2b1a')
|
||||
down_revision: str | Sequence[str] | None = ('a5b6c7d8e9f0', 'e5f4d3c2b1a')
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
@@ -25,9 +36,18 @@ FK_DEFS = [
|
||||
]
|
||||
|
||||
|
||||
def _table_exists(table: str) -> bool:
|
||||
"""Check if a table exists in the current database connection."""
|
||||
conn = op.get_bind()
|
||||
inspector = inspect(conn)
|
||||
return inspector.has_table(table)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
for table, constraint_name, column in FK_DEFS:
|
||||
if not _table_exists(table):
|
||||
continue
|
||||
op.drop_constraint(constraint_name, table, type_='foreignkey')
|
||||
op.create_foreign_key(
|
||||
constraint_name,
|
||||
@@ -42,6 +62,8 @@ def upgrade() -> None:
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
for table, constraint_name, column in reversed(FK_DEFS):
|
||||
if not _table_exists(table):
|
||||
continue
|
||||
op.drop_constraint(constraint_name, table, type_='foreignkey')
|
||||
op.create_foreign_key(
|
||||
constraint_name,
|
||||
|
||||
Reference in New Issue
Block a user