问题: 从未加密得数据库升级到加密数据库,则需要考虑到兼容问题
方案: 打开数据库open -> 设置秘钥 setkey -> 查看连接 goodConnection -> 新建数据库并迁移数据
代码
- (BOOL)openWithFlags:(int)flags vfs:(NSString *)vfsName {
#if SQLITE_VERSION_NUMBER >= 3005000
if (_db) {
return YES;
}
int err = sqlite3_open_v2([self sqlitePath], (sqlite3**)&_db, flags, [vfsName UTF8String]);
if(err != SQLITE_OK) {
NSLog(@"error opening!: %d", err);
return NO;
} else {
[self setKey:DB_KEY];
}
if (![self goodConnection]) {
if ([NSString stringWithCString:[self sqlitePath] encoding:NSUTF8StringEncoding]) {
[self upgradeDatabase:[NSString stringWithCString:[self sqlitePath] encoding:NSUTF8StringEncoding]];
// 下面几行是重点,很多不细心得程序员总是喜欢拷贝别人的文章粘贴成自己的博客,真是害人。
// 原因:原本是未加密得数据库,设置setKey没效果,需要迁移数据库之后再设置一次才有效
// u can try: 把下面5行隐藏了实验。从你从未加密数据库升级过来,第一次打开App总是没有数据,只有第二次打开App才会有数据显示。
[self close];
BOOL reRes = [self openWithFlags:flags vfs:vfsName];
if (reRes) {
[self setKey:DB_KEY];
}
}
}
if (_maxBusyRetryTimeInterval > 0.0) {
// set the handler
[self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval];
}
return YES;
#else
NSLog(@"openWithFlags requires SQLite 3.5");
return NO;
#endif
}
- (void)upgradeDatabase:(NSString *)path{
NSString *targetPath = [NSString stringWithFormat:@"%@.tmp.db", path];
if(targetPath){
const char* sqlQ = [[NSString stringWithFormat:@"ATTACH DATABASE '%@' AS encrypted KEY '%@';", targetPath, DB_KEY] UTF8String];
sqlite3 *unencrypted_DB;
if (sqlite3_open([path UTF8String], &unencrypted_DB) == SQLITE_OK) {
char *errmsg;
// Attach empty encrypted database to unencrypted database
sqlite3_exec(unencrypted_DB, sqlQ, NULL, NULL, &errmsg);
if (errmsg) {
NSLog(@"%@", [NSString stringWithUTF8String:errmsg]);
sqlite3_close(unencrypted_DB);
}
// export database
sqlite3_exec(unencrypted_DB, "SELECT sqlcipher_export('encrypted');", NULL, NULL, &errmsg);
if (errmsg) {
NSLog(@"%@", [NSString stringWithUTF8String:errmsg]);
sqlite3_close(unencrypted_DB);
}
// Detach encrypted database
sqlite3_exec(unencrypted_DB, "DETACH DATABASE encrypted;", NULL, NULL, &errmsg);
if (errmsg) {
NSLog(@"%@", [NSString stringWithUTF8String:errmsg]);
sqlite3_close(unencrypted_DB);
}
sqlite3_close(unencrypted_DB);
//delete tmp database
[self removeDatabasePath:targetPath targetPath:path];
}
else {
sqlite3_close(unencrypted_DB);
NSAssert1(NO, @"Failed to open database with message '%s'.", sqlite3_errmsg(unencrypted_DB));
}
}
}
- (void)removeDatabasePath:(NSString *)targetPath targetPath:(NSString *)sourcePath {
NSFileManager *fm = [[NSFileManager alloc] init];
[fm removeItemAtPath:sourcePath error:nil];
[fm moveItemAtPath:targetPath toPath:sourcePath error:nil];
}