FMDatabasePool.m 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. //
  2. // FMDatabasePool.m
  3. // fmdb
  4. //
  5. // Created by August Mueller on 6/22/11.
  6. // Copyright 2011 Flying Meat Inc. All rights reserved.
  7. //
  8. #import "FMDatabasePool.h"
  9. #import "FMDatabase.h"
  10. @interface FMDatabasePool()
  11. - (void)pushDatabaseBackInPool:(FMDatabase*)db;
  12. - (FMDatabase*)db;
  13. @end
  14. @implementation FMDatabasePool
  15. @synthesize path=_path;
  16. @synthesize delegate=_delegate;
  17. @synthesize maximumNumberOfDatabasesToCreate=_maximumNumberOfDatabasesToCreate;
  18. @synthesize openFlags=_openFlags;
  19. + (instancetype)databasePoolWithPath:(NSString*)aPath {
  20. return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]);
  21. }
  22. + (instancetype)databasePoolWithPath:(NSString*)aPath flags:(int)openFlags {
  23. return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath flags:openFlags]);
  24. }
  25. - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags {
  26. self = [super init];
  27. if (self != nil) {
  28. _path = [aPath copy];
  29. _lockQueue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
  30. _databaseInPool = FMDBReturnRetained([NSMutableArray array]);
  31. _databaseOutPool = FMDBReturnRetained([NSMutableArray array]);
  32. _openFlags = openFlags;
  33. }
  34. return self;
  35. }
  36. - (instancetype)initWithPath:(NSString*)aPath
  37. {
  38. // default flags for sqlite3_open
  39. return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE];
  40. }
  41. - (instancetype)init {
  42. return [self initWithPath:nil];
  43. }
  44. - (void)dealloc {
  45. _delegate = 0x00;
  46. FMDBRelease(_path);
  47. FMDBRelease(_databaseInPool);
  48. FMDBRelease(_databaseOutPool);
  49. if (_lockQueue) {
  50. FMDBDispatchQueueRelease(_lockQueue);
  51. _lockQueue = 0x00;
  52. }
  53. #if ! __has_feature(objc_arc)
  54. [super dealloc];
  55. #endif
  56. }
  57. - (void)executeLocked:(void (^)(void))aBlock {
  58. dispatch_sync(_lockQueue, aBlock);
  59. }
  60. - (void)pushDatabaseBackInPool:(FMDatabase*)db {
  61. if (!db) { // db can be null if we set an upper bound on the # of databases to create.
  62. return;
  63. }
  64. [self executeLocked:^() {
  65. if ([self->_databaseInPool containsObject:db]) {
  66. [[NSException exceptionWithName:@"Database already in pool" reason:@"The FMDatabase being put back into the pool is already present in the pool" userInfo:nil] raise];
  67. }
  68. [self->_databaseInPool addObject:db];
  69. [self->_databaseOutPool removeObject:db];
  70. }];
  71. }
  72. - (FMDatabase*)db {
  73. __block FMDatabase *db;
  74. [self executeLocked:^() {
  75. db = [self->_databaseInPool lastObject];
  76. BOOL shouldNotifyDelegate = NO;
  77. if (db) {
  78. [self->_databaseOutPool addObject:db];
  79. [self->_databaseInPool removeLastObject];
  80. }
  81. else {
  82. if (self->_maximumNumberOfDatabasesToCreate) {
  83. NSUInteger currentCount = [self->_databaseOutPool count] + [self->_databaseInPool count];
  84. if (currentCount >= self->_maximumNumberOfDatabasesToCreate) {
  85. NSLog(@"Maximum number of databases (%ld) has already been reached!", (long)currentCount);
  86. return;
  87. }
  88. }
  89. db = [FMDatabase databaseWithPath:self->_path];
  90. shouldNotifyDelegate = YES;
  91. }
  92. //This ensures that the db is opened before returning
  93. #if SQLITE_VERSION_NUMBER >= 3005000
  94. BOOL success = [db openWithFlags:self->_openFlags];
  95. #else
  96. BOOL success = [db open];
  97. #endif
  98. if (success) {
  99. if ([self->_delegate respondsToSelector:@selector(databasePool:shouldAddDatabaseToPool:)] && ![self->_delegate databasePool:self shouldAddDatabaseToPool:db]) {
  100. [db close];
  101. db = 0x00;
  102. }
  103. else {
  104. //It should not get added in the pool twice if lastObject was found
  105. if (![self->_databaseOutPool containsObject:db]) {
  106. [self->_databaseOutPool addObject:db];
  107. if (shouldNotifyDelegate && [self->_delegate respondsToSelector:@selector(databasePool:didAddDatabase:)]) {
  108. [self->_delegate databasePool:self didAddDatabase:db];
  109. }
  110. }
  111. }
  112. }
  113. else {
  114. NSLog(@"Could not open up the database at path %@", self->_path);
  115. db = 0x00;
  116. }
  117. }];
  118. return db;
  119. }
  120. - (NSUInteger)countOfCheckedInDatabases {
  121. __block NSUInteger count;
  122. [self executeLocked:^() {
  123. count = [self->_databaseInPool count];
  124. }];
  125. return count;
  126. }
  127. - (NSUInteger)countOfCheckedOutDatabases {
  128. __block NSUInteger count;
  129. [self executeLocked:^() {
  130. count = [self->_databaseOutPool count];
  131. }];
  132. return count;
  133. }
  134. - (NSUInteger)countOfOpenDatabases {
  135. __block NSUInteger count;
  136. [self executeLocked:^() {
  137. count = [self->_databaseOutPool count] + [self->_databaseInPool count];
  138. }];
  139. return count;
  140. }
  141. - (void)releaseAllDatabases {
  142. [self executeLocked:^() {
  143. [self->_databaseOutPool removeAllObjects];
  144. [self->_databaseInPool removeAllObjects];
  145. }];
  146. }
  147. - (void)inDatabase:(void (^)(FMDatabase *db))block {
  148. FMDatabase *db = [self db];
  149. block(db);
  150. [self pushDatabaseBackInPool:db];
  151. }
  152. - (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block {
  153. BOOL shouldRollback = NO;
  154. FMDatabase *db = [self db];
  155. if (useDeferred) {
  156. [db beginDeferredTransaction];
  157. }
  158. else {
  159. [db beginTransaction];
  160. }
  161. block(db, &shouldRollback);
  162. if (shouldRollback) {
  163. [db rollback];
  164. }
  165. else {
  166. [db commit];
  167. }
  168. [self pushDatabaseBackInPool:db];
  169. }
  170. - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
  171. [self beginTransaction:YES withBlock:block];
  172. }
  173. - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
  174. [self beginTransaction:NO withBlock:block];
  175. }
  176. #if SQLITE_VERSION_NUMBER >= 3007000
  177. - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block {
  178. static unsigned long savePointIdx = 0;
  179. NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++];
  180. BOOL shouldRollback = NO;
  181. FMDatabase *db = [self db];
  182. NSError *err = 0x00;
  183. if (![db startSavePointWithName:name error:&err]) {
  184. [self pushDatabaseBackInPool:db];
  185. return err;
  186. }
  187. block(db, &shouldRollback);
  188. if (shouldRollback) {
  189. // We need to rollback and release this savepoint to remove it
  190. [db rollbackToSavePointWithName:name error:&err];
  191. }
  192. [db releaseSavePointWithName:name error:&err];
  193. [self pushDatabaseBackInPool:db];
  194. return err;
  195. }
  196. #endif
  197. @end