简体   繁体   English

SQL - SQLiteDatabaseCorruptException - 编译时文件不是数据库:PRAGMA journal_mode

[英]SQL - SQLiteDatabaseCorruptException - file is not a database while compiling: PRAGMA journal_mode

I have been working on migrating our DB on a Kotlin Multiplatform project to be encrypted from unencrypted.我一直在努力将我们的数据库迁移到 Kotlin 多平台项目上,以便从未加密中进行加密。 It's all done on Android, however the iOS part is proving tricky.这一切都在 Android 上完成,但是 iOS 部分被证明是棘手的。 Finally I got it somewhat working, however when I return the DB driver, I get this error:最后我得到了一些工作,但是当我返回数据库驱动程序时,我得到了这个错误:

Function doesn't have or inherit @Throws annotation and thus exception isn't propagated from Kotlin to Objective-C/Swift as NSError. Function 没有或继承 @Throws 注释,因此异常不会从 Kotlin 作为 NSError 传播到 Objective-C/Swift。 It is considered unexpected and unhandled instead.相反,它被认为是意外和未处理的。 Program will be terminated.程序将终止。 Uncaught Kotlin exception: kotlin.Exception: android/database/sqlite/SQLiteDatabaseCorruptException - file is not a database (code 26): , while compiling: PRAGMA journal_mode未捕获的 Kotlin 异常:kotlin.Exception:android/database/sqlite/SQLiteDatabaseCorruptException - 文件不是数据库(代码 26):,编译时:PRAGMA journal_mode

It's strange as it mentions android/database and I'm not sure why.这很奇怪,因为它提到了 android/database,我不知道为什么。 Anyways for the migration, I have logs setup and I can see that it performs it, and if I debug the app and pull the DB, it does look like the DB has now been encrypted and has old data on it too.无论如何,对于迁移,我已经设置了日志,并且可以看到它执行它,如果我调试应用程序并拉取数据库,看起来数据库现在已经被加密并且上面也有旧数据。 It seems to crash when it gets to this code:当它到达这段代码时,它似乎崩溃了:

NativeSqliteDriver(DatabaseConfiguration(
    name = DatabaseName,
    version = AppDatabase.Schema.version,
    create = { connection -> wrapConnection(connection) { AppDatabase.Schema.create(it) } },
    upgrade = { connection, oldVersion, newVersion ->
        try {
            wrapConnection(connection) {
                NSLog("old version is ${oldVersion} new version is ${newVersion}")
                AppDatabase.Schema.migrate(it, oldVersion, newVersion)
            }
        } catch (exception: Exception) {
            NSLog("exception is ${exception.toString()}")
        }
     }
     //Workaround for DatabaseConnection.setCipherKey causing an exception on iOS 14
     configConnection = { connection, _ ->
         val statement = "PRAGMA key = \"$password\";"
         connection.withStatement(statement) {
             stringForQuery()
         }
     }
))

Breakpoints never trigger in the upgrade try/catch.断点永远不会在升级 try/catch 中触发。 The migration logic looks like this and is performed before returning the NativeSqlLiteDriver.迁移逻辑如下所示,在返回 NativeSqlLiteDriver 之前执行。

@ExperimentalUnsignedTypes
override fun migrateToEncryptedDatabase(databasePath: String, temporaryDatabasePath: String, password: String) {
    val fileManager = NSFileManager.defaultManager()
    fileManager.createFileAtPath(temporaryDatabasePath, null, null)

    if (fileManager.fileExistsAtPath(databasePath)) {
        memScoped {
            val unencryptedDb: CPointerVar<sqlite3> = allocPointerTo()
            val encryptedDb: CPointerVar<sqlite3> = allocPointerTo()

            if (sqlite3_open(databasePath, unencryptedDb.ptr) == SQLITE_OK) {
                val exec1 = sqlite3_exec(unencryptedDb.value, "ATTACH DATABASE '$temporaryDatabasePath' AS encrypted KEY '$password';", null, null, null)
                val exec2 = sqlite3_exec(unencryptedDb.value, "SELECT sqlcipher_export('encrypted')", null, null, null)
                val exec3 = sqlite3_exec(unencryptedDb.value, "DETACH DATABASE encrypted;", null, null, null)

                val version = sqlite3_version
                sqlite3_close(unencryptedDb.value)

                if (sqlite3_open(temporaryDatabasePath, encryptedDb.ptr) == SQLITE_OK) {
                    sqlite3_key(encryptedDb.value, password.cstr, password.cstr.size)
                }
                sqlite3_close(unencryptedDb.value)

                val error: ObjCObjectVar<NSError?> = alloc()
                val removeResult = fileManager.removeItemAtPath(databasePath, error.ptr)

                if (removeResult == false) {
                    NSLog("Error removing db file: " + error.value)
                } else {

                }

                val result = fileManager.moveItemAtPath(temporaryDatabasePath, databasePath, error.ptr)

                if (result == false) {
                    NSLog("Error moving db file: " + error.value)
                } else {

                }
            } else {
                NSLog("Failed to open the unencrypted DB with message: " + sqlite3_errmsg(unencryptedDb.value))
                sqlite3_close(unencryptedDb.value)
            }
        }
    }
}

Thanks for any help谢谢你的帮助

This was actually caused by not updating the version correctly.这实际上是由于没有正确更新版本造成的。 When testing, I thought I had updated the version correctly, however it was still returning as 0 instead of 1. I solved it by doing this.测试时,我认为我已经正确更新了版本,但是它仍然返回为 0 而不是 1。我通过这样做解决了它。 First a method to retrieve the current DB version:首先是检索当前数据库版本的方法:

private fun getUserVersion(unencryptedDBPointer: CPointerVar<sqlite3>): Int? {
    memScoped {
        val sqliteStatementPointer: CPointerVar<sqlite3_stmt> = allocPointerTo()
        var databaseVersion: Int? = null

        if (sqlite3_prepare_v2(unencryptedDBPointer.value, "PRAGMA user_version;", USER_VERSION_STATEMENT_MAX_LENGTH, sqliteStatementPointer.ptr, null) == SQLITE_OK) {
            while (sqlite3_step(sqliteStatementPointer.value) == SQLITE_ROW) {
                databaseVersion = sqlite3_column_int(sqliteStatementPointer.value, COLUMN_TO_USE)
            }
        } else {
            Logger.d("Error preparing the database: ${sqlite3_errmsg(unencryptedDBPointer.value)}")
        }

        sqlite3_finalize(sqliteStatementPointer.value)

        return databaseVersion
    }
}

Then, inside my migration method, I had this code:然后,在我的迁移方法中,我有以下代码:

val dbVersion = getUserVersion(unencryptedDbPointer)

if (sqlite3_open(temporaryDatabasePath, encryptedDbPointer.ptr) == SQLITE_OK && dbVersion != null) {
     sqlite3_key(encryptedDbPointer.value, password.cstr, password.cstr.size)
     sqlite3_exec(encryptedDbPointer.value, "PRAGMA user_version=$dbVersion", null, null, null)
}

This correctly set the version on the DB and fixed the issue.这正确设置了数据库上的版本并解决了问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM