"""Merge legacy heads, add cashiers, prevent duplicate entries and prize places.""" from alembic import op import sqlalchemy as sa revision = "20260913_staff_concurrency" down_revision = ("20260701_perf_indexes", "41aae82e631b") branch_labels = None depends_on = None def upgrade(): connection = op.get_bind() # Stop safely on ambiguous historical results; never silently delete prizes. checks = ( ("participations", "lottery_id, account_number", "account_number IS NOT NULL"), ("participations", "lottery_id, user_id", "account_number IS NULL AND user_id IS NOT NULL"), ("winners", "lottery_id, place", "1=1"), ) for table, columns, predicate in checks: duplicate = connection.execute(sa.text( f"SELECT 1 FROM {table} WHERE {predicate} GROUP BY {columns} HAVING COUNT(*) > 1 LIMIT 1" )).first() if duplicate: raise RuntimeError(f"Duplicate {table} ({columns}); review duplicates before migrating. No data removed.") op.add_column("users", sa.Column("is_cashier", sa.Boolean(), server_default=sa.false(), nullable=False)) with op.batch_alter_table("participations") as batch: batch.create_unique_constraint("uq_participation_account", ["lottery_id", "account_number"]) op.create_index("uq_participation_user", "participations", ["lottery_id", "user_id"], unique=True, postgresql_where=sa.text("account_number IS NULL"), sqlite_where=sa.text("account_number IS NULL")) with op.batch_alter_table("winners") as batch: batch.create_unique_constraint("uq_winner_place", ["lottery_id", "place"]) def downgrade(): with op.batch_alter_table("winners") as batch: batch.drop_constraint("uq_winner_place", type_="unique") op.drop_index("uq_participation_user", table_name="participations") with op.batch_alter_table("participations") as batch: batch.drop_constraint("uq_participation_account", type_="unique") op.drop_column("users", "is_cashier")