Files
HartOMat/backend/alembic/versions/049_role_hierarchy.py
T
Hartmut da9014831a fix(migration049): use COMMIT/BEGIN instead of execution_options AUTOCOMMIT
asyncpg+alembic keeps a transaction open via begin_transaction(); calling
execution_options(isolation_level=AUTOCOMMIT) on an already-begun connection
raises InvalidRequestError. Replace with explicit COMMIT before ALTER TYPE
and BEGIN before the backfill UPDATE.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 20:26:57 +01:00

50 lines
1.9 KiB
Python

"""Extend UserRole enum: add global_admin and tenant_admin.
Existing 'admin' users are backfilled to 'global_admin'.
The legacy 'admin' value is kept in the enum so existing tokens/sessions
remain valid during the transition period. It will be removed in a later
cleanup migration once all call sites are updated.
Revision ID: 049
Revises: 048
"""
from alembic import op
import sqlalchemy as sa
revision = "049"
down_revision = "048"
branch_labels = None
depends_on = None
def upgrade() -> None:
# PostgreSQL requires ALTER TYPE ADD VALUE to be committed before the new value
# can be referenced in the same session. With asyncpg + alembic the connection
# already has an open transaction (begin_transaction), so we cannot change
# isolation level via execution_options. Instead we manually COMMIT the outer
# transaction, run the DDL, then open a new transaction for the DML backfill.
conn = op.get_bind()
# Commit the alembic-managed transaction so ALTER TYPE runs outside a tx block
conn.execute(sa.text("COMMIT"))
conn.execute(sa.text("ALTER TYPE userrole ADD VALUE IF NOT EXISTS 'global_admin'"))
conn.execute(sa.text("ALTER TYPE userrole ADD VALUE IF NOT EXISTS 'tenant_admin'"))
# Start a new transaction for the backfill DML
conn.execute(sa.text("BEGIN"))
conn.execute(
sa.text("UPDATE users SET role = 'global_admin'::userrole WHERE role = 'admin'::userrole")
)
def downgrade() -> None:
# Restore global_admin/tenant_admin back to admin (for rollback)
op.execute(
"UPDATE users SET role = 'admin'::userrole WHERE role = 'global_admin'::userrole"
)
op.execute(
"UPDATE users SET role = 'client'::userrole WHERE role = 'tenant_admin'::userrole"
)
# Note: cannot DROP enum values in PostgreSQL without recreating the type
# The values will remain in the enum but unused after downgrade