我使用TypeORM(v0.2.18(和Node.js(v12.7.0(在我的SQLite数据库中执行迁移。
这就是我的情况:我有一个名为country
的表和一个名为workflow
的表。我想删除country
中名为name
的列之一,但workflow
通过该列引用country
。
使用SQLite的DB浏览器,我可以使用以下语句成功删除该列:
PRAGMA foreign_keys=OFF;
CREATE TEMPORARY TABLE country_backup(id, createdAt, updatedAt, enabled, codeIso2);
INSERT INTO country_backup SELECT id, createdAt, updatedAt, enabled, codeIso2 FROM country;
DROP TABLE country;
CREATE TABLE country(id, createdAt, updatedAt, enabled, codeIso2);
INSERT INTO country SELECT id, createdAt, updatedAt, enabled, codeIso2 FROM country_backup;
DROP TABLE country_backup;
PRAGMA foreign_keys=ON;
我在这样的TypeORM迁移中使用了它:
...
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query("PRAGMA foreign_keys=OFF");
await queryRunner.query("CREATE TEMPORARY TABLE country_backup(id, createdAt, updatedAt, enabled, codeIso2)");
await queryRunner.query("INSERT INTO country_backup SELECT id, createdAt, updatedAt, enabled, codeIso2 FROM country");
await queryRunner.query("DROP TABLE country");
await queryRunner.query("CREATE TABLE country(id, createdAt, updatedAt, enabled, codeIso2)");
await queryRunner.query("INSERT INTO country SELECT id, createdAt, updatedAt, enabled, codeIso2 FROM country_backup");
await queryRunner.query("DROP TABLE country_backup");
await queryRunner.query("PRAGMA foreign_keys=ON");
}
...
但我得到了这个错误:
Error during migration run:
QueryFailedError: SQLITE_CONSTRAINT: FOREIGN KEY constraint failed
at new QueryFailedError (/.../api/src/error/QueryFailedError.ts:9:9)
at Statement.handler (/.../src/driver/sqlite/SqliteQueryRunner.ts:53:26)
at Statement.replacement (/.../api/node_modules/sqlite3/lib/trace.js:19:31)
at Statement.replacement (/.../api/node_modules/sqlite3/lib/trace.js:19:31) {
message: 'SQLITE_CONSTRAINT: FOREIGN KEY constraint failed',
errno: 19,
code: 'SQLITE_CONSTRAINT',
name: 'QueryFailedError',
query: 'DROP TABLE country',
parameters: []
}
为什么它能在SQLite的DB浏览器中工作,而不能在TypeORM中工作?它好像忽略了PRAGMA的声明。
基于此,我尝试了PRAGMA foreign_keys
和PRAGMA legacy_alter_table
(分别设置为OFF和on(在任何操作之前(以及on和OFF(在任何动作之后((。
我尝试在up()
函数内部和外部使用PRAGMA。在外部,这意味着我将await typeOrmConnection.runMigrations()
之前和之后的语句放在main.ts
文件中,如await typeOrmConnection.query("PRAGMA foreign_keys=OFF")
。
sqlite文档说,在执行多个语句时不能设置PRAGMA foreign_keys=off
,所以我尝试了这个方法,并通过将PRAGMA语句与其他语句分离来实现它。
已经为传递给up()
函数的查询运行器实例启动了事务。因此,立即结束现有事务,然后使用事务外查询关闭检查,然后启动另一个事务。
在新交易中写下剩余的非PRAGMA代码。
就在函数结束之前,提交内部事务,然后使用常规的单语句查询重新打开密钥检查,然后启动另一个事务(这样up()
运行后就会自动关闭一些事务,否则会出现错误(。
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.commitTransaction();
await queryRunner.query('PRAGMA foreign_keys=off');
await queryRunner.startTransaction();
// the rest of your queries
await queryRunner.commitTransaction();
await queryRunner.query('PRAGMA foreign_keys=on');
await queryRunner.startTransaction();
}