diff --git a/database/versions/5b3355c964bb_2_2_0.py b/database/versions/5b3355c964bb_2_2_0.py new file mode 100644 index 00000000..24842137 --- /dev/null +++ b/database/versions/5b3355c964bb_2_2_0.py @@ -0,0 +1,106 @@ +"""2.2.0 + +Revision ID: 5b3355c964bb +Revises: d58298a0879f +Create Date: 2025-08-19 12:27:08.451371 + +""" +import sqlalchemy as sa +from alembic import op + +from app.core.config import settings + +# revision identifiers, used by Alembic. +revision = '5b3355c964bb' +down_revision = 'd58298a0879f' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + if settings.DB_TYPE.lower() == "postgresql": + # 将SQLite的Sequence转换为PostgreSQL的Identity + fix_postgresql_sequences() + # ### end Alembic commands ### + + +def fix_postgresql_sequences(): + """ + 修复PostgreSQL数据库中的序列问题 + 将SQLite迁移过来的Sequence转换为PostgreSQL的Identity + """ + connection = op.get_bind() + + # 获取所有表名 + result = connection.execute(sa.text(""" + SELECT table_name + FROM information_schema.tables + WHERE table_schema = 'public' + AND table_type = 'BASE TABLE' + """)) + tables = [row[0] for row in result.fetchall()] + + print(f"发现 {len(tables)} 个表需要检查序列") + + for table_name in tables: + fix_table_sequence(connection, table_name) + + +def fix_table_sequence(connection, table_name): + """ + 修复单个表的序列 + """ + try: + # 检查表是否有id列 + result = connection.execute(sa.text(f""" + SELECT column_name, data_type, is_nullable, column_default + FROM information_schema.columns + WHERE table_name = '{table_name}' + AND column_name = 'id' + """)) + + id_column = result.fetchone() + if not id_column: + print(f"表 {table_name} 没有id列,跳过") + return + + _, _, _, column_default = id_column + + # 检查是否已经是Identity类型 + if column_default and 'GENERATED BY DEFAULT AS IDENTITY' in column_default: + print(f"表 {table_name} 的id列已经是Identity类型,跳过") + return + + # 检查是否有序列 + print(f"表 {table_name} 存在序列,需要修复") + convert_to_identity(connection, table_name) + + except Exception as e: + print(f"修复表 {table_name} 序列时出错: {e}") + + +def convert_to_identity(connection, table_name): + """ + 将序列转换为Identity,保持原有约束不变 + """ + try: + # 获取当前序列的最大值 + result = connection.execute(sa.text(f""" + SELECT COALESCE(MAX(id), 0) + 1 as next_value + FROM {table_name} + """)) + next_value = result.fetchone()[0] + + # 直接修改列属性,添加Identity,保持其他约束不变 + # 这种方式不会删除主键约束和索引 + connection.execute(sa.text(f""" + ALTER TABLE {table_name} + ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH {next_value}) + """)) + + print(f"表 {table_name} 序列已转换为Identity,起始值为 {next_value}") + + except Exception as e: + print(f"转换表 {table_name} 序列时出错: {e}") + raise diff --git a/database/versions/d58298a0879f_2_1_9.py b/database/versions/d58298a0879f_2_1_9.py index a4064c5d..dea8c887 100644 --- a/database/versions/d58298a0879f_2_1_9.py +++ b/database/versions/d58298a0879f_2_1_9.py @@ -5,12 +5,6 @@ Revises: 4666ce24a443 Create Date: 2025-08-19 11:56:39.652032 """ -import contextlib -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import sqlite - -from app.core.config import settings # revision identifiers, used by Alembic. revision = 'd58298a0879f' @@ -20,106 +14,8 @@ depends_on = None def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - if settings.DB_TYPE.lower() == "postgresql": - # PostgreSQL序列修复:将SQLite的Sequence转换为PostgreSQL的Identity - fix_postgresql_sequences() - # ### end Alembic commands ### + pass def downgrade() -> None: pass - - -def fix_postgresql_sequences(): - """ - 修复PostgreSQL数据库中的序列问题 - 将SQLite迁移过来的Sequence转换为PostgreSQL的Identity - """ - connection = op.get_bind() - - # 获取所有表名 - result = connection.execute(sa.text(""" - SELECT table_name - FROM information_schema.tables - WHERE table_schema = 'public' - AND table_type = 'BASE TABLE' - """)) - tables = [row[0] for row in result.fetchall()] - - print(f"发现 {len(tables)} 个表需要检查序列") - - for table_name in tables: - fix_table_sequence(connection, table_name) - - -def fix_table_sequence(connection, table_name): - """ - 修复单个表的序列 - """ - try: - # 检查表是否有id列 - result = connection.execute(sa.text(f""" - SELECT column_name, data_type, is_nullable, column_default - FROM information_schema.columns - WHERE table_name = '{table_name}' - AND column_name = 'id' - """)) - - id_column = result.fetchone() - if not id_column: - print(f"表 {table_name} 没有id列,跳过") - return - - column_name, data_type, is_nullable, column_default = id_column - - # 检查是否已经是Identity类型 - if column_default and 'GENERATED BY DEFAULT AS IDENTITY' in column_default: - print(f"表 {table_name} 的id列已经是Identity类型,跳过") - return - - # 检查是否有序列 - result = connection.execute(sa.text(f""" - SELECT sequence_name - FROM information_schema.sequences - WHERE sequence_name LIKE '{table_name}_id_seq' - """)) - - sequence_exists = result.fetchone() - - if sequence_exists: - print(f"表 {table_name} 存在序列,需要修复") - convert_to_identity(connection, table_name) - - except Exception as e: - print(f"修复表 {table_name} 序列时出错: {e}") - - -def convert_to_identity(connection, table_name): - """ - 将序列转换为Identity - """ - try: - # 获取当前序列的最大值 - result = connection.execute(sa.text(f""" - SELECT COALESCE(MAX(id), 0) + 1 as next_value - FROM {table_name} - """)) - next_value = result.fetchone()[0] - - # 删除旧的序列 - connection.execute(sa.text(f"DROP SEQUENCE IF EXISTS {table_name}_id_seq")) - - # 修改列定义,添加Identity - connection.execute(sa.text(f""" - ALTER TABLE {table_name} - ALTER COLUMN id SET DATA TYPE INTEGER, - ALTER COLUMN id SET DEFAULT NULL, - ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH {next_value}) - """)) - - print(f"表 {table_name} 序列已转换为Identity,起始值为 {next_value}") - - except Exception as e: - print(f"转换表 {table_name} 序列时出错: {e}") - raise