Knex 3.2.3 在 SQLite 事务内改表,误报 foreign_keys 嵌套事务
Knex 3.2.3 可在 SQLite 嵌套改表时把未改动的 pragma 状态误判为需要恢复,3.2.7包含修补,约束状态仍需检查。
在 SQLite 事务内调用 Knex 的 dropForeign()、renameColumn() 等改表操作,可能遇到:
Refusing to create transaction: unable to change `foreign_keys` pragma inside a nested transaction
Knex 3.2.3 的一个问题会在并不需要改变 foreign_keys 时也抛出这条错误。SQLite 确实限制在活动事务中切换外键 pragma,但这里还需要区分数据库限制与 Knex 自己的判断错误。
改表辅助代码读取表元数据时会创建嵌套事务,传入 enforceForeignCheck: null,表示不碰当前 pragma。底层返回 null,也表示没有做需要恢复的变化;受影响的保护判断却使用 restoreForeignCheck !== undefined,将这个 null 也当作修改过状态,于是提前拒绝操作。
更新包含 guard 修补的版本
Knex 3.2.7 包含修复。修补将“确实需要恢复状态”与 null、undefined 区分,避免元数据读取的辅助事务误触发拒绝。应更新到包含这项修补、与项目其余依赖兼容的版本,而不是直接修改 node_modules 中的一行。
在已有测试数据库副本上,可以保留原来的事务入口复核,例如测试表中存在 old_name 列时:
await knex.transaction(async (trx) => {
await trx.schema.table('accounts', (table) => {
table.renameColumn('old_name', 'new_name');
});
});
不要把示例直接套到业务表名上。SQLite 的某些改表操作会由 Knex 重建表、复制数据,成功提交也是实际 schema 变更,应该先在测试副本核对迁移结果。
外键状态不能靠关闭约束来绕过
不要为了让迁移继续执行,就在整个应用启动时关闭 foreign_keys,或把所有事务的约束检查配置都改掉。这会把工具层的判断错误扩大成数据完整性变化。
验收时检查列定义、原有数据以及应保留的约束。可以查看:
PRAGMA foreign_keys;
PRAGMA foreign_key_check;
第一项核对连接上的外键检查状态,第二项检查仍存在的外键是否有违规记录。迁移如果本来就是删除某个外键,应按目标 schema 判断该约束;不能一边主动删除它,一边期待它继续拒绝无效数据。
这个修补不表示 SQLite 允许在任何嵌套事务中切换 foreign_keys。真正需要修改 pragma 的操作仍要遵守 SQLite 的事务边界;如果更新后仍出现相同文案,应检查自己是否确实在活动事务里要求改变检查状态。


