"""labels: a tag's name is unique among LIVE tags only Revision ID: 0040 Revises: 0039 Create Date: 2026-10-08 Deleting a tag now leaves a tombstone row (`purged_at` set) so the change feed can tell linked devices about it (#5382). Before, the web deleted the row outright and devices never heard. A tombstone must not hold its name: creating `#grocery` again after deleting it has to make a live tag, on the web and from a device alike. So `(owner_id, name)` stays unique only where `purged_at IS NULL`. ## Downgrade Restores the plain unique constraint. Tombstones are deleted first, since one may share its name with a live tag; devices that have not pulled since keep the tag. """ import sqlalchemy as sa from alembic import op revision = "0040" down_revision = "0039" branch_labels = None depends_on = None def upgrade() -> None: op.drop_constraint("uq_labels_owner_name", "labels", type_="unique") op.create_index( "uq_labels_owner_name_live", "labels", ["owner_id", "name"], unique=True, postgresql_where=sa.text("purged_at IS NULL"), ) def downgrade() -> None: op.drop_index("uq_labels_owner_name_live", table_name="labels") op.execute("DELETE FROM labels WHERE purged_at IS NOT NULL") op.create_unique_constraint("uq_labels_owner_name", "labels", ["owner_id", "name"])