123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- //
- // FMDatabaseQueue.m
- // fmdb
- //
- // Created by August Mueller on 6/22/11.
- // Copyright 2011 Flying Meat Inc. All rights reserved.
- //
- #import "FMDatabaseQueue.h"
- #import "FMDatabase.h"
- /*
-
- Note: we call [self retain]; before using dispatch_sync, just incase
- FMDatabaseQueue is released on another thread and we're in the middle of doing
- something in dispatch_sync
-
- */
- /*
- * A key used to associate the FMDatabaseQueue object with the dispatch_queue_t it uses.
- * This in turn is used for deadlock detection by seeing if inDatabase: is called on
- * the queue's dispatch queue, which should not happen and causes a deadlock.
- */
- static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey;
-
- @implementation FMDatabaseQueue
- @synthesize path = _path;
- @synthesize openFlags = _openFlags;
- + (instancetype)databaseQueueWithPath:(NSString*)aPath {
-
- FMDatabaseQueue *q = [[self alloc] initWithPath:aPath];
-
- FMDBAutorelease(q);
-
- return q;
- }
- + (instancetype)databaseQueueWithPath:(NSString*)aPath flags:(int)openFlags {
-
- FMDatabaseQueue *q = [[self alloc] initWithPath:aPath flags:openFlags];
-
- FMDBAutorelease(q);
-
- return q;
- }
- + (Class)databaseClass {
- return [FMDatabase class];
- }
- - (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;
- }
-
- return self;
- }
- - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags {
- return [self initWithPath:aPath flags:openFlags vfs:nil];
- }
- - (instancetype)initWithPath:(NSString*)aPath {
-
- // default flags for sqlite3_open
- return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:nil];
- }
- - (instancetype)init {
- return [self initWithPath:nil];
- }
-
- - (void)dealloc {
-
- FMDBRelease(_db);
- FMDBRelease(_path);
-
- 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*)database {
- if (!_db) {
- _db = FMDBReturnRetained([FMDatabase databaseWithPath:_path]);
-
- #if SQLITE_VERSION_NUMBER >= 3005000
- BOOL success = [_db openWithFlags:_openFlags];
- #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:(void (^)(FMDatabase *db))block {
- /* 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");
-
- 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:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block {
- FMDBRetain(self);
- dispatch_sync(_queue, ^() {
-
- BOOL shouldRollback = NO;
-
- if (useDeferred) {
- [[self database] beginDeferredTransaction];
- }
- else {
- [[self database] beginTransaction];
- }
-
- block([self database], &shouldRollback);
-
- if (shouldRollback) {
- [[self database] rollback];
- }
- else {
- [[self database] commit];
- }
- });
-
- FMDBRelease(self);
- }
- - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
- [self beginTransaction:YES withBlock:block];
- }
- - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
- [self beginTransaction:NO withBlock:block];
- }
- #if SQLITE_VERSION_NUMBER >= 3007000
- - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block {
-
- static unsigned long savePointIdx = 0;
- __block NSError *err = 0x00;
- FMDBRetain(self);
- dispatch_sync(_queue, ^() {
-
- NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++];
-
- BOOL shouldRollback = NO;
-
- if ([[self database] startSavePointWithName:name error:&err]) {
-
- block([self database], &shouldRollback);
-
- if (shouldRollback) {
- // We need to rollback and release this savepoint to remove it
- [[self database] rollbackToSavePointWithName:name error:&err];
- }
- [[self database] releaseSavePointWithName:name error:&err];
-
- }
- });
- FMDBRelease(self);
- return err;
- }
- #endif
- @end
|