SQL - SQLiteDatabaseCorruptException - 编译时文件不是数据库:PRAGMA journal_mode
SQL - SQLiteDatabaseCorruptException - file is not a database while compiling: PRAGMA journal_mode
我一直致力于将 Kotlin Multiplatform 项目上的数据库从未加密迁移到加密。这一切都是在 Android 上完成的,但是 iOS 部分被证明是棘手的。
最后我让它有点工作了,但是当我 return 数据库驱动程序时,我得到这个错误:
Function doesn't have or inherit @Throws annotation and thus exception isn't propagated from Kotlin to Objective-C/Swift as NSError.
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
它提到 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。
迁移逻辑如下所示,在 returning 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)
}
}
}
}
感谢您的帮助
这其实是没有正确更新版本造成的。
测试时,我以为我已经正确更新了版本,但它仍然返回 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)
}
这在数据库上正确设置了版本并解决了问题。
我一直致力于将 Kotlin Multiplatform 项目上的数据库从未加密迁移到加密。这一切都是在 Android 上完成的,但是 iOS 部分被证明是棘手的。 最后我让它有点工作了,但是当我 return 数据库驱动程序时,我得到这个错误:
Function doesn't have or inherit @Throws annotation and thus exception isn't propagated from Kotlin to Objective-C/Swift as NSError. 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
它提到 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。 迁移逻辑如下所示,在 returning 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)
}
}
}
}
感谢您的帮助
这其实是没有正确更新版本造成的。 测试时,我以为我已经正确更新了版本,但它仍然返回 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)
}
这在数据库上正确设置了版本并解决了问题。