diff --git a/changes/12912.fix.md b/changes/12912.fix.md
new file mode 100644
index 00000000000..d15350ab8b2
--- /dev/null
+++ b/changes/12912.fix.md
@@ -0,0 +1 @@
+Fix the session result enum downgrade aborting when the column default cannot be cast to the recreated type
diff --git a/src/ai/backend/manager/models/alembic/versions/0b1efbb2db84_fix_sessionresult_enum_type_coexistence.py b/src/ai/backend/manager/models/alembic/versions/0b1efbb2db84_fix_sessionresult_enum_type_coexistence.py
index c2572b453ab..c9840e85280 100644
--- a/src/ai/backend/manager/models/alembic/versions/0b1efbb2db84_fix_sessionresult_enum_type_coexistence.py
+++ b/src/ai/backend/manager/models/alembic/versions/0b1efbb2db84_fix_sessionresult_enum_type_coexistence.py
@@ -41,6 +41,23 @@ def _is_expected_divergence(exc: BaseException) -> bool:
return _sqlstate_of(exc) in _EXPECTED_DIVERGENCE_SQLSTATES
+def _retype_result_column(conn: sa.engine.Connection, table: str, target_type: str) -> None:
+ """Change ``
.result`` to ``target_type``, carrying its default across.
+
+ PostgreSQL cannot cast an existing column default between two unrelated enum
+ types, so ALTER COLUMN ... TYPE fails outright while a default is attached.
+ Drop it, retype, then put it back -- the same order dec0deba5893 uses.
+ """
+ conn.exec_driver_sql(f"ALTER TABLE {table} ALTER COLUMN result DROP DEFAULT")
+ conn.exec_driver_sql(
+ f"ALTER TABLE {table} ALTER COLUMN result"
+ f" TYPE {target_type} USING result::text::{target_type}"
+ )
+ conn.exec_driver_sql(
+ f"ALTER TABLE {table} ALTER COLUMN result SET DEFAULT 'UNDEFINED'::{target_type}"
+ )
+
+
def upgrade() -> None:
conn = op.get_bind()
@@ -62,10 +79,7 @@ def upgrade() -> None:
# - "sessionresults" was created by b6b884fbae1f (2022, sessions.result)
# - ffcf0ed13a26 skipped rename because both existed
# Fix: alter sessions.result to use the singular type, then drop plural.
- conn.exec_driver_sql(
- "ALTER TABLE sessions ALTER COLUMN result"
- " TYPE sessionresult USING result::text::sessionresult"
- )
+ _retype_result_column(conn, "sessions", "sessionresult")
conn.exec_driver_sql("DROP TYPE sessionresults")
elif has_plural and not has_singular:
# Only plural exists (ffcf0ed13a26 was never applied, or was skipped).
@@ -102,10 +116,7 @@ def downgrade() -> None:
conn.exec_driver_sql(
"CREATE TYPE sessionresults AS ENUM ('UNDEFINED', 'SUCCESS', 'FAILURE')"
)
- conn.exec_driver_sql(
- "ALTER TABLE sessions ALTER COLUMN result"
- " TYPE sessionresults USING result::text::sessionresults"
- )
+ _retype_result_column(conn, "sessions", "sessionresults")
except sa.exc.DBAPIError as e:
if not _is_expected_divergence(e):
raise