【发布时间】:2022-01-09 05:20:24
【问题描述】:
我一直致力于在 Kotlin 多平台项目上迁移我们的数据库,以便从未加密的状态中进行加密。这一切都在 Android 上完成,但 iOS 部分被证明是棘手的。 最后我得到了一些工作,但是当我返回数据库驱动程序时,我得到了这个错误:
函数没有或继承 @Throws 注释,因此异常不会作为 NSError 从 Kotlin 传播到 Objective-C/Swift。 相反,它被认为是意外和未处理的。程序将终止。 未捕获的 Kotlin 异常:kotlin.Exception: android/database/sqlite/SQLiteDatabaseCorruptException - 文件不是数据库(代码 26):,编译时:PRAGMA journal_mode
这很奇怪,因为它提到了 android/database,我不知道为什么。 无论如何,对于迁移,我已经设置了日志,并且可以看到它执行它,如果我调试应用程序并拉取数据库,看起来数据库现在已经被加密并且上面也有旧数据。 当它到达这段代码时似乎崩溃了:
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()
}
}
))
断点永远不会在升级 try/catch 中触发。 迁移逻辑如下所示,在返回 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)
}
}
}
}
感谢您的帮助
【问题讨论】:
-
这应该如何工作?
val exec1 = sqlite3_exec(unencryptedDb.value, "ATTACH DATABASE '$temporaryDatabasePath' AS encrypted KEY '$password';", null, null, null)你忘了替换参数吗?提示:如果断点不起作用,请尝试在代码中添加 NSLog,并找到确切的崩溃行。 -
@battlmonstr 来自我在网上看到的示例,看起来这些参数是可选的
-
哪些参数?您需要将字符串中的 $temporaryDatabasePath 替换为实际值(temporaryDatabasePath),请参阅sqlite.org/lang_attach.html PRAGMA 键和 ATTACH KEY 不在文档中。你在用SEE吗?
-
@battlmonstr 参数是 exec 命令中标记为 null 的内容。 $temporaryDatabasePath 只是将实际的数据库路径插入到 exec 命令中。 PRAGMA 和 ATTACH 是可以使用的命令。看来我已经想通了,我需要尽快在这里发布答案,但问题实际上是版本没有在新数据库上正确更新,返回为 0 而不是 1
-
啊...我对 Kotlin 的字符串插值语法感到困惑。但是 SQL 中的“KEY”语法似乎仍然是错误的。
标签: sql objective-c sqlite kotlin sqlcipher