Files
MemoryBear/api/migrations/versions/20a742ef1d93_202512051405.py
2025-12-15 13:54:31 +08:00

225 lines
8.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""202512051405
Revision ID: 20a742ef1d93
Revises: fc9664b8e4a1
Create Date: 2025-12-05 14:24:33.545865
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = '20a742ef1d93'
down_revision: Union[str, None] = 'fc9664b8e4a1'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def table_exists(table_name: str) -> bool:
"""检查表是否存在"""
bind = op.get_bind()
inspector = inspect(bind)
return table_name in inspector.get_table_names()
def column_exists(table_name: str, column_name: str) -> bool:
"""检查列是否存在"""
bind = op.get_bind()
inspector = inspect(bind)
if not table_exists(table_name):
return False
columns = [col['name'] for col in inspector.get_columns(table_name)]
return column_name in columns
def index_exists(table_name: str, index_name: str) -> bool:
"""检查索引是否存在"""
bind = op.get_bind()
inspector = inspect(bind)
if not table_exists(table_name):
return False
indexes = [idx['name'] for idx in inspector.get_indexes(table_name)]
return index_name in indexes
def constraint_exists(table_name: str, constraint_name: str) -> bool:
"""检查约束是否存在(外键、唯一约束等)"""
bind = op.get_bind()
inspector = inspect(bind)
if not table_exists(table_name):
return False
# 检查外键约束
foreign_keys = [fk['name'] for fk in inspector.get_foreign_keys(table_name) if fk['name']]
if constraint_name in foreign_keys:
return True
# 检查唯一约束
unique_constraints = [uc['name'] for uc in inspector.get_unique_constraints(table_name) if uc['name']]
if constraint_name in unique_constraints:
return True
# 检查检查约束
check_constraints = [cc['name'] for cc in inspector.get_check_constraints(table_name) if cc['name']]
if constraint_name in check_constraints:
return True
return False
def trigger_exists(trigger_name: str) -> bool:
"""检查触发器是否存在PostgreSQL"""
bind = op.get_bind()
result = bind.execute(sa.text(
"SELECT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = :trigger_name)"
), {"trigger_name": trigger_name})
return result.scalar()
def sequence_exists(sequence_name: str) -> bool:
"""检查序列是否存在PostgreSQL"""
bind = op.get_bind()
result = bind.execute(sa.text(
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relkind = 'S' AND relname = :sequence_name)"
), {"sequence_name": sequence_name})
return result.scalar()
def enum_exists(enum_name: str) -> bool:
"""检查枚举类型是否存在PostgreSQL"""
bind = op.get_bind()
result = bind.execute(sa.text(
"SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"
), {"enum_name": enum_name})
return result.scalar()
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('data_config', schema=None) as batch_op:
batch_op.alter_column('llm',
existing_type=sa.VARCHAR(length=255),
comment='LLM模型配置ID',
existing_comment='LS',
existing_nullable=True)
with op.batch_alter_table('end_users', schema=None) as batch_op:
batch_op.alter_column('id',
existing_type=sa.UUID(),
server_default=None,
existing_nullable=False)
with op.batch_alter_table('knowledges', schema=None) as batch_op:
batch_op.alter_column('permission_id',
existing_type=sa.VARCHAR(),
comment='permission ID:Private|Share|Memory',
existing_comment='permission ID:Private|Share',
existing_nullable=True)
with op.batch_alter_table('memory_increments', schema=None) as batch_op:
batch_op.alter_column('id',
existing_type=sa.UUID(),
server_default=None,
existing_nullable=False)
batch_op.alter_column('created_at',
existing_type=postgresql.TIMESTAMP(precision=0),
server_default=None,
existing_nullable=True)
batch_op.alter_column('updated_at',
existing_type=postgresql.TIMESTAMP(precision=0),
server_default=None,
existing_nullable=True)
# 为 model_configs 添加 tenant_id 列和相关约束
if table_exists('model_configs'):
# 添加列(分步骤处理 NOT NULL 约束)
if not column_exists('model_configs', 'tenant_id'):
# 步骤1: 先添加可空列
with op.batch_alter_table('model_configs', schema=None) as batch_op:
batch_op.add_column(sa.Column('tenant_id', sa.UUID(), nullable=True, comment='租户ID'))
# 步骤2: 为现有数据填充默认值
# 注意这里需要根据实际业务逻辑填充这里假设获取第一个租户ID
bind = op.get_bind()
result = bind.execute(sa.text("SELECT id FROM tenants LIMIT 1"))
first_tenant = result.scalar()
if first_tenant:
# 更新现有记录
bind.execute(
sa.text("UPDATE model_configs SET tenant_id = :tenant_id WHERE tenant_id IS NULL"),
{"tenant_id": first_tenant}
)
# 步骤3: 将列改为 NOT NULL
with op.batch_alter_table('model_configs', schema=None) as batch_op:
batch_op.alter_column('tenant_id', nullable=False)
# 创建索引
if not index_exists('model_configs', 'ix_model_configs_tenant_id'):
op.create_index('ix_model_configs_tenant_id', 'model_configs', ['tenant_id'], unique=False)
# 创建外键
if not constraint_exists('model_configs', 'model_configs_tenant_id_fkey'):
op.create_foreign_key('model_configs_tenant_id_fkey', 'model_configs', 'tenants', ['tenant_id'], ['id'])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# 回滚 model_configs 的修改
if table_exists('model_configs'):
# 删除外键
if constraint_exists('model_configs', 'model_configs_tenant_id_fkey'):
op.drop_constraint('model_configs_tenant_id_fkey', 'model_configs', type_='foreignkey')
# 删除索引
if index_exists('model_configs', 'ix_model_configs_tenant_id'):
op.drop_index('ix_model_configs_tenant_id', table_name='model_configs')
# 删除列
if column_exists('model_configs', 'tenant_id'):
with op.batch_alter_table('model_configs', schema=None) as batch_op:
batch_op.drop_column('tenant_id')
with op.batch_alter_table('memory_increments', schema=None) as batch_op:
batch_op.alter_column('updated_at',
existing_type=postgresql.TIMESTAMP(precision=0),
server_default=sa.text('CURRENT_TIMESTAMP'),
existing_nullable=True)
batch_op.alter_column('created_at',
existing_type=postgresql.TIMESTAMP(precision=0),
server_default=sa.text('CURRENT_TIMESTAMP'),
existing_nullable=True)
batch_op.alter_column('id',
existing_type=sa.UUID(),
server_default=sa.text('gen_random_uuid()'),
existing_nullable=False)
with op.batch_alter_table('knowledges', schema=None) as batch_op:
batch_op.alter_column('permission_id',
existing_type=sa.VARCHAR(),
comment='permission ID:Private|Share',
existing_comment='permission ID:Private|Share|Memory',
existing_nullable=True)
with op.batch_alter_table('end_users', schema=None) as batch_op:
batch_op.alter_column('id',
existing_type=sa.UUID(),
server_default=sa.text('gen_random_uuid()'),
existing_nullable=False)
with op.batch_alter_table('data_config', schema=None) as batch_op:
batch_op.alter_column('llm',
existing_type=sa.VARCHAR(length=255),
comment='LS',
existing_comment='LLM模型配置ID',
existing_nullable=True)
# ### end Alembic commands ###