"""Fix guest_users email+tenant_id unique index. Alembic 0059 created ix_guest_users_email_tenant as a normal (non-unique) index. The SQLAlchemy model defines it as unique=True, and production already has a UNIQUE INDEX. This migration aligns Alembic with production. Before creating the unique index, checks for duplicate (email, tenant_id) pairs. If duplicates exist, the migration aborts with a data cleanup report. Revision ID: 0095 Revises: 0094 """ from __future__ import annotations from alembic import op import sqlalchemy as sa revision = "0095" down_revision = "0094" branch_labels = None depends_on = None def upgrade() -> None: # Check for duplicates before creating unique index conn = op.get_bind() duplicates = conn.execute( sa.text( "SELECT email, tenant_id, count(*) FROM guest_users " "GROUP BY email, tenant_id HAVING count(*) > 1" ) ).fetchall() if duplicates: raise RuntimeError( f"Cannot create unique index: {len(duplicates)} duplicate (email, tenant_id) pairs found. " "Data cleanup required before migration." ) # Drop the non-unique index and recreate as unique op.execute("DROP INDEX IF EXISTS ix_guest_users_email_tenant") op.execute( "CREATE UNIQUE INDEX IF NOT EXISTS ix_guest_users_email_tenant " "ON guest_users (email, tenant_id)" ) def downgrade() -> None: op.execute("DROP INDEX IF EXISTS ix_guest_users_email_tenant") op.execute( "CREATE INDEX IF NOT EXISTS ix_guest_users_email_tenant " "ON guest_users (email, tenant_id)" )