# 修复文件夹迁移的 asyncpg 兼容性问题 > **日期**:2026-01-29 > **类型**:Bug 修复 > **影响范围**:数据库迁移 --- ## 问题描述 在执行 `20260129_1410_create_folder_shares_and_export_jobs.py` 迁移时,遇到以下错误: ``` asyncpg.exceptions.PostgresSyntaxError: cannot insert multiple commands into a prepared statement ``` **原因**:asyncpg 驱动不支持在一个 prepared statement 中执行多个 SQL 命令(如多个 COMMENT 语句用分号分隔)。 --- ## 解决方案 将所有多行 COMMENT 语句拆分为单独的 `op.execute()` 调用。 ### 修改前 ```python op.execute(""" COMMENT ON TABLE folder_shares IS '文件夹分享表'; COMMENT ON COLUMN folder_shares.id IS '分享记录ID (UUID v7)'; COMMENT ON COLUMN folder_shares.folder_id IS '文件夹ID(逻辑外键,无物理约束)'; ... """) ``` ### 修改后 ```python op.execute("COMMENT ON TABLE folder_shares IS '文件夹分享表'") op.execute("COMMENT ON COLUMN folder_shares.id IS '分享记录ID (UUID v7)'") op.execute("COMMENT ON COLUMN folder_shares.folder_id IS '文件夹ID(逻辑外键,无物理约束)'") ... ``` --- ## 变更内容 **文件**:`server/alembic/versions/20260129_1410_create_folder_shares_and_export_jobs.py` 1. **folder_shares 表注释**:拆分为 14 个独立的 `op.execute()` 调用 2. **folder_export_jobs 表注释**:拆分为 18 个独立的 `op.execute()` 调用 --- ## 验证结果 ### 迁移执行成功 ```bash $ docker exec jointo-server-app alembic current 20260129_1410 (head) ``` ### 表结构验证 **folder_shares 表**: - ✅ 13 个字段 - ✅ 5 个索引(包括主键) - ✅ 4 个约束检查 - ✅ 使用 TIMESTAMPTZ 类型 - ✅ 使用 SMALLINT 枚举 **folder_export_jobs 表**: - ✅ 17 个字段 - ✅ 5 个索引(包括主键) - ✅ 2 个约束检查 - ✅ 使用 TIMESTAMPTZ 类型 - ✅ 使用 SMALLINT 枚举 --- ## 技术说明 ### asyncpg 限制 asyncpg 使用 prepared statements 来提高性能,但这要求每个 statement 只能包含一个 SQL 命令。当使用分号分隔多个命令时,会触发此错误。 ### 最佳实践 在 Alembic 迁移中使用 asyncpg 时: 1. **单个命令**:每个 `op.execute()` 只执行一个 SQL 命令 2. **避免分号**:不要在一个字符串中用分号分隔多个命令 3. **批量操作**:如果需要执行多个相似命令,使用循环 ```python # ✅ 推荐 for column in columns: op.execute(f"COMMENT ON COLUMN {table}.{column} IS '{comment}'") # ❌ 避免 op.execute(""" COMMENT ON COLUMN table.col1 IS 'comment1'; COMMENT ON COLUMN table.col2 IS 'comment2'; """) ``` --- ## 相关文档 - [asyncpg 文档](https://magicstack.github.io/asyncpg/) - [Alembic 迁移最佳实践](https://alembic.sqlalchemy.org/en/latest/tutorial.html) - [文件夹服务完整实现](./2026-01-29-folder-service-complete-implementation.md) --- ## 总结 通过将多行 COMMENT 语句拆分为独立的 `op.execute()` 调用,成功解决了 asyncpg 的 prepared statement 限制问题。迁移现已正常执行,两个新表(folder_shares 和 folder_export_jobs)已成功创建。