【原】FMDB源码阅读(二) 【原】FMDB源码阅读(二)
本文转载请注明出处 —— polobymulberry-博客园
1. 前言
上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比如FMDB的executeUpdate:系列方法、数据库的加解密等等。这次写的就是对FMDatabase和FMResultSet这两个文件的补全内容。每次写这种补全的内容最头疼,内容会很分散,感觉没啥条理。
2. executeUpdate:系列函数
注意除了“SELECT”语句外,其他的SQL语句都需要使用executeUpdate:系列函数,这些SQL语句包括`CREATE`, `UPDATE`, `INSERT`, `ALTER`, `COMMIT`, `BEGIN`, `DETACH`, `DELETE`, `DROP`, `END`, `EXPLAIN`, `VACUUM`, 和`REPLACE`等等。
executeUpdate:函数使用例子如下:
BOOL success = [db executeUpdate:@"INSERT INTO authors (identifier, name, date, comment) VALUES (?, ?, ?, ?)", @(identifier), name, date, comment ?: [NSNull null]]; if (!success) { NSLog(@"error = %@", [db lastErrorMessage]); }
基本上所有executeUpdate:系列函数都是对- [FMDatabase executeUpdate:error:withArgumentsInArray:orDictionary:orVAList:]函数的封装。注意- [FMDatabase executeUpdate:error:withArgumentsInArray:orDictionary:orVAList:]函数的具体实现,基本和- [FMDatabase executeQuery:withArgumentsInArray:orDictionary:orVAList:]大部分实现是差不多的,关键在于executeQuery是查询语句,所以它需要FMResultSet来保存查询的结果。而executeUpdate是非查询语句,不需要保存查询结果,但需要调用sqlite3_step(pStmt)来执行该SQL语句。这里就不赘述了,详见源码。
3. executeStatements:系列函数
使用executeStatements:函数可以将多个SQL执行语句写在一个字符串中,并执行。具体使用举例如下:
NSString *sql = @"create table bulktest1 (id integer primary key autoincrement, x text);" "create table bulktest2 (id integer primary key autoincrement, y text);" "create table bulktest3 (id integer primary key autoincrement, z text);" "insert into bulktest1 (x) values ('XXX');" "insert into bulktest2 (y) values ('YYY');" "insert into bulktest3 (z) values ('ZZZ');"; success = [db executeStatements:sql]; sql = @"select count(*) as count from bulktest1;" "select count(*) as count from bulktest2;" "select count(*) as count from bulktest3;"; success = [self.db executeStatements:sql withResultBlock:^int(NSDictionary *dictionary) { NSInteger count = [dictionary[@"count"] integerValue]; XCTAssertEqual(count, 1, @"expected one record for dictionary %@", dictionary); return 0; }];
基本上executeStatements:系列函数最终封装的都是- [FMDatabase executeStatements:withResultBlock:]函数,而此函数又是对sqlite3_exec函数的封装。
sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)
该例程提供了一个执行 SQL 命令的快捷方式,SQL 命令由 sql 参数提供,可以由多个 SQL 命令组成。
在这里,第一个参数 sqlite3 是打开的数据库对象,sqlite_callback 是一个回调,data 作为其第一个参数,errmsg 将被返回用来获取程序生成的任何错误。
sqlite3_exec() 程序解析并执行由 sql 参数所给的每个命令,直到字符串结束或者遇到错误为止。
executeStatements:源码如下(很简单,就不赘述了):
- (BOOL)executeStatements:(NSString *)sql withResultBlock:(FMDBExecuteStatementsCallbackBlock)block { int rc; char *errmsg = nil; rc = sqlite3_exec([self sqliteHandle], [sql UTF8String], block ? FMDBExecuteBulkSQLCallback : nil, (__bridge void *)(block), &errmsg); if (errmsg && [self logsErrors]) { NSLog(@"Error inserting batch: %s", errmsg); sqlite3_free(errmsg); } return (rc == SQLITE_OK); }
4. executeQueryWithFormat:和executeUpdateWithFormat:函数
考虑到如果用户直接调用printf那种形式的字符串(比如“ INSERT INTO myTable (%@) VALUES (%d)”, “age”,25),那么就需要自己将对应字符串处理成相应的SQL语句。恰好executeQuery和executeUpdate系列函数提供了相应的接口:
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2); - (BOOL)executeUpdateWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
其实这两个函数和其他executeQuery和executeUpdate系列方法,多的就是一个将format和…转化为可用的SQL语句步骤。其它部分其实本质还是调用- [FMDatabase executeUpdate:error:withArgumentsInArray:orDictionary:orVAList:]和- [FMDatabase executeQuery:withArgumentsInArray:orDictionary:orVAList:]。下面仅列出format和…的转化代码:
va_list args; // 将args指向format中第一个参数 va_start(args, format); NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]]; NSMutableArray *arguments = [NSMutableArray array]; // 使用extractSQL函数将format和args转化为sql和arguments供后面函数使用 [self extractSQL:format argumentsList:args intoString:sql arguments:arguments]; // 关闭args,与va_start成对出现 va_end(args);
至于extractSQL:这个函数其实就是将(“INSERT INTO myTable (%@) VALUES (%d)”, “age”,25)中的%s和%d这种符号变成”?”,然后将”age”和25加入到arguments中。具体实现如下:
- (void)extractSQL:(NSString *)sql argumentsList:(va_list)args intoString:(NSMutableString *)cleanedSQL arguments:(NSMutableArray *)arguments { NSUInteger length = [sql length]; unichar last = '