FMDatabase.swift 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116
  1. import Foundation
  2. import SQLite3
  3. // Why the f doesn't this show up in the sqlite3.h headers for swift?
  4. let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
  5. #warning("Should we give this a better name? It's not very FMDB-like")
  6. enum SqliteValueType : Int32 {
  7. case SqliteValueTypeInteger = 1,
  8. SqliteValueTypeFloat = 2,
  9. SqliteValueTypeText = 3,
  10. SqliteValueTypeBlob = 4,
  11. SqliteValueTypeNull = 5
  12. }
  13. /*
  14. // These are from the sqlite3 headers.
  15. #define SQLITE_INTEGER 1
  16. #define SQLITE_FLOAT 2
  17. #define SQLITE3_TEXT 3
  18. #define SQLITE_BLOB 4
  19. #define SQLITE_NULL 5
  20. */
  21. enum FMDBError: Error {
  22. case sqlite3ErrorCode(Int32)
  23. }
  24. public class FMDatabase : NSObject {
  25. private var _db : OpaquePointer?
  26. var _databasePath : String?
  27. internal var _isOpen : Bool
  28. private var _maxBusyRetryTimeInterval : TimeInterval = 2
  29. private var _startBusyRetryTime : TimeInterval = 0
  30. public var logsErrors : Bool
  31. private var _shouldCacheStatements = false
  32. private var _openResultSets : Array<FMResultSet>
  33. private var _cachedStatements : Dictionary<String, Set<FMStatement>>?
  34. private var _isInTransaction = false
  35. private var _isExecutingStatement = false
  36. public var crashOnErrors = false
  37. public var traceExecution = true
  38. #warning("We need to port over all instances of _isExecutingStatement")
  39. public override init() {
  40. _isOpen = false
  41. _databasePath = ":memory:"
  42. _openResultSets = Array<FMResultSet>()
  43. logsErrors = true
  44. }
  45. static func isSQLiteThreadSafe() -> Bool {
  46. // make sure to read the sqlite headers on this guy!
  47. return sqlite3_threadsafe() != 0;
  48. }
  49. static func database(with filePath : String) -> FMDatabase {
  50. let db = FMDatabase()
  51. db._databasePath = filePath
  52. return db
  53. }
  54. static func database(with fileURL : URL) -> FMDatabase {
  55. return FMDatabase.database(with: fileURL.path)
  56. }
  57. func databaseURL() -> NSURL? {
  58. return (_databasePath != nil) ? NSURL(fileURLWithPath: _databasePath!) : nil
  59. //[NSURL fileURLWithPath:_databasePath] : nil;
  60. }
  61. // MARK: Cached statements
  62. public func clearCachedStatements() {
  63. if var unwrappedCachedStatements = _cachedStatements {
  64. for (_, statementSet) in unwrappedCachedStatements {
  65. for (statement) in statementSet {
  66. statement.close()
  67. }
  68. }
  69. unwrappedCachedStatements.removeAll()
  70. }
  71. }
  72. func cachedStatementForQuery(_ query : String) -> FMStatement? {
  73. if let uwCachedStatements = _cachedStatements {
  74. let setOfStatements = uwCachedStatements[query] // Dictionary<String, Set<FMStatement>>?
  75. let st = setOfStatements?.first(where: { st in
  76. !st._inUse
  77. })
  78. return st
  79. }
  80. return nil
  81. }
  82. func setCachedStatement(_ statement : FMStatement, forQuery: String) {
  83. statement._query = forQuery
  84. if var uwCachedStatements = _cachedStatements {
  85. var statements = uwCachedStatements[forQuery]
  86. if (statements == nil) {
  87. statements = Set<FMStatement>()
  88. uwCachedStatements[forQuery] = statements
  89. }
  90. statements?.insert(statement)
  91. }
  92. }
  93. func shouldCacheStatements() -> Bool {
  94. return _shouldCacheStatements
  95. }
  96. func setShouldCacheStatements(_ flag: Bool) {
  97. _shouldCacheStatements = flag
  98. if (_shouldCacheStatements && _cachedStatements != nil) {
  99. _cachedStatements = Dictionary<String, Set<FMStatement>>()
  100. }
  101. if (!_shouldCacheStatements) {
  102. _cachedStatements = nil
  103. }
  104. }
  105. public func hasOpenResultSets() -> Bool {
  106. return _openResultSets.count > 0
  107. }
  108. public func closeOpenResultSets() {
  109. for rs in (_openResultSets as NSArray as! [FMResultSet]) {
  110. rs.parentDB = nil // nil it out so close doesn't call resultSetDidClose
  111. rs.close()
  112. }
  113. _openResultSets.removeAll()
  114. }
  115. @discardableResult public func close() -> Bool {
  116. clearCachedStatements()
  117. closeOpenResultSets()
  118. var rc = SQLITE_ERROR
  119. var retry: Bool
  120. var triedFinalizingOpenStatements = false
  121. repeat {
  122. retry = false
  123. rc = sqlite3_close(_db)
  124. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  125. if (!triedFinalizingOpenStatements) {
  126. triedFinalizingOpenStatements = true;
  127. var pStmt : OpaquePointer?
  128. pStmt = sqlite3_next_stmt(_db, nil)
  129. while (pStmt != nil) {
  130. print("Closing leaked statement")
  131. sqlite3_finalize(pStmt);
  132. pStmt = sqlite3_next_stmt(_db, nil)
  133. retry = true;
  134. }
  135. }
  136. }
  137. else if (SQLITE_OK != rc) {
  138. print("error closing!: %d", rc)
  139. }
  140. }
  141. while (retry)
  142. _db = nil
  143. _isOpen = false
  144. return SQLITE_OK == rc
  145. }
  146. @discardableResult public func open() throws -> Bool {
  147. return try openWithFlags(flags: SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, vfsName: nil)
  148. }
  149. @discardableResult public func openWithFlags(flags : Int32, vfsName : String?) throws -> Bool {
  150. if (_isOpen) {
  151. return true
  152. }
  153. if (_db != nil) {
  154. close()
  155. }
  156. let err = sqlite3_open_v2(_databasePath, &_db, flags, vfsName)
  157. if (err != SQLITE_OK) {
  158. print("Error opening SQLite database: ", self.lastErrorMessage()!)
  159. throw FMDBError.sqlite3ErrorCode(err)
  160. }
  161. if (_maxBusyRetryTimeInterval > 0.0) {
  162. // set the handler
  163. setMaxBusyRetryTimeInterval(_maxBusyRetryTimeInterval);
  164. }
  165. _isOpen = true
  166. return true
  167. }
  168. func lastErrorMessage() -> String? {
  169. return String(utf8String: sqlite3_errmsg(_db))
  170. }
  171. func lastErrorCode() -> Int32 {
  172. return sqlite3_errcode(_db)
  173. }
  174. func lastExtendedErrorCode() -> Int32 {
  175. return sqlite3_extended_errcode(_db)
  176. }
  177. func error(withMessage: String) -> NSError {
  178. return NSError(
  179. domain: "FMDatabase",
  180. code: Int(sqlite3_errcode(_db)),
  181. userInfo: [
  182. NSLocalizedDescriptionKey: withMessage
  183. ]
  184. )
  185. }
  186. func lastError() -> NSError {
  187. return self.error(withMessage: lastErrorMessage() ?? "")
  188. }
  189. func hadError() -> Bool {
  190. let lastErrCode = lastErrorCode()
  191. return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW)
  192. }
  193. func databaseExists() ->Bool {
  194. if (!_isOpen) {
  195. print("The FMDatabase \(self) is not open.");
  196. if (crashOnErrors) {
  197. abort();
  198. }
  199. return false;
  200. }
  201. return true;
  202. }
  203. func internalBind(object: Any?, toColumn:Int32, statement: OpaquePointer?) -> Int32 {
  204. if object == nil || (object as? NSNull) != nil {
  205. return sqlite3_bind_null(statement, toColumn);
  206. }
  207. else if (object as? NSNull) != nil {
  208. return sqlite3_bind_null(statement, toColumn);
  209. }
  210. else if (object as? NSData) != nil {
  211. let d = object as? NSData
  212. let b = d?.bytes
  213. if (b == nil) {
  214. // it's an empty NSData object, aka [NSData data].
  215. // Don't pass a NULL pointer, or sqlite will bind a SQL null instead of a blob.
  216. // FIXME: This is pretty dumb.
  217. // This is just copied out of the swift book. I'm not sure what to pass here - but it can't be nil.
  218. let bytesPointer = UnsafeMutableRawPointer.allocate(byteCount: 4, alignment: 4)
  219. bytesPointer.storeBytes(of: 0xFFFF_FFFF, as: UInt32.self)
  220. return sqlite3_bind_blob(statement, toColumn, bytesPointer, 0, SQLITE_TRANSIENT);
  221. }
  222. return sqlite3_bind_blob(statement, toColumn, b, Int32(d?.length ?? 0), SQLITE_TRANSIENT);
  223. }
  224. else if let object = object as? String {
  225. return sqlite3_bind_text(statement, toColumn, object, -1, SQLITE_TRANSIENT)
  226. }
  227. else if let object = object as? Double {
  228. return sqlite3_bind_double(statement, toColumn, object)
  229. }
  230. else if let object = object as? Int64 {
  231. return sqlite3_bind_int64(statement, toColumn, object)
  232. }
  233. else if let object = object as? UInt64 {
  234. return sqlite3_bind_int64(statement, toColumn, Int64(truncatingIfNeeded:object))
  235. }
  236. else if let object = object as? Int32 {
  237. return sqlite3_bind_int(statement, toColumn, object)
  238. }
  239. else if let object = object as? UInt32 {
  240. return sqlite3_bind_int(statement, toColumn, Int32(object))
  241. }
  242. else if let object = object as? Int {
  243. return sqlite3_bind_int(statement, toColumn, Int32(object))
  244. }
  245. else if let object = object as? Bool {
  246. return sqlite3_bind_int(statement, toColumn, object ? 1 : 0)
  247. }
  248. else if let object = object as? Date {
  249. #warning("Need to check for a date formatter when binding")
  250. // if (self.hasDateFormatter)
  251. // return sqlite3_bind_text(pStmt, idx, [[self stringFromDate:obj] UTF8String], -1, SQLITE_TRANSIENT);
  252. // else
  253. return sqlite3_bind_double(statement, toColumn, object.timeIntervalSince1970);
  254. }
  255. print("unknown type: ", type(of: object))
  256. #warning("These types are all well and good, but FMDB supports much much more. Port them all over, Gus")
  257. return SQLITE_ERROR
  258. }
  259. func bind(statement: FMStatement, argArray: Array<Any?>?, argDict: Dictionary<String, Any?>?) throws -> Bool {
  260. var idx = Int(0)
  261. let queryCount = sqlite3_bind_parameter_count(statement._statement); // pointed out by Dominic Yu (thanks!)
  262. if let uwArgDict = argDict {
  263. for (k, obj) in uwArgDict {
  264. // Prefix the key with a colon.
  265. let parameterName = ":\(k)"
  266. // Get the index for the parameter name.
  267. let namedIdx = sqlite3_bind_parameter_index(statement._statement, parameterName);
  268. if (namedIdx > 0) {
  269. // Standard binding from here.
  270. if (traceExecution) {
  271. print("Index \(namedIdx), \(k) = \(obj ?? "nil")")
  272. }
  273. let rc = internalBind(object: obj, toColumn: Int32(namedIdx), statement: statement._statement ?? nil)
  274. if (rc != SQLITE_OK) {
  275. let errString = lastErrorMessage()
  276. print("Error: unable to bind (\(rc), \(errString ?? "")")
  277. // #define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */
  278. _isExecutingStatement = false
  279. throw FMDBError.sqlite3ErrorCode(rc)
  280. }
  281. // increment the binding count, so our check below works out
  282. idx = idx + 1;
  283. }
  284. else {
  285. print("Could not find index for \(k)");
  286. }
  287. }
  288. }
  289. else if let uwArgArray = argArray {
  290. while (idx < queryCount) {
  291. if (idx >= uwArgArray.count) {
  292. //We ran out of arguments
  293. break;
  294. }
  295. let obj = uwArgArray[idx]
  296. idx += 1
  297. let rc = internalBind(object: obj, toColumn: Int32(idx), statement: statement._statement ?? nil)
  298. if (rc != SQLITE_OK) {
  299. print("Error: unable to bind (\(rc), \(String(describing: sqlite3_errmsg(_db)))")
  300. _isExecutingStatement = false
  301. sqlite3_finalize(statement._statement);
  302. throw FMDBError.sqlite3ErrorCode(rc)
  303. }
  304. }
  305. }
  306. if (idx != queryCount) {
  307. print("Error: the bind count is not correct for the # of variables (executeQuery)");
  308. assert(false, "Error: the bind count is not correct for the # of variables (executeQuery)")
  309. // sqlite3_finalize(pStmt);
  310. // pStmt = 0x00;
  311. // _isExecutingStatement = NO;
  312. // return false;
  313. }
  314. return true
  315. }
  316. @discardableResult func executeQuery(_ sql : String, argArray : [Any?]?, argDictionary : Dictionary<String, Any?>?, shouldBind: Bool) throws -> FMResultSet {
  317. if (!databaseExists()) {
  318. throw FMDBError.sqlite3ErrorCode(-1)
  319. }
  320. if (_isExecutingStatement) {
  321. warnInUse();
  322. throw FMDBError.sqlite3ErrorCode(-1)
  323. }
  324. _isExecutingStatement = true
  325. let rc : Int32
  326. var pStmt : OpaquePointer?
  327. var statement : FMStatement?
  328. if (_shouldCacheStatements) {
  329. statement = cachedStatementForQuery(sql)
  330. pStmt = statement != nil ? statement?._statement : nil
  331. statement?.reset()
  332. }
  333. if (pStmt == nil) {
  334. rc = sqlite3_prepare_v2(_db, sql, -1, &pStmt, nil)
  335. if (SQLITE_OK != rc) {
  336. if (logsErrors) {
  337. print("DB Error: ", self.lastErrorCode(), self.lastErrorMessage()!);
  338. print("DB Query: ", sql);
  339. print("DB Path: ", _databasePath!);
  340. }
  341. sqlite3_finalize(pStmt);
  342. _isExecutingStatement = false
  343. throw FMDBError.sqlite3ErrorCode(rc)
  344. }
  345. }
  346. if (statement == nil) {
  347. statement = FMStatement()
  348. statement?._statement = pStmt
  349. if (_shouldCacheStatements) {
  350. setCachedStatement(statement!, forQuery: sql)
  351. }
  352. }
  353. if let uwArgArary = argArray {
  354. for item in uwArgArary {
  355. // FIXME: Can we check for arrays of any type?
  356. assert(type(of: item) != type(of: Array<Any>()), "We can't bind arrays in executeQuery (\(type(of: item)))")
  357. }
  358. }
  359. if (shouldBind) {
  360. if (try bind(statement: statement!, argArray: argArray, argDict: argDictionary)) {
  361. }
  362. }
  363. //
  364. // var bindingItems = items
  365. //
  366. // if (items.count == 1 && type(of: items[0]) == type(of: Array<Any>())) {
  367. // // need to unpack! We can't bind arrays and this was just passed down from another method
  368. // bindingItems = items[0] as! [Any]
  369. // }
  370. //
  371. //
  372. // if (try bind(statement: statement, usingArguments: bindingItems)) {
  373. //
  374. // }
  375. let rs = FMResultSet.resultSet(withStatement: statement!, parentDatabase: self, shouldAutoClose: true);
  376. _openResultSets.append(rs)
  377. statement?.useCount = statement!.useCount + 1
  378. _isExecutingStatement = false;
  379. return rs;
  380. }
  381. @discardableResult func executeUpdate(_ sql : String, argArray:[Any?]?, argDictionary : Dictionary<String, Any?>?) throws -> Bool {
  382. var rs : FMResultSet
  383. do {
  384. rs = try self.executeQuery(sql, argArray: argArray, argDictionary:argDictionary, shouldBind:true)
  385. }
  386. catch {
  387. return false
  388. }
  389. var rc : Int32
  390. do {
  391. rc = try rs.internalStep()
  392. }
  393. catch {
  394. return false
  395. }
  396. return rc == SQLITE_DONE
  397. }
  398. @discardableResult func executeUpdate(_ sql : String, withParameterDictionary : Dictionary<String, Any>) throws -> Bool {
  399. return try executeUpdate(sql, argArray: nil, argDictionary: withParameterDictionary)
  400. }
  401. @discardableResult func executeQuery(_ sql : String, withParameterDictionary : Dictionary<String, Any>) throws -> FMResultSet {
  402. return try self.executeQuery(sql, argArray: nil, argDictionary:withParameterDictionary, shouldBind:true)
  403. }
  404. func pullSQLFromArgumnets(_ ar : [Any?]) -> (String, [Any?]) {
  405. if (ar.count < 1) {
  406. assert(false, "Empty array in pullSQLFromArgumnets")
  407. }
  408. let sql = ar[0] as! String
  409. assert(type(of: sql) == type(of: String()), "First argument is not a String, got \(type(of: sql))")
  410. var arx = Array(ar)
  411. arx.removeFirst()
  412. // Pop out an array from an array.
  413. if (arx.count == 1 && arx[0]! is Array<Any>) {
  414. arx = arx[0] as! [Any?]
  415. }
  416. return (sql, arx)
  417. }
  418. @discardableResult func executeUpdate(_ sqlStringAndBindings : Any?...) throws -> Bool {
  419. let (sql, args) = pullSQLFromArgumnets(sqlStringAndBindings)
  420. return try executeUpdate(sql, argArray: args, argDictionary: nil)
  421. }
  422. /*
  423. @discardableResult func executeUpdate(_ sql : String, items : Any...) throws -> Bool {
  424. if (!databaseExists()) {
  425. throw FMDBError.sqlite3ErrorCode(-1)
  426. }
  427. // print("type: ", type(of: items)) // type: Array<Any>
  428. #warning("We need to unpack items if items[0] is an array")
  429. var pStmt : OpaquePointer?
  430. let rc = sqlite3_prepare_v2(_db, sql, -1, &pStmt, nil)
  431. if (SQLITE_OK != rc) {
  432. if (logsErrors) {
  433. print("DB Error: ", self.lastErrorCode(), self.lastErrorMessage()!);
  434. print("DB Query: ", sql);
  435. print("DB Path: ", _databasePath!);
  436. }
  437. sqlite3_finalize(pStmt);
  438. throw FMDBError.sqlite3ErrorCode(rc)
  439. }
  440. let statement = FMStatement()
  441. statement._statement = pStmt
  442. if (try bind(statement: statement, argArray: items, argDict: nil)) {
  443. }
  444. let rs = FMResultSet.resultSet(withStatement: statement, parentDatabase: self, shouldAutoClose: true);
  445. _openResultSets.append(rs)
  446. let stepCode = { return try? rs.internalStep() }()
  447. #warning("Should we just close out the rs here?")
  448. return stepCode == SQLITE_DONE;
  449. }
  450. */
  451. @discardableResult func executeQuery(_ sqlStringAndBindings : Any...) throws -> FMResultSet {
  452. let (sql, args) = pullSQLFromArgumnets(sqlStringAndBindings)
  453. return try executeQuery(sql, argArray: args, argDictionary: nil, shouldBind: true)
  454. }
  455. /*
  456. @discardableResult func executeQuery(_ sql : String, items : Any...) throws -> FMResultSet {
  457. if (!databaseExists()) {
  458. throw FMDBError.sqlite3ErrorCode(-1)
  459. }
  460. let rc : Int32
  461. var pStmt : OpaquePointer?
  462. rc = sqlite3_prepare_v2(_db, sql, -1, &pStmt, nil)
  463. if (SQLITE_OK != rc) {
  464. if (logsErrors) {
  465. print("DB Error: ", self.lastErrorCode(), self.lastErrorMessage()!);
  466. print("DB Query: ", sql);
  467. print("DB Path: ", _databasePath!);
  468. }
  469. sqlite3_finalize(pStmt);
  470. throw FMDBError.sqlite3ErrorCode(rc)
  471. }
  472. let statement = FMStatement()
  473. statement._statement = pStmt
  474. var bindingItems = items
  475. if (items.count == 1 && type(of: items[0]) == type(of: Array<Any>())) {
  476. // need to unpack! We can't bind arrays and this was just passed down from another method
  477. bindingItems = items[0] as! [Any]
  478. }
  479. if (try bind(statement: statement, argArray: bindingItems, argDict: nil)) {
  480. }
  481. let rs = FMResultSet.resultSet(withStatement: statement, parentDatabase: self, shouldAutoClose: true);
  482. _openResultSets.append(rs)
  483. return rs;
  484. }
  485. */
  486. public func setMaxBusyRetryTimeInterval(_ timeout : TimeInterval) {
  487. _maxBusyRetryTimeInterval = timeout
  488. if (_db == nil) {
  489. return
  490. }
  491. if (timeout > 0) {
  492. func handler(_ myself: UnsafeMutableRawPointer?, _ count: Int32) -> Int32 {
  493. let me = unsafeBitCast(myself, to: FMDatabase.self)
  494. if (count == 0) {
  495. me._startBusyRetryTime = NSDate().timeIntervalSinceReferenceDate
  496. return 1;
  497. }
  498. let delta = NSDate().timeIntervalSinceReferenceDate - me._startBusyRetryTime
  499. if (delta < me._maxBusyRetryTimeInterval) {
  500. let requestedSleepInMillseconds = arc4random_uniform(50) + 50;
  501. let actualSleepInMilliseconds = sqlite3_sleep(Int32(requestedSleepInMillseconds));
  502. if (actualSleepInMilliseconds != requestedSleepInMillseconds) {
  503. print("WARNING: Requested sleep of \(requestedSleepInMillseconds) milliseconds, but SQLite returned \(actualSleepInMilliseconds). Maybe SQLite wasn't built with HAVE_USLEEP=1?");
  504. }
  505. return 1;
  506. }
  507. return 0
  508. }
  509. sqlite3_busy_handler(_db, handler, unsafeBitCast(self, to: UnsafeMutableRawPointer.self));
  510. }
  511. else {
  512. // turn it off otherwise
  513. sqlite3_busy_handler(_db, nil, nil);
  514. }
  515. }
  516. func sqliteHandle() -> OpaquePointer? {
  517. return _db
  518. }
  519. func resultSetDidClose(resultSet: FMResultSet) {
  520. _openResultSets.removeAll(where: {$0 == resultSet})
  521. }
  522. @discardableResult func rollback() throws -> Bool {
  523. let b = try executeUpdate("rollback transaction")
  524. if (b) {
  525. _isInTransaction = false
  526. }
  527. return b
  528. }
  529. @discardableResult func commit() throws -> Bool {
  530. let b = try executeUpdate("commit transaction")
  531. if (b) {
  532. _isInTransaction = false
  533. }
  534. return b
  535. }
  536. @discardableResult func beginTransaction() throws -> Bool {
  537. return try beginExclusiveTransaction()
  538. }
  539. @discardableResult func beginDeferredTransaction() throws -> Bool {
  540. let b = try executeUpdate("begin deferred transaction")
  541. if (b) {
  542. _isInTransaction = true
  543. }
  544. return b
  545. }
  546. @discardableResult func beginImmediateTransaction() throws -> Bool {
  547. let b = try executeUpdate("begin immediate transaction")
  548. if (b) {
  549. _isInTransaction = true
  550. }
  551. return b
  552. }
  553. @discardableResult func beginExclusiveTransaction() throws -> Bool {
  554. let b = try executeUpdate("begin exclusive transaction")
  555. if (b) {
  556. _isInTransaction = true
  557. }
  558. return b
  559. }
  560. func inTransaction() -> Bool {
  561. _isInTransaction
  562. }
  563. @discardableResult func interrupt() -> Bool {
  564. if (_db != nil) {
  565. sqlite3_interrupt(_db);
  566. return true;
  567. }
  568. return false;
  569. }
  570. // Extras?
  571. func getTableSchema(_ name:String) throws -> FMResultSet {
  572. let s = "pragma table_info('\(name)')"
  573. return try executeQuery(s)
  574. }
  575. func boolForQuery(_ sqlStringAndBindings : Any...) throws -> Bool {
  576. let (sql, args) = pullSQLFromArgumnets(sqlStringAndBindings)
  577. let rs = try executeQuery(sql, argArray: args, argDictionary: nil, shouldBind: true)
  578. if (try !rs.next()) {
  579. return false
  580. }
  581. return rs.bool(0)
  582. }
  583. func intForQuery(_ sqlStringAndBindings : Any...) throws -> Int32 {
  584. let (sql, args) = pullSQLFromArgumnets(sqlStringAndBindings)
  585. let rs = try executeQuery(sql, argArray: args, argDictionary: nil, shouldBind: true)
  586. if (try !rs.next()) {
  587. return 0
  588. }
  589. return rs.int(0)
  590. }
  591. func dateForQuery(_ sqlStringAndBindings : Any...) throws -> NSDate? {
  592. let (sql, args) = pullSQLFromArgumnets(sqlStringAndBindings)
  593. let rs = try executeQuery(sql, argArray: args, argDictionary: nil, shouldBind: true)
  594. if (try !rs.next()) {
  595. return nil
  596. }
  597. return rs.date(0)
  598. }
  599. func changes() -> Int32 {
  600. if (_isExecutingStatement) {
  601. warnInUse()
  602. return 0;
  603. }
  604. _isExecutingStatement = true;
  605. let ret = sqlite3_changes(_db);
  606. _isExecutingStatement = false;
  607. return ret;
  608. }
  609. func validateSQL(_ sql : String) throws {
  610. var pStmt : OpaquePointer?
  611. let rc = sqlite3_prepare_v2(_db, sql, -1, &pStmt, nil)
  612. sqlite3_finalize(pStmt);
  613. if (rc != SQLITE_OK) {
  614. throw FMDBError.sqlite3ErrorCode(rc)
  615. }
  616. }
  617. func tableExists(_ tableName : String) -> Bool {
  618. do {
  619. let tableNameLower = tableName.lowercased()
  620. let rs = try self.executeQuery("select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableNameLower)
  621. let returnBool = try rs.next()
  622. rs.close()
  623. return returnBool
  624. }
  625. catch {
  626. return false
  627. }
  628. }
  629. func columnExists(_ columnName : String, inTable: String) -> Bool {
  630. do {
  631. let lowerTableName = inTable.lowercased()
  632. let lowerColumnName = columnName.lowercased()
  633. var returnBool = false
  634. let rs = try self.getTableSchema(lowerTableName)
  635. while (try rs.next()) {
  636. if (rs.string("name")?.lowercased() == lowerColumnName) {
  637. returnBool = true
  638. break
  639. }
  640. }
  641. rs.close()
  642. return returnBool
  643. }
  644. catch {
  645. return false
  646. }
  647. }
  648. func getSchema() -> FMResultSet? {
  649. var rs : FMResultSet?
  650. do {
  651. rs = try executeQuery("SELECT type, name, tbl_name, rootpage, sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type != 'meta' AND name NOT LIKE 'sqlite_%' ORDER BY tbl_name, type DESC, name")
  652. }
  653. catch {
  654. print("getSchema has failed.")
  655. }
  656. return rs;
  657. }
  658. func setUserVersion(_ version: Int32) {
  659. var rs : FMResultSet?
  660. do {
  661. rs = try executeQuery("pragma user_version = \(version)")
  662. try rs?.next()
  663. rs?.close()
  664. }
  665. catch {
  666. print("setUserVersion has failed.")
  667. }
  668. }
  669. func userVersion() -> Int32 {
  670. var rs : FMResultSet
  671. do {
  672. rs = try executeQuery("pragma user_version")
  673. if (try rs.next()) {
  674. let r = rs.int(0)
  675. rs.close()
  676. return r;
  677. }
  678. }
  679. catch {
  680. print("userVersion has failed.")
  681. }
  682. return 0
  683. }
  684. func setApplicationID(_ appID:UInt32) {
  685. do {
  686. let rs = try executeQuery("pragma application_id=\(appID)")
  687. try rs.next()
  688. rs.close()
  689. }
  690. catch {
  691. print("setApplicationID has failed.")
  692. }
  693. }
  694. func applicationID() -> UInt32 {
  695. do {
  696. var r : UInt32 = 0
  697. let rs = try executeQuery("pragma application_id")
  698. if (try rs.next()) {
  699. r = UInt32(rs.longLongInt(0))
  700. }
  701. rs.close()
  702. return r
  703. }
  704. catch {
  705. print("applicationID has failed.")
  706. }
  707. return 0
  708. }
  709. func setApplicationIDString(_ appID : String) {
  710. if (appID.count != 4) {
  711. print("setApplicationIDString: string passed is not exactly 4 chars long. (was \(appID.count))");
  712. }
  713. // var r : FourCharCode = 0
  714. // for char in appID.utf16 {
  715. // r = (r << 8) + FourCharCode(char)
  716. // }
  717. //
  718. let typeCode = NSHFSTypeCodeFromFileType("'\(appID)'")
  719. setApplicationID(typeCode)
  720. }
  721. func applicationIDString() -> String? {
  722. let s = NSFileTypeForHFSTypeCode(applicationID())
  723. if let s = s {
  724. if (s.count != 6) {
  725. return nil
  726. }
  727. let lowerBound = s.index(s.startIndex, offsetBy: 1)
  728. let upperBound = s.index(s.startIndex, offsetBy: 5)
  729. return String(s[lowerBound..<upperBound])
  730. //return NSString(string:s).substring(with: NSMakeRange(1, 4))
  731. }
  732. return nil
  733. }
  734. func warnInUse() {
  735. print("The FMDatabase \(self) is currently in use.");
  736. if (crashOnErrors) {
  737. assert(false, "The FMDatabase \(self) is currently in use.");
  738. abort();
  739. }
  740. }
  741. }