"""image_provenance: from_attachment_id (which archive an image was extracted from) Milestone #87. When an image is pulled out of a .zip/.rar, record WHICH archive PostAttachment it came from, so the provenance UI can show the single archive a file lives inside instead of every attachment on the post. Nullable FK with ON DELETE SET NULL — a loose (non-archive) download leaves it NULL, and deleting the archive attachment forgets the linkage without destroying the (image, post) provenance edge. Existing rows are NULL until the reextract backfill stamps them. Revision ID: 0055 Revises: 0054 Create Date: 2026-06-22 """ from typing import Sequence, Union import sqlalchemy as sa from alembic import op revision: str = "0055" down_revision: Union[str, None] = "0054" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: op.add_column( "image_provenance", sa.Column("from_attachment_id", sa.Integer(), nullable=True), ) op.create_index( "ix_image_provenance_from_attachment_id", "image_provenance", ["from_attachment_id"], ) op.create_foreign_key( "fk_image_provenance_from_attachment", "image_provenance", "post_attachment", ["from_attachment_id"], ["id"], ondelete="SET NULL", ) def downgrade() -> None: op.drop_constraint( "fk_image_provenance_from_attachment", "image_provenance", type_="foreignkey", ) op.drop_index( "ix_image_provenance_from_attachment_id", table_name="image_provenance", ) op.drop_column("image_provenance", "from_attachment_id")