This repository has been archived on 2026-05-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
imagerepo/migrations/versions/b26011901_add_app_settings_expand_phash.py
T

133 lines
4.6 KiB
Python

"""Add app_settings table and expand perceptual_hash to 64 chars for 256-bit hashes
Revision ID: b26011901
Revises: a46e641430b8
Create Date: 2026-01-19
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'b26011901'
down_revision = 'a46e641430b8'
branch_labels = None
depends_on = None
def upgrade():
# Create app_settings table
op.create_table(
'app_settings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('key', sa.String(length=64), nullable=False),
sa.Column('value', sa.Text(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('key')
)
# Expand perceptual_hash column from 16 to 64 chars
bind = op.get_bind()
dialect = bind.dialect.name
if dialect == "sqlite":
# SQLite requires table recreation for column type changes.
# IMPORTANT: When recreating image_record, we must also recreate
# image_tags to preserve foreign key relationships.
# Step 1: Backup image_tags data
op.execute("CREATE TABLE image_tags_backup AS SELECT * FROM image_tags")
# Step 2: Drop image_tags (it references image_record)
op.drop_table("image_tags")
# Step 3: Recreate image_record with new column size
with op.batch_alter_table("image_record", recreate="always") as batch_op:
batch_op.alter_column(
'perceptual_hash',
existing_type=sa.String(length=16),
type_=sa.String(length=64),
existing_nullable=True
)
# Step 4: Recreate image_tags with proper FK constraints
op.create_table(
"image_tags",
sa.Column("image_id", sa.Integer(), nullable=False),
sa.Column("tag_id", sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint("image_id", "tag_id"),
sa.ForeignKeyConstraint(["image_id"], ["image_record.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["tag_id"], ["tag.id"], ondelete="CASCADE"),
sa.UniqueConstraint("image_id", "tag_id", name="uq_image_tag"),
)
# Step 5: Restore image_tags data
op.execute("INSERT INTO image_tags (image_id, tag_id) SELECT image_id, tag_id FROM image_tags_backup")
# Step 6: Drop backup table
op.drop_table("image_tags_backup")
else:
# PostgreSQL and others can alter directly
op.alter_column(
'image_record',
'perceptual_hash',
existing_type=sa.String(length=16),
type_=sa.String(length=64),
existing_nullable=True
)
def downgrade():
bind = op.get_bind()
dialect = bind.dialect.name
# Shrink perceptual_hash back to 16 chars (will truncate data!)
if dialect == "sqlite":
# Same approach as upgrade: backup image_tags, recreate image_record,
# then restore image_tags to preserve FK relationships
# Step 1: Backup image_tags data
op.execute("CREATE TABLE image_tags_backup AS SELECT * FROM image_tags")
# Step 2: Drop image_tags (it references image_record)
op.drop_table("image_tags")
# Step 3: Recreate image_record with old column size
with op.batch_alter_table("image_record", recreate="always") as batch_op:
batch_op.alter_column(
'perceptual_hash',
existing_type=sa.String(length=64),
type_=sa.String(length=16),
existing_nullable=True
)
# Step 4: Recreate image_tags with proper FK constraints
op.create_table(
"image_tags",
sa.Column("image_id", sa.Integer(), nullable=False),
sa.Column("tag_id", sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint("image_id", "tag_id"),
sa.ForeignKeyConstraint(["image_id"], ["image_record.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["tag_id"], ["tag.id"], ondelete="CASCADE"),
sa.UniqueConstraint("image_id", "tag_id", name="uq_image_tag"),
)
# Step 5: Restore image_tags data
op.execute("INSERT INTO image_tags (image_id, tag_id) SELECT image_id, tag_id FROM image_tags_backup")
# Step 6: Drop backup table
op.drop_table("image_tags_backup")
else:
op.alter_column(
'image_record',
'perceptual_hash',
existing_type=sa.String(length=64),
type_=sa.String(length=16),
existing_nullable=True
)
# Drop app_settings table
op.drop_table('app_settings')