FMDatabasePool.m 7.5 KB

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