Browse Source

Added a new class that works as a queue for queries and updates, instead of a pool- which you can still get deadlocked on if you aren't careful.

ccgus 14 years ago
parent
commit
c0160b3569
3 changed files with 212 additions and 2 deletions
  1. 34 0
      src/FMDatabaseQueue.h
  2. 118 0
      src/FMDatabaseQueue.m
  3. 60 2
      src/fmdb.m

+ 34 - 0
src/FMDatabaseQueue.h

@@ -0,0 +1,34 @@
+//
+//  FMDatabasePool.h
+//  fmdb
+//
+//  Created by August Mueller on 6/22/11.
+//  Copyright 2011 Flying Meat Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "sqlite3.h"
+
+@class FMDatabase;
+
+@interface FMDatabaseQueue : NSObject {
+    dispatch_queue_t    _queue;
+    FMDatabase          *_db;
+}
+
++ (id)databaseQueueWithPath:(NSString*)aPath;
+- (id)initWithPath:(NSString*)aPath;
+
+- (void)inDatabase:(void (^)(FMDatabase *db))block;
+
+- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;
+- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;
+
+#if SQLITE_VERSION_NUMBER >= 3007000
+// 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;
+#endif
+
+@end
+

+ 118 - 0
src/FMDatabaseQueue.m

@@ -0,0 +1,118 @@
+//
+//  FMDatabasePool.m
+//  fmdb
+//
+//  Created by August Mueller on 6/22/11.
+//  Copyright 2011 Flying Meat Inc. All rights reserved.
+//
+
+#import "FMDatabaseQueue.h"
+#import "FMDatabase.h"
+
+@implementation FMDatabaseQueue
+
++ (id)databaseQueueWithPath:(NSString*)aPath {
+    return [[[self alloc] initWithPath:aPath] autorelease];
+}
+
+- (id)initWithPath:(NSString*)aPath {
+	
+    self = [super init];
+    
+	if (self != nil) {
+        
+        _db = [[FMDatabase databaseWithPath:aPath] retain];
+        
+        if (![_db open]) {
+            NSLog(@"Could not create database queue for path %@", aPath);
+            [self release];
+            return 0x00;
+        }
+        
+        _queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
+	}
+    
+	return self;
+}
+
+- (void)dealloc {
+    
+    [_db release];
+    
+    if (_queue) {
+        dispatch_release(_queue);
+        _queue = 0x00;
+    }
+    
+    [super dealloc];
+}
+
+- (void)inDatabase:(void (^)(FMDatabase *db))block {
+    
+    dispatch_sync(_queue, ^() { block(_db); });
+}
+
+- (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block {
+    
+    dispatch_sync(_queue, ^() { 
+        
+        BOOL shouldRollback = NO;
+        
+        if (useDeferred) {
+            [_db beginDeferredTransaction];
+        }
+        else {
+            [_db beginTransaction];
+        }
+        
+        block(_db, &shouldRollback);
+        
+        if (shouldRollback) {
+            [_db rollback];
+        }
+        else {
+            [_db commit];
+        }
+    
+    });
+}
+
+- (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;
+    
+    dispatch_sync(_queue, ^() { 
+        
+        NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++];
+        
+        BOOL shouldRollback = NO;
+        
+        if ([_db startSavePointWithName:name error:&err]) {
+            
+            block(_db, &shouldRollback);
+            
+            if (shouldRollback) {
+                [_db rollbackToSavePointWithName:name error:&err];
+            }
+            else {
+                [_db releaseSavePointWithName:name error:&err];
+            }
+            
+        }
+    });
+    
+    return err;
+}
+#endif
+
+@end

+ 60 - 2
src/fmdb.m

@@ -2,6 +2,7 @@
 #import "FMDatabase.h"
 #import "FMDatabaseAdditions.h"
 #import "FMDatabasePool.h"
+#import "FMDatabaseQueue.h"
 
 #define FMDBQuickCheck(SomeBool) { if (!(SomeBool)) { NSLog(@"Failure on line %d", __LINE__); abort(); } }
 
@@ -901,13 +902,70 @@ int main (int argc, const char * argv[]) {
         
     }
     
+    NSLog(@"That was version %@ of sqlite", [FMDatabase sqliteLibVersion]);
     
+    [pool release];
     
     
+    FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
+    
+    FMDBQuickCheck(queue);
+    
+    {
+        
+        [queue inDatabase:^(FMDatabase *db) {
+            
+            
+            int count = 0;
+            FMResultSet *rsl = [db executeQuery:@"select * from likefoo where foo like 'h%'"];
+            while ([rsl next]) {
+                count++;
+            }
+            
+            FMDBQuickCheck(count == 2);
+            
+            count = 0;
+            rsl = [db executeQuery:@"select * from likefoo where foo like ?", @"h%"];
+            while ([rsl next]) {
+                count++;
+            }
+            
+            FMDBQuickCheck(count == 2);
+        }];
+        
+    }
+    
+    
+    {
+        
+        int ops = 16;
+        
+        dispatch_queue_t dqueue = dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_HIGH);
+        
+        dispatch_apply(ops, dqueue, ^(size_t nby) {
+            
+            // just mix things up a bit for demonstration purposes.
+            if (nby % 2 == 1) {
+                [NSThread sleepForTimeInterval:.1];
+            }
+            
+            if (nby % 3 == 1) {
+                [NSThread sleepForTimeInterval:.1];
+            }
+            
+            [queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
+                NSLog(@"Starting %ld", nby);
+                [db executeUpdate:@"insert into likefoo values ('1')"];
+                [db executeUpdate:@"insert into likefoo values ('2')"];
+                [db executeUpdate:@"insert into likefoo values ('3')"];
+                NSLog(@"Ending   %ld", nby);
+            }];
+        });
+        
+    }
+    
     
-    NSLog(@"That was version %@ of sqlite", [FMDatabase sqliteLibVersion]);
     
-    [pool release];
     
     return 0;
 }