FMDB是对SQLite数据库的封装。源码地址
源码
FMDatabase -表示单个SQLite数据库。用于执行SQL语句。
FMResultSet -表示在FMDatabase上执行查询的结果。
FMDatabaseQueue -如果你想在多线程上执行查询和更新,你会想使用这个类。
FMDatabasePool -
FMDatabaseAdditions - FMDatabase扩展
FMDatabaseQueue
初始化、打开和关闭队列
初始化最终会调用- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName
方法。
+ (instancetype)databaseQueueWithPath:(NSString *)aPath {
FMDatabaseQueue *q = [[self alloc] initWithPath:aPath];
FMDBAutorelease(q);
return q;
}
.....
- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName {
self = [super init];
if (self != nil) {
_db = [[[self class] databaseClass] databaseWithPath:aPath];
FMDBRetain(_db);
#if SQLITE_VERSION_NUMBER >= 3005000
BOOL success = [_db openWithFlags:openFlags vfs:vfsName];
#else
BOOL success = [_db open];
#endif
if (!success) {
NSLog(@"Could not create database queue for path %@", aPath);
FMDBRelease(self);
return 0x00;
}
_path = FMDBReturnRetained(aPath);
_queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL);
_openFlags = openFlags;
_vfsName = [vfsName copy];
}
return self;
}
....
在该方法内会创建一个FMDatabase
对象、创建一个串行队列并将自身对象与该串行队列相关联用于死锁检测。
- (void)dealloc {
FMDBRelease(_db);
FMDBRelease(_path);
FMDBRelease(_vfsName);
if (_queue) {
FMDBDispatchQueueRelease(_queue);
_queue = 0x00;
}
#if ! __has_feature(objc_arc)
[super dealloc];
#endif
}
- (void)close {
FMDBRetain(self);
dispatch_sync(_queue, ^() {
[self->_db close];
FMDBRelease(_db);
self->_db = 0x00;
});
FMDBRelease(self);
}
在关闭会同时关闭FMDatabase
对象并将FMDatabase
对象置nil
。
释放时会将队列置nil
。
有意思的是在对FMDatabase
对象进行操作的时候不是直接调用FMDatabaseQueue
对象中的FMDatabase
对象属性,而是先调用一个返回FMDatabase
对象的方法在该方法中判断FMDatabase
对象是否打开,打开失败就返回nil。
- (FMDatabase*)database {
if (![_db isOpen]) {
if (!_db) {
_db = FMDBReturnRetained([[[self class] databaseClass] databaseWithPath:_path]);
}
#if SQLITE_VERSION_NUMBER >= 3005000
BOOL success = [_db openWithFlags:_openFlags vfs:_vfsName];
#else
BOOL success = [_db open];
#endif
if (!success) {
NSLog(@"FMDatabaseQueue could not reopen database for path %@", _path);
FMDBRelease(_db);
_db = 0x00;
return 0x00;
}
}
return _db;
}
操作数据库
- (void)inDatabase:(__attribute__((noescape)) void (^)(FMDatabase *db))block {
#ifndef NDEBUG
/* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue
* and then check it against self to make sure we're not about to deadlock. */
FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey);
assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock");
#endif
FMDBRetain(self);
dispatch_sync(_queue, ^() {
FMDatabase *db = [self database];
block(db);
if ([db hasOpenResultSets]) {
NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]");
#if defined(DEBUG) && DEBUG
NSSet *openSetCopy = FMDBReturnAutoreleased([[db valueForKey:@"_openResultSets"] copy]);
for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) {
FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue];
NSLog(@"query: '%@'", [rs query]);
}
#endif
}
});
FMDBRelease(self);
}
- (void)beginTransaction:(FMDBTransaction)transaction withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block {
FMDBRetain(self);
dispatch_sync(_queue, ^() {
BOOL shouldRollback = NO;
switch (transaction) {
case FMDBTransactionExclusive:
[[self database] beginTransaction];
break;
case FMDBTransactionDeferred:
[[self database] beginDeferredTransaction];
break;
case FMDBTransactionImmediate:
[[self database] beginImmediateTransaction];
break;
}
block([self database], &shouldRollback);
if (shouldRollback) {
[[self database] rollback];
}
else {
[[self database] commit];
}
});
FMDBRelease(self);
}
- (void)inTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block {
[self beginTransaction:FMDBTransactionExclusive withBlock:block];
}
- (void)inDeferredTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block {
[self beginTransaction:FMDBTransactionDeferred withBlock:block];
}
- (void)inExclusiveTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block {
[self beginTransaction:FMDBTransactionExclusive withBlock:block];
}
- (void)inImmediateTransaction:(__attribute__((noescape)) void (^)(FMDatabase * _Nonnull, BOOL * _Nonnull))block {
[self beginTransaction:FMDBTransactionImmediate withBlock:block];
}
- (void)inDatabase:(__attribute__((noescape)) void (^)(FMDatabase *db))block
方法时会dispatch_get_specific
判断是否当前队列,避免重入队列引起死锁。
事务操作
详见:ios 事务
Checkpoint
详见:SQLite学习笔记(未完待续)
FMDatabase
初始化、关闭
- (instancetype)initWithPath:(NSString *)path {
assert(sqlite3_threadsafe()); // whoa there big boy- gotta make sure sqlite it happy with what we're going to do.
self = [super init];
if (self) {
_databasePath = [path copy];
_openResultSets = [[NSMutableSet alloc] init];
_db = nil;
_logsErrors = YES;
_crashOnErrors = NO;
_maxBusyRetryTimeInterval = 2;
_isOpen = NO;
}
return self;
}
......
#if ! __has_feature(objc_arc)
- (void)finalize {
[self close];
[super finalize];
}
#endif
- (void)dealloc {
[self close];
FMDBRelease(_openResultSets);
FMDBRelease(_cachedStatements);
FMDBRelease(_dateFormat);
FMDBRelease(_databasePath);
FMDBRelease(_openFunctions);
#if ! __has_feature(objc_arc)
[super dealloc];
#endif
}
.......
- (BOOL)close {
[self clearCachedStatements];
[self closeOpenResultSets];
if (!_db) {
return YES;
}
int rc;
BOOL retry;
BOOL triedFinalizingOpenStatements = NO;
do {
retry = NO;
rc = sqlite3_close(_db);
if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
if (!triedFinalizingOpenStatements) {
triedFinalizingOpenStatements = YES;
sqlite3_stmt *pStmt;
while ((pStmt = sqlite3_next_stmt(_db, nil)) !=0) {
NSLog(@"Closing leaked statement");
sqlite3_finalize(pStmt);
retry = YES;
}
}
}
else if (SQLITE_OK != rc) {
NSLog(@"error closing!: %d", rc);
}
}
while (retry);
_db = nil;
_isOpen = false;
return YES;
}
关闭时清空存储,回复初始设置。
存储
- (BOOL)executeUpdate:(NSString*)sql, ... {
va_list args;
//必须调用va_start() 传递有效的参数列表va_list和函数强制的第一个参数。第一个参数代表将要传递的参数的个数。
va_start(args, sql);
BOOL result = [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:nil orVAList:args];
//调用va_end()传递va_list对完成后的清除是必须的。
va_end(args);
return result;
}
.....
- (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
if (![self databaseExists]) {
return NO;
}
if (_isExecutingStatement) {
[self warnInUse];
return NO;
}
_isExecutingStatement = YES;
int rc = 0x00;
sqlite3_stmt *pStmt = 0x00;
FMStatement *cachedStmt = 0x00;
if (_traceExecution && sql) {
NSLog(@"%@ executeUpdate: %@", self, sql);
}
if (_shouldCacheStatements) {
cachedStmt = [self cachedStatementForQuery:sql];
pStmt = cachedStmt ? [cachedStmt statement] : 0x00;
[cachedStmt reset];
}
if (!pStmt) {
rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
if (SQLITE_OK != rc) {
if (_logsErrors) {
NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
NSLog(@"DB Query: %@", sql);
NSLog(@"DB Path: %@", _databasePath);
}
if (_crashOnErrors) {
NSAssert(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
abort();
}
if (outErr) {
*outErr = [self errorWithMessage:[NSString stringWithUTF8String:sqlite3_errmsg(_db)]];
}
sqlite3_finalize(pStmt);
_isExecutingStatement = NO;
return NO;
}
}
id obj;
int idx = 0;
int queryCount = sqlite3_bind_parameter_count(pStmt);
// If dictionaryArgs is passed in, that means we are using sqlite's named parameter support
if (dictionaryArgs) {
for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
// Prefix the key with a colon.
NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
if (_traceExecution) {
NSLog(@"%@ = %@", parameterName, [dictionaryArgs objectForKey:dictionaryKey]);
}
// Get the index for the parameter name.
int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
FMDBRelease(parameterName);
if (namedIdx > 0) {
// Standard binding from here.
[self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
// increment the binding count, so our check below works out
idx++;
}
else {
NSString *message = [NSString stringWithFormat:@"Could not find index for %@", dictionaryKey];
if (_logsErrors) {
NSLog(@"%@", message);
}
if (outErr) {
*outErr = [self errorWithMessage:message];
}
}
}
}
else {
while (idx < queryCount) {
if (arrayArgs && idx < (int)[arrayArgs count]) {
obj = [arrayArgs objectAtIndex:(NSUInteger)idx];
}
else if (args) {
//调用va_arg()传递参数列表va_list 和将被返回的参数的类型。va_arg()的返回值是当前的参数。
obj = va_arg(args, id);
}
else {
//We ran out of arguments
break;
}
if (_traceExecution) {
if ([obj isKindOfClass:[NSData class]]) {
NSLog(@"data: %ld bytes", (unsigned long)[(NSData*)obj length]);
}
else {
NSLog(@"obj: %@", obj);
}
}
idx++;
[self bindObject:obj toColumn:idx inStatement:pStmt];
}
}
if (idx != queryCount) {
NSString *message = [NSString stringWithFormat:@"Error: the bind count (%d) is not correct for the # of variables in the query (%d) (%@) (executeUpdate)", idx, queryCount, sql];
if (_logsErrors) {
NSLog(@"%@", message);
}
if (outErr) {
*outErr = [self errorWithMessage:message];
}
sqlite3_finalize(pStmt);
_isExecutingStatement = NO;
return NO;
}
/* Call sqlite3_step() to run the virtual machine. Since the SQL being
** executed is not a SELECT statement, we assume no data will be returned.
*/
rc = sqlite3_step(pStmt);
if (SQLITE_DONE == rc) {
// all is well, let's return.
}
else if (SQLITE_INTERRUPT == rc) {
if (_logsErrors) {
NSLog(@"Error calling sqlite3_step. Query was interrupted (%d: %s) SQLITE_INTERRUPT", rc, sqlite3_errmsg(_db));
NSLog(@"DB Query: %@", sql);
}
}
else if (rc == SQLITE_ROW) {
NSString *message = [NSString stringWithFormat:@"A executeUpdate is being called with a query string '%@'", sql];
if (_logsErrors) {
NSLog(@"%@", message);
NSLog(@"DB Query: %@", sql);
}
if (outErr) {
*outErr = [self errorWithMessage:message];
}
}
else {
if (outErr) {
*outErr = [self errorWithMessage:[NSString stringWithUTF8String:sqlite3_errmsg(_db)]];
}
if (SQLITE_ERROR == rc) {
if (_logsErrors) {
NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db));
NSLog(@"DB Query: %@", sql);
}
}
else if (SQLITE_MISUSE == rc) {
// uh oh.
if (_logsErrors) {
NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db));
NSLog(@"DB Query: %@", sql);
}
}
else {
// wtf?
if (_logsErrors) {
NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db));
NSLog(@"DB Query: %@", sql);
}
}
}
if (_shouldCacheStatements && !cachedStmt) {
cachedStmt = [[FMStatement alloc] init];
[cachedStmt setStatement:pStmt];
[self setCachedStatement:cachedStmt forQuery:sql];
FMDBRelease(cachedStmt);
}
int closeErrorCode;
if (cachedStmt) {
[cachedStmt setUseCount:[cachedStmt useCount] + 1];
closeErrorCode = sqlite3_reset(pStmt);
}
else {
/* Finalize the virtual machine. This releases all memory and other
** resources allocated by the sqlite3_prepare() call above.
*/
closeErrorCode = sqlite3_finalize(pStmt);
}
if (closeErrorCode != SQLITE_OK) {
if (_logsErrors) {
NSLog(@"Unknown error finalizing or resetting statement (%d: %s)", closeErrorCode, sqlite3_errmsg(_db));
NSLog(@"DB Query: %@", sql);
}
}
_isExecutingStatement = NO;
return (rc == SQLITE_DONE || rc == SQLITE_OK);
}
.....
//根据存储的数据类型调用对应的方法存储
- (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt {
if ((!obj) || ((NSNull *)obj == [NSNull null])) {
sqlite3_bind_null(pStmt, idx);
}
// FIXME - someday check the return codes on these binds.
else if ([obj isKindOfClass:[NSData class]]) {
const void *bytes = [obj bytes];
if (!bytes) {
// it's an empty NSData object, aka [NSData data].
// Don't pass a NULL pointer, or sqlite will bind a SQL null instead of a blob.
bytes = "";
}
sqlite3_bind_blob(pStmt, idx, bytes, (int)[obj length], SQLITE_STATIC);
}
else if ([obj isKindOfClass:[NSDate class]]) {
if (self.hasDateFormatter)
sqlite3_bind_text(pStmt, idx, [[self stringFromDate:obj] UTF8String], -1, SQLITE_STATIC);
else
sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]);
}
else if ([obj isKindOfClass:[NSNumber class]]) {
if (strcmp([obj objCType], @encode(char)) == 0) {
sqlite3_bind_int(pStmt, idx, [obj charValue]);
}
else if (strcmp([obj objCType], @encode(unsigned char)) == 0) {
sqlite3_bind_int(pStmt, idx, [obj unsignedCharValue]);
}
else if (strcmp([obj objCType], @encode(short)) == 0) {
sqlite3_bind_int(pStmt, idx, [obj shortValue]);
}
else if (strcmp([obj objCType], @encode(unsigned short)) == 0) {
sqlite3_bind_int(pStmt, idx, [obj unsignedShortValue]);
}
else if (strcmp([obj objCType], @encode(int)) == 0) {
sqlite3_bind_int(pStmt, idx, [obj intValue]);
}
else if (strcmp([obj objCType], @encode(unsigned int)) == 0) {
sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedIntValue]);
}
else if (strcmp([obj objCType], @encode(long)) == 0) {
sqlite3_bind_int64(pStmt, idx, [obj longValue]);
}
else if (strcmp([obj objCType], @encode(unsigned long)) == 0) {
sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedLongValue]);
}
else if (strcmp([obj objCType], @encode(long long)) == 0) {
sqlite3_bind_int64(pStmt, idx, [obj longLongValue]);
}
else if (strcmp([obj objCType], @encode(unsigned long long)) == 0) {
sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedLongLongValue]);
}
else if (strcmp([obj objCType], @encode(float)) == 0) {
sqlite3_bind_double(pStmt, idx, [obj floatValue]);
}
else if (strcmp([obj objCType], @encode(double)) == 0) {
sqlite3_bind_double(pStmt, idx, [obj doubleValue]);
}
else if (strcmp([obj objCType], @encode(BOOL)) == 0) {
sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0));
}
else {
sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
}
}
else {
sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
}
}
最总,会走到- (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt
方法根据存储的数据类型sqlite3调用对应的方法存储。
存储的数据类型:
NSNull:sqlite3_bind_null
NSData:sqlite3_bind_blob
NSNumber:
sqlite3_bind_double
sqlite3_bind_int
sqlite3_bind_int64
NSString:
sqlite3_bind_text
注意:存储日期时有两种存储方式
1.传入了日期格式,则字符串存储
2.时间戳存储
查询
- (FMResultSet *)executeQuery:(NSString*)sql, ... {
va_list args;
va_start(args, sql);
id result = [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args];
va_end(args);
return result;
}
......
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
if (![self databaseExists]) {
return 0x00;
}
if (_isExecutingStatement) {
[self warnInUse];
return 0x00;
}
_isExecutingStatement = YES;
int rc = 0x00;
sqlite3_stmt *pStmt = 0x00;
FMStatement *statement = 0x00;
FMResultSet *rs = 0x00;
if (_traceExecution && sql) {
NSLog(@"%@ executeQuery: %@", self, sql);
}
if (_shouldCacheStatements) {
statement = [self cachedStatementForQuery:sql];
pStmt = statement ? [statement statement] : 0x00;
[statement reset];
}
if (!pStmt) {
//sqlite3_prepare将sql语句转换成sqlite3_stmt类型的句柄,用于执行。
rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
if (SQLITE_OK != rc) {
if (_logsErrors) {
NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
NSLog(@"DB Query: %@", sql);
NSLog(@"DB Path: %@", _databasePath);
}
if (_crashOnErrors) {
NSAssert(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
abort();
}
// 释放sqlite3_stmt对象,避免内存泄漏。
sqlite3_finalize(pStmt);
_isExecutingStatement = NO;
return nil;
}
}
id obj;
int idx = 0;
int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)
// If dictionaryArgs is passed in, that means we are using sqlite's named parameter support
if (dictionaryArgs) {
for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
// Prefix the key with a colon.
NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
if (_traceExecution) {
NSLog(@"%@ = %@", parameterName, [dictionaryArgs objectForKey:dictionaryKey]);
}
// Get the index for the parameter name.
int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
FMDBRelease(parameterName);
if (namedIdx > 0) {
// Standard binding from here.
[self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
// increment the binding count, so our check below works out
idx++;
}
else {
NSLog(@"Could not find index for %@", dictionaryKey);
}
}
}
else {
while (idx < queryCount) {
if (arrayArgs && idx < (int)[arrayArgs count]) {
obj = [arrayArgs objectAtIndex:(NSUInteger)idx];
}
else if (args) {
obj = va_arg(args, id);
}
else {
//We ran out of arguments
break;
}
if (_traceExecution) {
if ([obj isKindOfClass:[NSData class]]) {
NSLog(@"data: %ld bytes", (unsigned long)[(NSData*)obj length]);
}
else {
NSLog(@"obj: %@", obj);
}
}
idx++;
[self bindObject:obj toColumn:idx inStatement:pStmt];
}
}
if (idx != queryCount) {
NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
sqlite3_finalize(pStmt);
_isExecutingStatement = NO;
return nil;
}
FMDBRetain(statement); // to balance the release below
if (!statement) {
statement = [[FMStatement alloc] init];
//存储sqlite3_stmt类型的句柄
[statement setStatement:pStmt];
if (_shouldCacheStatements && sql) {
[self setCachedStatement:statement forQuery:sql];
}
}
// the statement gets closed in rs's dealloc or [rs close];
rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self];
[rs setQuery:sql];
NSValue *openResultSet = [NSValue valueWithNonretainedObject:rs];
[_openResultSets addObject:openResultSet];
[statement setUseCount:[statement useCount] + 1];
FMDBRelease(statement);
_isExecutingStatement = NO;
return rs;
}
FMResultSet
初始化、关闭
+ (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB {
FMResultSet *rs = [[FMResultSet alloc] init];
[rs setStatement:statement];
[rs setParentDB:aDB];
NSParameterAssert(![statement inUse]);
[statement setInUse:YES]; // weak reference
return FMDBReturnAutoreleased(rs);
}
#if ! __has_feature(objc_arc)
- (void)finalize {
[self close];
[super finalize];
}
#endif
- (void)dealloc {
[self close];
FMDBRelease(_query);
_query = nil;
FMDBRelease(_columnNameToIndexMap);
_columnNameToIndexMap = nil;
#if ! __has_feature(objc_arc)
[super dealloc];
#endif
}
- (void)close {
[_statement reset];
FMDBRelease(_statement);
_statement = nil;
// we don't need this anymore... (i think)
//[_parentDB setInUse:NO];
[_parentDB resultSetDidClose:self];
[self setParentDB:nil];
}
创建时传入所在的FMDatabase
对象,在关闭时调用FMDatabase
对象移除对本身的存储。
查询
- (BOOL)next {
return [self nextWithError:nil];
}
- (BOOL)nextWithError:(NSError **)outErr {
//执行实例化为sqlite3_stmt类型的SQL语句。
int rc = sqlite3_step([_statement statement]);
if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [_parentDB databasePath]);
NSLog(@"Database busy");
if (outErr) {
*outErr = [_parentDB lastError];
}
}
else if (SQLITE_DONE == rc || SQLITE_ROW == rc) {
// all is well, let's return.
}
else if (SQLITE_ERROR == rc) {
NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle]));
if (outErr) {
*outErr = [_parentDB lastError];
}
}
else if (SQLITE_MISUSE == rc) {
// uh oh.
NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle]));
if (outErr) {
if (_parentDB) {
*outErr = [_parentDB lastError];
}
else {
// If 'next' or 'nextWithError' is called after the result set is closed,
// we need to return the appropriate error.
NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:@"parentDB does not exist" forKey:NSLocalizedDescriptionKey];
*outErr = [NSError errorWithDomain:@"FMDatabase" code:SQLITE_MISUSE userInfo:errorMessage];
}
}
}
else {
// wtf?
NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle]));
if (outErr) {
*outErr = [_parentDB lastError];
}
}
if (rc != SQLITE_ROW) {
[self close];
}
return (rc == SQLITE_ROW);
}
类型取值
- (NSString*)stringForColumn:(NSString*)columnName;
.....
参考文档:
SQLite——step操作