iOS sqlcipher fmdb inTransaction “文件已加密或不是数据库”

Posted

技术标签:

【中文标题】iOS sqlcipher fmdb inTransaction “文件已加密或不是数据库”【英文标题】:iOS sqlcipher fmdb inTransaction “File is encrypted or is not a database” 【发布时间】:2015-11-02 09:03:32 【问题描述】:

当我使用sqlcipher加密我的数据库,并在FMDatabaseQueue中调用inDatabase——成功!

但是当我将inDatabase 更改为inTransaction 时,控制台显示“文件已加密或不是数据库”。

代码:

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:st_dbPath];

// success
[queue inDatabase:^(FMDatabase *db) 

    [db setKey:st_dbKey];
    [db executeUpdate:@"INSERT INTO t_user VALUES (16)"];
];

// fail : File is encrypted or is not a database
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) 

    [db setKey:st_dbKey];
    [db executeUpdate:@"INSERT INTO t_user VALUES (17)"];
];

以及加密数据库代码:

NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDir = [documentPaths objectAtIndex:0];
NSString *ecDB = [documentDir stringByAppendingPathComponent:st_dbEncryptedName];

// SQL Query. NOTE THAT DATABASE IS THE FULL PATH NOT ONLY THE NAME
const char* sqlQ = [[NSString stringWithFormat:@"ATTACH DATABASE '%@' AS encrypted KEY '%@';", ecDB, st_dbKey] UTF8String];

sqlite3 *unencrypted_DB;
if (sqlite3_open([st_dbPath UTF8String], &unencrypted_DB) == SQLITE_OK) 

    // Attach empty encrypted database to unencrypted database
    sqlite3_exec(unencrypted_DB, sqlQ, NULL, NULL, NULL);

    // export database
    sqlite3_exec(unencrypted_DB, "SELECT sqlcipher_export('encrypted');", NULL, NULL, NULL);

    // Detach encrypted database
    sqlite3_exec(unencrypted_DB, "DETACH DATABASE encrypted;", NULL, NULL, NULL);

    sqlite3_close(unencrypted_DB);

else 
    sqlite3_close(unencrypted_DB);
    NSAssert1(NO, @"Failed to open database with message '%s'.", sqlite3_errmsg(unencrypted_DB));

加密代码来自那里:http://www.guilmo.com/fmdb-with-sqlcipher-tutorial/

【问题讨论】:

【参考方案1】:

调用 inTransaction 会导致 SQL 语句 begin exclusive transaction 在调用完成块之前在您的数据库上执行。因此,在您有机会调用 setKey 之前执行该 SQL。

您可以改为使用 inDatabase 并在 FBDatabase 实例上调用 beginTransaction,如下所示:

[self.queue inDatabase:^(FMDatabase *db) 

    [db setKey:st_dbKey];
    [db beginTransaction];

    [db executeUpdate:@"INSERT INTO t_user VALUES (17)"];

    [db commit];
];

【讨论】:

【参考方案2】:

Gus Hovland 的回答有效,但我认为更好的方法是将 inTransaction: 更改为 inDeferredTransaction:。来自 FMDB 的 inTransaction 文档:

" 不同于SQLite的BEGIN TRANSACTION,这个方法目前执行 排他事务,而不是延迟事务。这种行为 在 FMDB 的未来版本中可能会发生变化,因此这种方法 最终可能会采用标准的 SQLite 行为并执行 延期交易。如果你真的需要独家交易,那就是 建议您使用inExclusiveTransaction,而不是仅 使您的意图明确,但也使您的代码面向未来。”

【讨论】:

以上是关于iOS sqlcipher fmdb inTransaction “文件已加密或不是数据库”的主要内容,如果未能解决你的问题,请参考以下文章

FMDB/SQLCipher 和 FMDBMigrationManager 迁移失败

如何在 Swift 中使用“FMDB/SQLCipher”进行加密和解密?

将 FMDB + SQLCipher 与 Rubymotion 一起使用?

如何通过 SQLCipher 在后台使用 FMDB?

迁移 SQLCipher 版本。 2.x 数据库到版本。 FMDB 使用的 3.x

Cocoapods、Swift 和 FMDB Cipher - 这三个目前兼容吗?