我一直致力于在 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)
}
}
}
}
謝謝你的幫助
uj5u.com熱心網友回復:
這實際上是由于沒有正確更新版本造成的。測驗時,我以為我已經正確更新了版本,但它仍然回傳 0 而不是 1。我通過這樣做解決了它。首先是檢索當前資料庫版本的方法:
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
}
}
然后,在我的遷移方法中,我有以下代碼:
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)
}
這正確設定了資料庫上的版本并解決了問題。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/382084.html
上一篇:macOS:以編程方式檢查行程是否作為launchDaemon或launchAgent或從命令列運行
下一篇:條件?:ComboBox的運算子
