Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Tests/FMDatabaseQueueTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
26 changes: 26 additions & 0 deletions src/fmdb/FMDatabaseQueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,20 +141,44 @@

- (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`
*/

- (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`
*/

- (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
///-----------------------------------------------
Expand All @@ -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
Expand Down
72 changes: 62 additions & 10 deletions src/fmdb/FMDatabaseQueue.m
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,18 @@ - (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);
assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock");

FMDBRetain(self);

dispatch_sync(_queue, ^() {
dispatch_block_t dispatchBlock = ^() {

FMDatabase *db = [self database];
block(db);
Expand All @@ -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;

Expand All @@ -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++];

Expand All @@ -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

Expand Down