diff --git a/Tests/FMDatabaseQueueTests.m b/Tests/FMDatabaseQueueTests.m index cd7fb92f..b6c7c348 100644 --- a/Tests/FMDatabaseQueueTests.m +++ b/Tests/FMDatabaseQueueTests.m @@ -62,6 +62,33 @@ - (void)testQueueSelect }]; } +- (void)testAsync +{ + XCTestExpectation *queueExpectation = [self expectationWithDescription:@"Async inDatabase operation"]; + + [self.queue inDatabase:^(FMDatabase *adb) { + int count = 0; + FMResultSet *rsl = [adb executeQuery:@"select * from qfoo where foo like 'h%'"]; + while ([rsl next]) { + count++; + } + + XCTAssertEqual(count, 2); + + count = 0; + rsl = [adb executeQuery:@"select * from qfoo where foo like ?", @"h%"]; + while ([rsl next]) { + count++; + } + + XCTAssertEqual(count, 2); + + [queueExpectation fulfill]; + } async:YES]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + - (void)testReadOnlyQueue { FMDatabaseQueue *queue2 = [FMDatabaseQueue databaseQueueWithPath:self.databasePath flags:SQLITE_OPEN_READONLY]; diff --git a/src/fmdb/FMDatabaseQueue.h b/src/fmdb/FMDatabaseQueue.h index 34c0750d..113f3617 100644 --- a/src/fmdb/FMDatabaseQueue.h +++ b/src/fmdb/FMDatabaseQueue.h @@ -141,6 +141,14 @@ - (void)inDatabase:(void (^)(FMDatabase *db))block; +/** Perform database operations on queue. + + @param block The code to be run on the queue of `FMDatabaseQueue` + @param async When set to `YES`, this method will return immediately and the work will be done asynchronously. + */ + +- (void)inDatabase:(void (^)(FMDatabase *db))block async:(BOOL)async; + /** Synchronously perform database operations on queue, using transactions. @param block The code to be run on the queue of `FMDatabaseQueue` @@ -148,6 +156,14 @@ - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; +/** Perform database operations on queue, using transactions. + + @param block The code to be run on the queue of `FMDatabaseQueue` + @param async When set to `YES`, this method will return immediately and the work will be done asynchronously. + */ + +- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block async:(BOOL)async; + /** Synchronously perform database operations on queue, using deferred transactions. @param block The code to be run on the queue of `FMDatabaseQueue` @@ -155,6 +171,14 @@ - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; +/** Perform database operations on queue, using deferred transactions. + + @param block The code to be run on the queue of `FMDatabaseQueue` + @param async When set to `YES`, this method will return immediately and the work will be done asynchronously. + */ + +- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block async:(BOOL)async; + ///----------------------------------------------- /// @name Dispatching database operations to queue ///----------------------------------------------- @@ -168,6 +192,8 @@ // NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. // If you need to nest, use FMDatabase's startSavePointWithName:error: instead. - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; + +- (void)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block async:(BOOL)async completion:(void(^)(NSError *error))completion; #endif @end diff --git a/src/fmdb/FMDatabaseQueue.m b/src/fmdb/FMDatabaseQueue.m index ccf31fbf..52f09d1e 100644 --- a/src/fmdb/FMDatabaseQueue.m +++ b/src/fmdb/FMDatabaseQueue.m @@ -137,6 +137,10 @@ - (FMDatabase*)database { } - (void)inDatabase:(void (^)(FMDatabase *db))block { + [self inDatabase:block async:NO]; +} + +- (void)inDatabase:(void (^)(FMDatabase *db))block async:(BOOL)async { /* 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); @@ -144,7 +148,7 @@ - (void)inDatabase:(void (^)(FMDatabase *db))block { FMDBRetain(self); - dispatch_sync(_queue, ^() { + dispatch_block_t dispatchBlock = ^() { FMDatabase *db = [self database]; block(db); @@ -160,15 +164,22 @@ - (void)inDatabase:(void (^)(FMDatabase *db))block { } #endif } - }); + }; + + if (async) { + dispatch_async(_queue, dispatchBlock); + } else { + dispatch_sync(_queue, dispatchBlock); + } FMDBRelease(self); } -- (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { +- (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block async:(BOOL)async { FMDBRetain(self); - dispatch_sync(_queue, ^() { + + dispatch_block_t dispatchBlock = ^() { BOOL shouldRollback = NO; @@ -187,26 +198,56 @@ - (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, B else { [[self database] commit]; } - }); + }; + + if (async) { + dispatch_async(_queue, dispatchBlock); + } else { + dispatch_sync(_queue, dispatchBlock); + } FMDBRelease(self); } - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { - [self beginTransaction:YES withBlock:block]; + [self beginTransaction:YES withBlock:block async:NO]; +} + +- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block async:(BOOL)async { + [self beginTransaction:YES withBlock:block async:async]; } - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { - [self beginTransaction:NO withBlock:block]; + [self beginTransaction:NO withBlock:block async:NO]; +} + +- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block async:(BOOL)async { + [self beginTransaction:NO withBlock:block async:async]; } #if SQLITE_VERSION_NUMBER >= 3007000 - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { + __block NSError *err = 0x00; + + FMDBRetain(self); + + [self inSavePoint:block async:NO completion:^(NSError *error) { + err = error; + }]; + + FMDBRelease(self); + + return err; +} + +- (void)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block async:(BOOL)async completion:(void(^)(NSError *error))completion { + static unsigned long savePointIdx = 0; __block NSError *err = 0x00; FMDBRetain(self); - dispatch_sync(_queue, ^() { + + dispatch_block_t dispatchBlock = ^() { NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; @@ -223,9 +264,20 @@ - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { [[self database] releaseSavePointWithName:name error:&err]; } - }); + + if (async) { + completion(err); + } + }; + + if (async) { + dispatch_async(_queue, dispatchBlock); + } else { + dispatch_sync(_queue, dispatchBlock); + completion(err); + } + FMDBRelease(self); - return err; } #endif