首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用GRDB禁用或延迟外键强制执行以进行数据库迁移

使用GRDB禁用或延迟外键强制执行以进行数据库迁移
EN

Stack Overflow用户
提问于 2021-03-03 16:56:53
回答 1查看 227关注 0票数 0

我已经在我的移动应用程序中使用了GRDB库。所有当前的特性都运行良好,我已经开始实现从早期数据库版本的迁移。为了与其他平台的实现保持一致,我决定使用SQLite的user_version,而不是使用自己的表的GRDB自己的迁移框架。

随着表被修改、复制、创建和删除,一个迁移步骤(数据库版本)中的更改会相互依赖。当在事务结束时执行更改时,这会导致外键冲突,升级失败。

解决这一问题的一种方法是通过延迟(defer_foreign_keys杂注)来防止外键强制执行,或者通过为事务设置foreign_keys杂注暂时禁用它。不幸的是,我在这两种选择中的任何一种都没有多大的运气。在一些测试之后,例如,试图关闭外键检查

代码语言:javascript
复制
config.prepareDatabase { db in
    try db.execute(sql: "PRAGMA foreign_keys = OFF")
}

以及阅读语用

代码语言:javascript
复制
    try dbQueue.write { db in
        print(try Bool.fetchOne(db, sql: "PRAGMA foreign_keys")! as Bool)
    }

或者通过检查数据库表明foreign_keys设置仍然处于打开状态。

我的迁移步骤看起来有点简化,如下所示:

代码语言:javascript
复制
if try userVersion() < 2 {
    try dbQueue.write { db in
        try db.execute(sql: ...)
        try db.execute(sql: ...)
        ...
        try db.execute(sql: "PRAGMA user_version = 2")
    }
}
if try userVersion() < 3 {
    try dbQueue.write { db in
        try db.execute(sql: ...)
        try db.execute(sql: ...)
        ...
        try db.execute(sql: "PRAGMA user_version = 3")
    }
}

我最初的GRDB设置如下:

代码语言:javascript
复制
var config = Configuration()
config.foreignKeysEnabled = true
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!
dbPath = (appSupportDirectory as NSString).appendingPathComponent(dbName)

let fileManager = FileManager.default
if fileManager.fileExists(atPath: dbPath) {
    // Just connect to database.
    dbQueue = try DatabaseQueue(path: dbPath, configuration: config)
} else {
    // Create new database.
    dbQueue = try DatabaseQueue(path: dbPath, configuration: config)
    // Create tables
    ...
}

// Database migration steps:
...

在数据库迁移过程中,防止外键故障的最佳方法是什么,为什么我的语用语句不起作用?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-03-04 10:11:04

引用关键字

此杂注foreign_keys是事务中的非操作;只有在没有挂起的BEGIN或保存点时,才能启用或禁用外键约束强制执行。

GRDB DatabaseQueue.write方法将打开一个事务。因此,当您想禁用外键时,必须执行人工事务处理

代码语言:javascript
复制
// Add the `writeWithDeferredForeignKeys` method to
// DatabaseQueue and DatabasePool.
extension DatabaseWriter {
    func writeWithDeferredForeignKeys(_ updates: (Database) throws -> Void) throws {
        try writeWithoutTransaction { db in
            // Disable foreign keys
            try db.execute(sql: "PRAGMA foreign_keys = OFF");
            
            do {
                // Perform updates in a transaction
                try db.inTransaction {
                    try updates(db)
                    
                    // Check foreign keys before commit
                    if try Row.fetchOne(db, sql: "PRAGMA foreign_key_check") != nil {
                        throw DatabaseError(resultCode: .SQLITE_CONSTRAINT_FOREIGNKEY)
                    }
                    
                    return .commit
                }
                
                // Re-enable foreign keys
                try db.execute(sql: "PRAGMA foreign_keys = ON");
            } catch {
                // Re-enable foreign keys and rethrow
                try db.execute(sql: "PRAGMA foreign_keys = ON");
                throw error
            }
        }
    }
}

try dbQueue.writeWithDeferredForeignKeys { db in
    try db.execute(sql: ...)
    try db.execute(sql: ...)
}

我对defer_foreign_keys还不太熟悉,但是上面的示例代码应该可以帮助您收敛到您想要的解决方案。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66461554

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档