"""Reconcile the database with what the models have always claimed (#3275). Milestone 328 discovered ~130 places where the ORM models and the deployed schema disagreed. Almost all of them were the MODEL being wrong — missing `server_default`s, indexes and CHECK constraints that only ever existed in a migration — and those are fixed in the model files with no DDL at all, because the database already had them. This migration carries the remainder: the one case where the MODEL was right and the database was missing something. `tag.fandom_id` is declared `index=True` on the model, but no migration ever created that index. Every autogenerate run since would have proposed adding it; nobody ran one, so the model and the database simply drifted apart and stayed that way. Deliberately NOT in this migration: anything about `image_record.sha256`. An earlier draft of this file claimed sha256 was not unique in the database and that duplicate rows were therefore possible. That was WRONG, and it was wrong because it was read off `op.create_index("ix_image_record_sha256", ...)` at 0001 line 151 without reading line 149 two lines above it: sa.UniqueConstraint("sha256", name="uq_image_record_sha256"), Uniqueness has been enforced since the initial schema. The database simply expresses it as a CONSTRAINT plus a separate non-unique lookup index, where the model expressed it as one `unique=True, index=True` column — the same guarantee built from different objects, which is why the two schemas did not line up. The model now declares the constraint and the plain index separately, so it describes what is actually there. No DDL is needed for it. Revision ID: 0088 Revises: 0087 Create Date: 2026-08-30 """ from typing import Sequence, Union from alembic import op revision: str = "0088" down_revision: Union[str, None] = "0087" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: # IF NOT EXISTS because the index is what the model already asks for: any # database built from metadata rather than from this chain will have it, # and this migration must be a no-op there rather than an error. op.execute("CREATE INDEX IF NOT EXISTS ix_tag_fandom_id ON tag (fandom_id)") def downgrade() -> None: op.execute("DROP INDEX IF EXISTS ix_tag_fandom_id")