FMDatabaseQueue.swift 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import Foundation
  2. import SQLite3
  3. public class FMDatabaseQueue : NSObject {
  4. enum FMDBTransaction : Int32 {
  5. case FMDBTransactionExclusive = 1,
  6. FMDBTransactionDeferred = 2,
  7. FMDBTransactionImmediate = 3
  8. }
  9. var _databasePath : String?
  10. var _openFlags : Int32 = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE
  11. var _vfsName : String?
  12. private var _db : FMDatabase?
  13. static var savePointIdx : CUnsignedLong = 0
  14. public let queue = DispatchQueue(label: "fmdb.\(String(describing: self))")
  15. private let queueKey = DispatchSpecificKey<FMDatabaseQueue>()
  16. public override init() {
  17. super.init()
  18. queue.setSpecific(key:queueKey, value:self)
  19. print(queue)
  20. }
  21. deinit {
  22. if let _db = _db {
  23. queue.sync {
  24. if (!_db.close()) {
  25. print("Could not close database")
  26. }
  27. }
  28. }
  29. }
  30. public func interrupt() {
  31. _db?.interrupt()
  32. }
  33. // FIXME: What would the swift version of this be?
  34. /*
  35. + (Class)databaseClass {
  36. return [FMDatabase class];
  37. }
  38. */
  39. static func queue(with filePath : String) -> FMDatabaseQueue {
  40. let q = FMDatabaseQueue()
  41. q._databasePath = filePath
  42. return q
  43. }
  44. static func queue(with fileURL : URL) -> FMDatabaseQueue {
  45. return FMDatabaseQueue.queue(with: fileURL.path)
  46. }
  47. func database() -> FMDatabase? {
  48. if (_db == nil || !(_db!._isOpen)) {
  49. if (_db == nil) {
  50. _db = FMDatabase.database(with: _databasePath!)
  51. }
  52. do {
  53. try _db?.openWithFlags(flags: _openFlags, vfsName: _vfsName)
  54. }
  55. catch {
  56. print(error)
  57. abort()
  58. }
  59. }
  60. return _db
  61. }
  62. func inDatabase(_ f: (FMDatabase) -> ()) {
  63. /* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue
  64. * and then check it against self to make sure we're not about to deadlock. */
  65. let currentSyncQueue = queue.getSpecific(key: queueKey)
  66. assert(currentSyncQueue != self, "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock")
  67. queue.sync {
  68. let db = database()
  69. f(db!)
  70. if (db!.hasOpenResultSets()) {
  71. print("Warning: there is at least one open result set around after performing FMDatabaseQueue.inDatabase()")
  72. }
  73. }
  74. }
  75. func beginTransaction(transaction: FMDBTransaction, f: (FMDatabase, inout Bool) -> ()) {
  76. queue.sync {
  77. if let db = database() {
  78. var shouldRollback = false
  79. do {
  80. switch (transaction) {
  81. case .FMDBTransactionExclusive:
  82. try db.beginExclusiveTransaction()
  83. break
  84. case .FMDBTransactionDeferred:
  85. try db.beginDeferredTransaction()
  86. break
  87. case .FMDBTransactionImmediate:
  88. try db.beginImmediateTransaction()
  89. break
  90. }
  91. f(db, &shouldRollback)
  92. if (shouldRollback) {
  93. try db.rollback();
  94. }
  95. else {
  96. try db.commit();
  97. }
  98. }
  99. catch {
  100. print(error)
  101. }
  102. }
  103. }
  104. }
  105. func inTransaction(_ f: (FMDatabase, inout Bool) -> ()) {
  106. beginTransaction(transaction: .FMDBTransactionExclusive, f: f)
  107. }
  108. func inDeferredTransaction(_ f: (FMDatabase, inout Bool) -> ()) {
  109. beginTransaction(transaction: .FMDBTransactionDeferred, f: f)
  110. }
  111. func inExclusiveTransaction(_ f: (FMDatabase, inout Bool) -> ()) {
  112. beginTransaction(transaction: .FMDBTransactionExclusive, f: f)
  113. }
  114. func inImmediateTransaction(_ f: (FMDatabase, inout Bool) -> ()) {
  115. beginTransaction(transaction: .FMDBTransactionImmediate, f: f)
  116. }
  117. func inSavePoint(_ f: (FMDatabase, inout Bool) -> ()) {
  118. queue.sync {
  119. #warning("Need to implement startSavePointWithName in FMDatabase before the Queue can do inSavePoint:")
  120. FMDatabaseQueue.savePointIdx = FMDatabaseQueue.savePointIdx + 1
  121. // let name = "savePoint\(FMDatabaseQueue.savePointIdx)"
  122. // var shouldRollback = false
  123. //
  124. }
  125. }
  126. /*
  127. - (NSError*)inSavePoint:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block {
  128. #if SQLITE_VERSION_NUMBER >= 3007000
  129. static unsigned long savePointIdx = 0;
  130. __block NSError *err = 0x00;
  131. FMDBRetain(self);
  132. dispatch_sync(_queue, ^() {
  133. NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++];
  134. BOOL shouldRollback = NO;
  135. if ([[self database] startSavePointWithName:name error:&err]) {
  136. block([self database], &shouldRollback);
  137. if (shouldRollback) {
  138. // We need to rollback and release this savepoint to remove it
  139. [[self database] rollbackToSavePointWithName:name error:&err];
  140. }
  141. [[self database] releaseSavePointWithName:name error:&err];
  142. }
  143. });
  144. FMDBRelease(self);
  145. return err;
  146. #else
  147. NSString *errorMessage = NSLocalizedStringFromTable(@"Save point functions require SQLite 3.7", @"FMDB", nil);
  148. if (_db.logsErrors) NSLog(@"%@", errorMessage);
  149. return [NSError errorWithDomain:@"FMDatabase" code:0 userInfo:@{NSLocalizedDescriptionKey : errorMessage}];
  150. #endif
  151. }
  152. - (BOOL)checkpoint:(FMDBCheckpointMode)mode error:(NSError * __autoreleasing *)error
  153. {
  154. return [self checkpoint:mode name:nil logFrameCount:NULL checkpointCount:NULL error:error];
  155. }
  156. - (BOOL)checkpoint:(FMDBCheckpointMode)mode name:(NSString *)name error:(NSError * __autoreleasing *)error
  157. {
  158. return [self checkpoint:mode name:name logFrameCount:NULL checkpointCount:NULL error:error];
  159. }
  160. - (BOOL)checkpoint:(FMDBCheckpointMode)mode name:(NSString *)name logFrameCount:(int * _Nullable)logFrameCount checkpointCount:(int * _Nullable)checkpointCount error:(NSError * __autoreleasing _Nullable * _Nullable)error
  161. {
  162. __block BOOL result;
  163. FMDBRetain(self);
  164. dispatch_sync(_queue, ^() {
  165. result = [self.database checkpoint:mode name:name logFrameCount:logFrameCount checkpointCount:checkpointCount error:error];
  166. });
  167. FMDBRelease(self);
  168. return result;
  169. }
  170. */
  171. }