From da9014831aceda59df60fc940c49f2892713b2b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Sun, 8 Mar 2026 20:26:57 +0100 Subject: [PATCH] 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 --- backend/alembic/versions/049_role_hierarchy.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/backend/alembic/versions/049_role_hierarchy.py b/backend/alembic/versions/049_role_hierarchy.py index 60f6b8d..30d399c 100644 --- a/backend/alembic/versions/049_role_hierarchy.py +++ b/backend/alembic/versions/049_role_hierarchy.py @@ -18,15 +18,20 @@ depends_on = None def upgrade() -> None: - # PostgreSQL requires ALTER TYPE ADD VALUE to be committed in its own transaction - # before the new value can be used. Execute each ADD VALUE with AUTOCOMMIT. + # 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() - with conn.execution_options(isolation_level="AUTOCOMMIT"): - 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'")) + # 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'")) - # Now in a normal transaction: backfill existing 'admin' → 'global_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") )