From cb1dd9f17df8d2bae147766fcda0c1f156d0b0e4 Mon Sep 17 00:00:00 2001 From: DDSRem <73049927+DDSRem@users.noreply.github.com> Date: Sat, 23 Aug 2025 18:12:13 +0800 Subject: [PATCH 1/2] fix(database): upgrade error in pg database Co-Authored-By: Aqr-K <95741669+Aqr-K@users.noreply.github.com> --- database/versions/0fb94bf69b38_2_0_2.py | 6 ++- database/versions/4666ce24a443_2_1_8.py | 15 +++--- database/versions/4b544f5d3b07_2_1_3.py | 21 +++++++-- database/versions/55390f1f77c1_2_0_9.py | 8 ++-- database/versions/610bb05ddeef_2_1_2.py | 7 +-- database/versions/bf28a012734c_2_0_8.py | 8 ++-- database/versions/ca5461f314f2_2_1_0.py | 18 +++++-- database/versions/e2dbe1421fa4_2_0_3.py | 62 ++++++++++++++++++------- database/versions/eaf9cbc49027_2_0_7.py | 15 ++++-- database/versions/ecf3c693fdf3_2_0_5.py | 40 ++++++++++++---- 10 files changed, 143 insertions(+), 57 deletions(-) diff --git a/database/versions/0fb94bf69b38_2_0_2.py b/database/versions/0fb94bf69b38_2_0_2.py index d2023acc..2752f5a1 100644 --- a/database/versions/0fb94bf69b38_2_0_2.py +++ b/database/versions/0fb94bf69b38_2_0_2.py @@ -21,7 +21,11 @@ depends_on = None def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### # 站点数据统计增加站点名称 - with contextlib.suppress(Exception): + conn = op.get_bind() + inspector = sa.inspect(conn) + columns = inspector.get_columns('siteuserdata') + # 检查 'name' 字段是否已存在 + if not any(c['name'] == 'name' for c in columns): op.add_column('siteuserdata', sa.Column('name', sa.String(), nullable=True)) # ### end Alembic commands ### diff --git a/database/versions/4666ce24a443_2_1_8.py b/database/versions/4666ce24a443_2_1_8.py index dfdfef24..ee9c8d76 100644 --- a/database/versions/4666ce24a443_2_1_8.py +++ b/database/versions/4666ce24a443_2_1_8.py @@ -18,19 +18,18 @@ depends_on = None def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - with contextlib.suppress(Exception): - # 添加触发类型字段 + conn = op.get_bind() + inspector = sa.inspect(conn) + columns = inspector.get_columns('workflow') + + if not any(c['name'] == 'trigger_type' for c in columns): op.add_column('workflow', sa.Column('trigger_type', sa.String(), nullable=True, default='timer')) - with contextlib.suppress(Exception): - # 添加事件类型字段 + if not any(c['name'] == 'event_type' for c in columns): op.add_column('workflow', sa.Column('event_type', sa.String(), nullable=True)) - with contextlib.suppress(Exception): - # 添加事件条件字段 + if not any(c['name'] == 'event_conditions' for c in columns): op.add_column('workflow', sa.Column('event_conditions', sa.JSON(), nullable=True, default={})) - # ### end Alembic commands ### def downgrade() -> None: diff --git a/database/versions/4b544f5d3b07_2_1_3.py b/database/versions/4b544f5d3b07_2_1_3.py index 9583caa1..1dcee814 100644 --- a/database/versions/4b544f5d3b07_2_1_3.py +++ b/database/versions/4b544f5d3b07_2_1_3.py @@ -19,13 +19,28 @@ depends_on = None def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - with contextlib.suppress(Exception): + conn = op.get_bind() + inspector = sa.inspect(conn) + + # 检查并添加 downloadhistory.episode_group + dh_columns = inspector.get_columns('downloadhistory') + if not any(c['name'] == 'episode_group' for c in dh_columns): op.add_column('downloadhistory', sa.Column('episode_group', sa.String, nullable=True)) + + # 检查并添加 subscribe.episode_group + s_columns = inspector.get_columns('subscribe') + if not any(c['name'] == 'episode_group' for c in s_columns): op.add_column('subscribe', sa.Column('episode_group', sa.String, nullable=True)) + + # 检查并添加 subscribehistory.episode_group + sh_columns = inspector.get_columns('subscribehistory') + if not any(c['name'] == 'episode_group' for c in sh_columns): op.add_column('subscribehistory', sa.Column('episode_group', sa.String, nullable=True)) + + # 检查并添加 transferhistory.episode_group + th_columns = inspector.get_columns('transferhistory') + if not any(c['name'] == 'episode_group' for c in th_columns): op.add_column('transferhistory', sa.Column('episode_group', sa.String, nullable=True)) - # ### end Alembic commands ### def downgrade() -> None: diff --git a/database/versions/55390f1f77c1_2_0_9.py b/database/versions/55390f1f77c1_2_0_9.py index 890c3538..e199212a 100644 --- a/database/versions/55390f1f77c1_2_0_9.py +++ b/database/versions/55390f1f77c1_2_0_9.py @@ -18,11 +18,11 @@ depends_on = None def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - # 整理历史记录 增加下载器字段 - with contextlib.suppress(Exception): + conn = op.get_bind() + inspector = sa.inspect(conn) + columns = inspector.get_columns('transferhistory') + if not any(c['name'] == 'downloader' for c in columns): op.add_column('transferhistory', sa.Column('downloader', sa.String(), nullable=True)) - # ### end Alembic commands ### def downgrade() -> None: diff --git a/database/versions/610bb05ddeef_2_1_2.py b/database/versions/610bb05ddeef_2_1_2.py index a38338b9..60947d3d 100644 --- a/database/versions/610bb05ddeef_2_1_2.py +++ b/database/versions/610bb05ddeef_2_1_2.py @@ -19,10 +19,11 @@ depends_on = None def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - with contextlib.suppress(Exception): + conn = op.get_bind() + inspector = sa.inspect(conn) + columns = inspector.get_columns('workflow') + if not any(c['name'] == 'flows' for c in columns): op.add_column('workflow', sa.Column('flows', sa.JSON(), nullable=True)) - # ### end Alembic commands ### def downgrade() -> None: diff --git a/database/versions/bf28a012734c_2_0_8.py b/database/versions/bf28a012734c_2_0_8.py index d9711d7a..212aa10a 100644 --- a/database/versions/bf28a012734c_2_0_8.py +++ b/database/versions/bf28a012734c_2_0_8.py @@ -18,11 +18,11 @@ depends_on = None def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - # 下载历史记录 增加下载器字段 - with contextlib.suppress(Exception): + conn = op.get_bind() + inspector = sa.inspect(conn) + columns = inspector.get_columns('downloadhistory') + if not any(c['name'] == 'downloader' for c in columns): op.add_column('downloadhistory', sa.Column('downloader', sa.String(), nullable=True)) - # ### end Alembic commands ### def downgrade() -> None: diff --git a/database/versions/ca5461f314f2_2_1_0.py b/database/versions/ca5461f314f2_2_1_0.py index c22de5e3..9ab24c12 100644 --- a/database/versions/ca5461f314f2_2_1_0.py +++ b/database/versions/ca5461f314f2_2_1_0.py @@ -18,13 +18,23 @@ depends_on = None def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - # 订阅增加mediaid - with contextlib.suppress(Exception): + conn = op.get_bind() + inspector = sa.inspect(conn) + + # 检查并添加 subscribe.mediaid + s_columns = inspector.get_columns('subscribe') + if not any(c['name'] == 'mediaid' for c in s_columns): op.add_column('subscribe', sa.Column('mediaid', sa.String(), nullable=True)) + + # 检查并创建索引 + s_indexes = inspector.get_indexes('subscribe') + if not any(i['name'] == 'ix_subscribe_mediaid' for i in s_indexes): op.create_index('ix_subscribe_mediaid', 'subscribe', ['mediaid'], unique=False) + + # 检查并添加 subscribehistory.mediaid + sh_columns = inspector.get_columns('subscribehistory') + if not any(c['name'] == 'mediaid' for c in sh_columns): op.add_column('subscribehistory', sa.Column('mediaid', sa.String(), nullable=True)) - # ### end Alembic commands ### def downgrade() -> None: diff --git a/database/versions/e2dbe1421fa4_2_0_3.py b/database/versions/e2dbe1421fa4_2_0_3.py index d2ccebe1..5df2dc50 100644 --- a/database/versions/e2dbe1421fa4_2_0_3.py +++ b/database/versions/e2dbe1421fa4_2_0_3.py @@ -21,28 +21,58 @@ depends_on = None def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - # 支持订阅自定义媒体类别和过滤规则组、自定义识别词 - with contextlib.suppress(Exception): + conn = op.get_bind() + inspector = sa.inspect(conn) + + # 检查并添加 downloadhistory.media_category + dh_columns = inspector.get_columns('downloadhistory') + if not any(c['name'] == 'media_category' for c in dh_columns): op.add_column('downloadhistory', sa.Column('media_category', sa.String(), nullable=True)) + + # 检查并添加 subscribe 表的列 + sub_columns = inspector.get_columns('subscribe') + if not any(c['name'] == 'custom_words' for c in sub_columns): op.add_column('subscribe', sa.Column('custom_words', sa.String(), nullable=True)) + if not any(c['name'] == 'media_category' for c in sub_columns): op.add_column('subscribe', sa.Column('media_category', sa.String(), nullable=True)) + if not any(c['name'] == 'filter_groups' for c in sub_columns): op.add_column('subscribe', sa.Column('filter_groups', sa.JSON(), nullable=True)) - # 将String转换为JSON类型 - with contextlib.suppress(Exception): - op.alter_column('subscribe', 'note', existing_type=sa.String(), type_=sa.JSON()) - op.alter_column('downloadhistory', 'note', existing_type=sa.String(), type_=sa.JSON()) - op.alter_column('mediaserveritem', 'note', existing_type=sa.String(), type_=sa.JSON()) - op.alter_column('message', 'note', existing_type=sa.String(), type_=sa.JSON()) - op.alter_column('plugindata', 'value', existing_type=sa.String(), type_=sa.JSON()) - op.alter_column('site', 'note', existing_type=sa.String(), type_=sa.JSON()) - op.alter_column('sitestatistic', 'note', existing_type=sa.String(), type_=sa.JSON()) - op.alter_column('systemconfig', 'value', existing_type=sa.String(), type_=sa.JSON()) - op.alter_column('userconfig', 'value', existing_type=sa.String(), type_=sa.JSON()) - # 清空用户配置表中不兼容的数据 + + # 定义需要检查和转换的表和列 + columns_to_alter = { + 'subscribe': 'note', + 'downloadhistory': 'note', + 'mediaserveritem': 'note', + 'message': 'note', + 'plugindata': 'value', + 'site': 'note', + 'sitestatistic': 'note', + 'systemconfig': 'value', + 'userconfig': 'value' + } + + for table, column_name in columns_to_alter.items(): + try: + cols = inspector.get_columns(table) + # 找到对应的列信息 + target_col = next((c for c in cols if c['name'] == column_name), None) + # 如果列存在且类型不是JSON,则进行修改 + if target_col and not isinstance(target_col['type'], sa.JSON): + # PostgreSQL需要指定USING子句来处理类型转换 + if conn.dialect.name == 'postgresql': + op.alter_column(table, column_name, + existing_type=sa.String(), + type_=sa.JSON(), + postgresql_using=f'"{column_name}"::json') + else: + op.alter_column(table, column_name, + existing_type=sa.String(), + type_=sa.JSON()) + except Exception as e: + print(f"Could not alter column {column_name} in table {table}: {e}") + with SessionFactory() as db: UserConfig.truncate(db) - # ### end Alembic commands ### def downgrade() -> None: diff --git a/database/versions/eaf9cbc49027_2_0_7.py b/database/versions/eaf9cbc49027_2_0_7.py index 0ced5b34..f1cce378 100644 --- a/database/versions/eaf9cbc49027_2_0_7.py +++ b/database/versions/eaf9cbc49027_2_0_7.py @@ -18,14 +18,19 @@ depends_on = None def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - # 站点管理、订阅增加下载器选项 - with contextlib.suppress(Exception): + conn = op.get_bind() + inspector = sa.inspect(conn) + + # 检查并添加 site.downloader + site_columns = inspector.get_columns('site') + if not any(c['name'] == 'downloader' for c in site_columns): op.add_column('site', sa.Column('downloader', sa.String(), nullable=True)) + + # 检查并添加 subscribe.downloader + subscribe_columns = inspector.get_columns('subscribe') + if not any(c['name'] == 'downloader' for c in subscribe_columns): op.add_column('subscribe', sa.Column('downloader', sa.String(), nullable=True)) -# ### end Alembic commands ### - def downgrade() -> None: pass diff --git a/database/versions/ecf3c693fdf3_2_0_5.py b/database/versions/ecf3c693fdf3_2_0_5.py index d0ea8119..fec107a7 100644 --- a/database/versions/ecf3c693fdf3_2_0_5.py +++ b/database/versions/ecf3c693fdf3_2_0_5.py @@ -19,15 +19,37 @@ depends_on = None def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - # 将String转换为JSON类型 - with contextlib.suppress(Exception): - op.alter_column('subscribehistory', 'sites', existing_type=sa.String(), type_=sa.JSON()) - with contextlib.suppress(Exception): - op.add_column('subscribehistory', sa.Column('custom_words', sa.String(), nullable=True)) - op.add_column('subscribehistory', sa.Column('media_category', sa.String(), nullable=True)) - op.add_column('subscribehistory', sa.Column('filter_groups', sa.JSON(), nullable=True)) - # ### end Alembic commands ### + conn = op.get_bind() + inspector = sa.inspect(conn) + table_name = 'subscribehistory' + + try: + columns = inspector.get_columns(table_name) + sites_col = next((c for c in columns if c['name'] == 'sites'), None) + # 如果 'sites' 列存在且类型不是 JSON,则进行修改 + if sites_col and not isinstance(sites_col['type'], sa.JSON): + if conn.dialect.name == 'postgresql': + op.alter_column(table_name, 'sites', + existing_type=sa.String(), + type_=sa.JSON(), + postgresql_using='sites::json') + else: + op.alter_column(table_name, 'sites', + existing_type=sa.String(), + type_=sa.JSON()) + except Exception as e: + print(f"Could not alter column 'sites' in table {table_name}: {e}") + + columns = inspector.get_columns(table_name) + + if not any(c['name'] == 'custom_words' for c in columns): + op.add_column(table_name, sa.Column('custom_words', sa.String(), nullable=True)) + + if not any(c['name'] == 'media_category' for c in columns): + op.add_column(table_name, sa.Column('media_category', sa.String(), nullable=True)) + + if not any(c['name'] == 'filter_groups' for c in columns): + op.add_column(table_name, sa.Column('filter_groups', sa.JSON(), nullable=True)) def downgrade() -> None: From 2766e8034667526a83337aa44823ef69046b8398 Mon Sep 17 00:00:00 2001 From: DDSRem <73049927+DDSRem@users.noreply.github.com> Date: Sat, 23 Aug 2025 18:36:11 +0800 Subject: [PATCH 2/2] fix(database): use logger as log output Co-Authored-By: Aqr-K <95741669+Aqr-K@users.noreply.github.com> --- database/versions/5b3355c964bb_2_2_0.py | 19 ++++++++++--------- database/versions/e2dbe1421fa4_2_0_3.py | 3 ++- database/versions/ecf3c693fdf3_2_0_5.py | 8 ++++---- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/database/versions/5b3355c964bb_2_2_0.py b/database/versions/5b3355c964bb_2_2_0.py index 4248810d..58f1402f 100644 --- a/database/versions/5b3355c964bb_2_2_0.py +++ b/database/versions/5b3355c964bb_2_2_0.py @@ -8,6 +8,7 @@ Create Date: 2025-08-19 12:27:08.451371 import sqlalchemy as sa from alembic import op +from app.log import logger from app.core.config import settings # revision identifiers, used by Alembic. @@ -41,7 +42,7 @@ def fix_postgresql_sequences(): """)) tables = [row[0] for row in result.fetchall()] - print(f"发现 {len(tables)} 个表需要检查序列") + logger.info(f"发现 {len(tables)} 个表需要检查序列") for table_name in tables: fix_table_sequence(connection, table_name) @@ -54,7 +55,7 @@ def fix_table_sequence(connection, table_name): try: # 跳过alembic_version表,它没有id列 if table_name == 'alembic_version': - print(f"跳过表 {table_name},这是Alembic版本表") + logger.debug(f"跳过表 {table_name},这是Alembic版本表") return # 检查表是否有id列 @@ -67,22 +68,22 @@ def fix_table_sequence(connection, table_name): id_column = result.fetchone() if not id_column: - print(f"表 {table_name} 没有id列,跳过") + logger.debug(f"表 {table_name} 没有id列,跳过") return is_identity, column_default = id_column # 检查是否已经是Identity类型 if is_identity == 'YES' or (column_default and 'GENERATED BY DEFAULT AS IDENTITY' in column_default): - print(f"表 {table_name} 的id列已经是Identity类型,跳过") + logger.debug(f"表 {table_name} 的id列已经是Identity类型,跳过") return # 检查是否有序列 - print(f"表 {table_name} 存在序列,需要修复") + logger.info(f"表 {table_name} 存在序列,需要修复") convert_to_identity(connection, table_name) except Exception as e: - print(f"修复表 {table_name} 序列时出错: {e}") + logger.error(f"修复表 {table_name} 序列时出错: {e}") # 回滚当前事务,避免影响后续操作 connection.rollback() @@ -106,12 +107,12 @@ def convert_to_identity(connection, table_name): ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH {next_value}) """)) - print(f"表 {table_name} 序列已转换为Identity,起始值为 {next_value}") + logger.info(f"表 {table_name} 序列已转换为Identity,起始值为 {next_value}") except Exception as e: - print(f"转换表 {table_name} 序列时出错: {e}") # 如果是已经存在的Identity错误,则忽略 if "already an identity column" in str(e): - print(f"表 {table_name} 的id列已经是Identity类型,忽略此错误") + logger.warn(f"表 {table_name} 的id列已经是Identity类型,忽略此错误: {e}") return + logger.error(f"转换表 {table_name} 序列时出错: {e}") raise diff --git a/database/versions/e2dbe1421fa4_2_0_3.py b/database/versions/e2dbe1421fa4_2_0_3.py index 5df2dc50..b4fb29ad 100644 --- a/database/versions/e2dbe1421fa4_2_0_3.py +++ b/database/versions/e2dbe1421fa4_2_0_3.py @@ -10,6 +10,7 @@ import contextlib from alembic import op import sqlalchemy as sa +from app.log import logger from app.db import SessionFactory from app.db.models import UserConfig @@ -69,7 +70,7 @@ def upgrade() -> None: existing_type=sa.String(), type_=sa.JSON()) except Exception as e: - print(f"Could not alter column {column_name} in table {table}: {e}") + logger.error(f"Could not alter column {column_name} in table {table}: {e}") with SessionFactory() as db: UserConfig.truncate(db) diff --git a/database/versions/ecf3c693fdf3_2_0_5.py b/database/versions/ecf3c693fdf3_2_0_5.py index fec107a7..f6787866 100644 --- a/database/versions/ecf3c693fdf3_2_0_5.py +++ b/database/versions/ecf3c693fdf3_2_0_5.py @@ -10,6 +10,8 @@ import contextlib from alembic import op import sqlalchemy as sa +from app.log import logger + # revision identifiers, used by Alembic. revision = 'ecf3c693fdf3' @@ -22,9 +24,9 @@ def upgrade() -> None: conn = op.get_bind() inspector = sa.inspect(conn) table_name = 'subscribehistory' + columns = inspector.get_columns(table_name) try: - columns = inspector.get_columns(table_name) sites_col = next((c for c in columns if c['name'] == 'sites'), None) # 如果 'sites' 列存在且类型不是 JSON,则进行修改 if sites_col and not isinstance(sites_col['type'], sa.JSON): @@ -38,9 +40,7 @@ def upgrade() -> None: existing_type=sa.String(), type_=sa.JSON()) except Exception as e: - print(f"Could not alter column 'sites' in table {table_name}: {e}") - - columns = inspector.get_columns(table_name) + logger.error(f"Could not alter column 'sites' in table {table_name}: {e}") if not any(c['name'] == 'custom_words' for c in columns): op.add_column(table_name, sa.Column('custom_words', sa.String(), nullable=True))