| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116 |
- import Foundation
- import SQLite3
- // Why the f doesn't this show up in the sqlite3.h headers for swift?
- let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
- #warning("Should we give this a better name? It's not very FMDB-like")
- enum SqliteValueType : Int32 {
- case SqliteValueTypeInteger = 1,
- SqliteValueTypeFloat = 2,
- SqliteValueTypeText = 3,
- SqliteValueTypeBlob = 4,
- SqliteValueTypeNull = 5
- }
- /*
- // These are from the sqlite3 headers.
- #define SQLITE_INTEGER 1
- #define SQLITE_FLOAT 2
- #define SQLITE3_TEXT 3
- #define SQLITE_BLOB 4
- #define SQLITE_NULL 5
-
- */
- enum FMDBError: Error {
- case sqlite3ErrorCode(Int32)
- }
- public class FMDatabase : NSObject {
-
- private var _db : OpaquePointer?
- var _databasePath : String?
- internal var _isOpen : Bool
- private var _maxBusyRetryTimeInterval : TimeInterval = 2
- private var _startBusyRetryTime : TimeInterval = 0
- public var logsErrors : Bool
- private var _shouldCacheStatements = false
- private var _openResultSets : Array<FMResultSet>
- private var _cachedStatements : Dictionary<String, Set<FMStatement>>?
- private var _isInTransaction = false
- private var _isExecutingStatement = false
- public var crashOnErrors = false
- public var traceExecution = true
-
-
- #warning("We need to port over all instances of _isExecutingStatement")
-
- public override init() {
-
- _isOpen = false
-
- _databasePath = ":memory:"
-
- _openResultSets = Array<FMResultSet>()
-
- logsErrors = true
-
- }
-
- static func isSQLiteThreadSafe() -> Bool {
- // make sure to read the sqlite headers on this guy!
- return sqlite3_threadsafe() != 0;
- }
-
- static func database(with filePath : String) -> FMDatabase {
-
- let db = FMDatabase()
- db._databasePath = filePath
-
- return db
- }
-
- static func database(with fileURL : URL) -> FMDatabase {
- return FMDatabase.database(with: fileURL.path)
- }
-
-
- func databaseURL() -> NSURL? {
- return (_databasePath != nil) ? NSURL(fileURLWithPath: _databasePath!) : nil
- //[NSURL fileURLWithPath:_databasePath] : nil;
- }
-
- // MARK: Cached statements
-
- public func clearCachedStatements() {
-
- if var unwrappedCachedStatements = _cachedStatements {
-
- for (_, statementSet) in unwrappedCachedStatements {
- for (statement) in statementSet {
- statement.close()
- }
- }
-
- unwrappedCachedStatements.removeAll()
- }
- }
-
- func cachedStatementForQuery(_ query : String) -> FMStatement? {
-
- if let uwCachedStatements = _cachedStatements {
-
- let setOfStatements = uwCachedStatements[query] // Dictionary<String, Set<FMStatement>>?
- let st = setOfStatements?.first(where: { st in
- !st._inUse
- })
-
- return st
- }
-
- return nil
- }
-
- func setCachedStatement(_ statement : FMStatement, forQuery: String) {
-
- statement._query = forQuery
-
- if var uwCachedStatements = _cachedStatements {
- var statements = uwCachedStatements[forQuery]
- if (statements == nil) {
- statements = Set<FMStatement>()
- uwCachedStatements[forQuery] = statements
- }
-
- statements?.insert(statement)
- }
- }
-
- func shouldCacheStatements() -> Bool {
- return _shouldCacheStatements
- }
-
- func setShouldCacheStatements(_ flag: Bool) {
- _shouldCacheStatements = flag
- if (_shouldCacheStatements && _cachedStatements != nil) {
- _cachedStatements = Dictionary<String, Set<FMStatement>>()
- }
-
- if (!_shouldCacheStatements) {
- _cachedStatements = nil
- }
- }
-
-
- public func hasOpenResultSets() -> Bool {
- return _openResultSets.count > 0
- }
-
- public func closeOpenResultSets() {
-
-
- for rs in (_openResultSets as NSArray as! [FMResultSet]) {
-
- rs.parentDB = nil // nil it out so close doesn't call resultSetDidClose
- rs.close()
-
- }
-
- _openResultSets.removeAll()
- }
-
-
- @discardableResult public func close() -> Bool {
-
- clearCachedStatements()
- closeOpenResultSets()
-
- var rc = SQLITE_ERROR
- var retry: Bool
- var triedFinalizingOpenStatements = false
-
- repeat {
- retry = false
- rc = sqlite3_close(_db)
- if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
- if (!triedFinalizingOpenStatements) {
- triedFinalizingOpenStatements = true;
- var pStmt : OpaquePointer?
- pStmt = sqlite3_next_stmt(_db, nil)
- while (pStmt != nil) {
- print("Closing leaked statement")
- sqlite3_finalize(pStmt);
- pStmt = sqlite3_next_stmt(_db, nil)
- retry = true;
- }
- }
- }
- else if (SQLITE_OK != rc) {
- print("error closing!: %d", rc)
- }
- }
- while (retry)
- _db = nil
- _isOpen = false
-
-
- return SQLITE_OK == rc
- }
-
- @discardableResult public func open() throws -> Bool {
- return try openWithFlags(flags: SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, vfsName: nil)
- }
-
-
- @discardableResult public func openWithFlags(flags : Int32, vfsName : String?) throws -> Bool {
-
- if (_isOpen) {
- return true
- }
-
- if (_db != nil) {
- close()
- }
-
- let err = sqlite3_open_v2(_databasePath, &_db, flags, vfsName)
-
- if (err != SQLITE_OK) {
- print("Error opening SQLite database: ", self.lastErrorMessage()!)
- throw FMDBError.sqlite3ErrorCode(err)
- }
-
- if (_maxBusyRetryTimeInterval > 0.0) {
- // set the handler
- setMaxBusyRetryTimeInterval(_maxBusyRetryTimeInterval);
- }
-
- _isOpen = true
-
- return true
- }
-
-
- func lastErrorMessage() -> String? {
- return String(utf8String: sqlite3_errmsg(_db))
- }
-
- func lastErrorCode() -> Int32 {
- return sqlite3_errcode(_db)
- }
-
- func lastExtendedErrorCode() -> Int32 {
- return sqlite3_extended_errcode(_db)
- }
-
- func error(withMessage: String) -> NSError {
-
- return NSError(
- domain: "FMDatabase",
- code: Int(sqlite3_errcode(_db)),
- userInfo: [
- NSLocalizedDescriptionKey: withMessage
- ]
- )
-
- }
-
-
- func lastError() -> NSError {
- return self.error(withMessage: lastErrorMessage() ?? "")
- }
-
- func hadError() -> Bool {
-
- let lastErrCode = lastErrorCode()
-
- return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW)
- }
-
- func databaseExists() ->Bool {
-
- if (!_isOpen) {
-
- print("The FMDatabase \(self) is not open.");
-
- if (crashOnErrors) {
- abort();
- }
-
- return false;
- }
-
- return true;
- }
-
- func internalBind(object: Any?, toColumn:Int32, statement: OpaquePointer?) -> Int32 {
-
- if object == nil || (object as? NSNull) != nil {
- return sqlite3_bind_null(statement, toColumn);
- }
- else if (object as? NSNull) != nil {
- return sqlite3_bind_null(statement, toColumn);
- }
- else if (object as? NSData) != nil {
-
- let d = object as? NSData
-
- let b = d?.bytes
- if (b == nil) {
- // it's an empty NSData object, aka [NSData data].
- // Don't pass a NULL pointer, or sqlite will bind a SQL null instead of a blob.
-
- // FIXME: This is pretty dumb.
- // This is just copied out of the swift book. I'm not sure what to pass here - but it can't be nil.
- let bytesPointer = UnsafeMutableRawPointer.allocate(byteCount: 4, alignment: 4)
- bytesPointer.storeBytes(of: 0xFFFF_FFFF, as: UInt32.self)
-
- return sqlite3_bind_blob(statement, toColumn, bytesPointer, 0, SQLITE_TRANSIENT);
- }
-
- return sqlite3_bind_blob(statement, toColumn, b, Int32(d?.length ?? 0), SQLITE_TRANSIENT);
-
- }
- else if let object = object as? String {
- return sqlite3_bind_text(statement, toColumn, object, -1, SQLITE_TRANSIENT)
- }
- else if let object = object as? Double {
- return sqlite3_bind_double(statement, toColumn, object)
- }
- else if let object = object as? Int64 {
- return sqlite3_bind_int64(statement, toColumn, object)
- }
- else if let object = object as? UInt64 {
- return sqlite3_bind_int64(statement, toColumn, Int64(truncatingIfNeeded:object))
- }
- else if let object = object as? Int32 {
- return sqlite3_bind_int(statement, toColumn, object)
- }
- else if let object = object as? UInt32 {
- return sqlite3_bind_int(statement, toColumn, Int32(object))
- }
- else if let object = object as? Int {
- return sqlite3_bind_int(statement, toColumn, Int32(object))
- }
- else if let object = object as? Bool {
- return sqlite3_bind_int(statement, toColumn, object ? 1 : 0)
- }
- else if let object = object as? Date {
-
- #warning("Need to check for a date formatter when binding")
-
- // if (self.hasDateFormatter)
- // return sqlite3_bind_text(pStmt, idx, [[self stringFromDate:obj] UTF8String], -1, SQLITE_TRANSIENT);
- // else
-
- return sqlite3_bind_double(statement, toColumn, object.timeIntervalSince1970);
- }
-
- print("unknown type: ", type(of: object))
-
- #warning("These types are all well and good, but FMDB supports much much more. Port them all over, Gus")
- return SQLITE_ERROR
- }
-
- func bind(statement: FMStatement, argArray: Array<Any?>?, argDict: Dictionary<String, Any?>?) throws -> Bool {
-
- var idx = Int(0)
- let queryCount = sqlite3_bind_parameter_count(statement._statement); // pointed out by Dominic Yu (thanks!)
-
-
- if let uwArgDict = argDict {
-
- for (k, obj) in uwArgDict {
-
- // Prefix the key with a colon.
- let parameterName = ":\(k)"
-
- // Get the index for the parameter name.
- let namedIdx = sqlite3_bind_parameter_index(statement._statement, parameterName);
- if (namedIdx > 0) {
- // Standard binding from here.
-
-
- if (traceExecution) {
- print("Index \(namedIdx), \(k) = \(obj ?? "nil")")
- }
-
- let rc = internalBind(object: obj, toColumn: Int32(namedIdx), statement: statement._statement ?? nil)
- if (rc != SQLITE_OK) {
-
- let errString = lastErrorMessage()
- print("Error: unable to bind (\(rc), \(errString ?? "")")
- // #define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */
-
- _isExecutingStatement = false
-
- throw FMDBError.sqlite3ErrorCode(rc)
- }
-
-
- // increment the binding count, so our check below works out
- idx = idx + 1;
- }
- else {
- print("Could not find index for \(k)");
- }
- }
-
- }
- else if let uwArgArray = argArray {
-
- while (idx < queryCount) {
-
- if (idx >= uwArgArray.count) {
- //We ran out of arguments
- break;
- }
-
- let obj = uwArgArray[idx]
-
- idx += 1
-
- let rc = internalBind(object: obj, toColumn: Int32(idx), statement: statement._statement ?? nil)
- if (rc != SQLITE_OK) {
-
- print("Error: unable to bind (\(rc), \(String(describing: sqlite3_errmsg(_db)))")
-
- _isExecutingStatement = false
-
- sqlite3_finalize(statement._statement);
- throw FMDBError.sqlite3ErrorCode(rc)
- }
-
-
- }
- }
-
-
- if (idx != queryCount) {
- print("Error: the bind count is not correct for the # of variables (executeQuery)");
- assert(false, "Error: the bind count is not correct for the # of variables (executeQuery)")
- // sqlite3_finalize(pStmt);
- // pStmt = 0x00;
- // _isExecutingStatement = NO;
- // return false;
- }
-
-
-
- return true
- }
-
-
-
-
- @discardableResult func executeQuery(_ sql : String, argArray : [Any?]?, argDictionary : Dictionary<String, Any?>?, shouldBind: Bool) throws -> FMResultSet {
-
- if (!databaseExists()) {
- throw FMDBError.sqlite3ErrorCode(-1)
- }
-
- if (_isExecutingStatement) {
- warnInUse();
- throw FMDBError.sqlite3ErrorCode(-1)
- }
-
- _isExecutingStatement = true
-
- let rc : Int32
- var pStmt : OpaquePointer?
- var statement : FMStatement?
-
- if (_shouldCacheStatements) {
- statement = cachedStatementForQuery(sql)
- pStmt = statement != nil ? statement?._statement : nil
- statement?.reset()
- }
-
-
- if (pStmt == nil) {
-
- rc = sqlite3_prepare_v2(_db, sql, -1, &pStmt, nil)
-
- if (SQLITE_OK != rc) {
- if (logsErrors) {
- print("DB Error: ", self.lastErrorCode(), self.lastErrorMessage()!);
- print("DB Query: ", sql);
- print("DB Path: ", _databasePath!);
- }
-
- sqlite3_finalize(pStmt);
- _isExecutingStatement = false
-
- throw FMDBError.sqlite3ErrorCode(rc)
- }
- }
-
-
- if (statement == nil) {
- statement = FMStatement()
- statement?._statement = pStmt
-
- if (_shouldCacheStatements) {
- setCachedStatement(statement!, forQuery: sql)
- }
-
- }
-
-
- if let uwArgArary = argArray {
- for item in uwArgArary {
- // FIXME: Can we check for arrays of any type?
-
- assert(type(of: item) != type(of: Array<Any>()), "We can't bind arrays in executeQuery (\(type(of: item)))")
- }
- }
-
- if (shouldBind) {
-
-
- if (try bind(statement: statement!, argArray: argArray, argDict: argDictionary)) {
-
- }
- }
- //
- // var bindingItems = items
- //
- // if (items.count == 1 && type(of: items[0]) == type(of: Array<Any>())) {
- // // need to unpack! We can't bind arrays and this was just passed down from another method
- // bindingItems = items[0] as! [Any]
- // }
- //
- //
- // if (try bind(statement: statement, usingArguments: bindingItems)) {
- //
- // }
-
- let rs = FMResultSet.resultSet(withStatement: statement!, parentDatabase: self, shouldAutoClose: true);
-
- _openResultSets.append(rs)
-
- statement?.useCount = statement!.useCount + 1
-
- _isExecutingStatement = false;
-
- return rs;
- }
-
- @discardableResult func executeUpdate(_ sql : String, argArray:[Any?]?, argDictionary : Dictionary<String, Any?>?) throws -> Bool {
-
- var rs : FMResultSet
-
- do {
- rs = try self.executeQuery(sql, argArray: argArray, argDictionary:argDictionary, shouldBind:true)
- }
- catch {
- return false
- }
-
- var rc : Int32
- do {
-
- rc = try rs.internalStep()
- }
- catch {
- return false
- }
-
- return rc == SQLITE_DONE
-
- }
-
-
- @discardableResult func executeUpdate(_ sql : String, withParameterDictionary : Dictionary<String, Any>) throws -> Bool {
-
- return try executeUpdate(sql, argArray: nil, argDictionary: withParameterDictionary)
- }
-
-
- @discardableResult func executeQuery(_ sql : String, withParameterDictionary : Dictionary<String, Any>) throws -> FMResultSet {
-
- return try self.executeQuery(sql, argArray: nil, argDictionary:withParameterDictionary, shouldBind:true)
- }
-
-
- func pullSQLFromArgumnets(_ ar : [Any?]) -> (String, [Any?]) {
-
- if (ar.count < 1) {
- assert(false, "Empty array in pullSQLFromArgumnets")
- }
-
- let sql = ar[0] as! String
-
- assert(type(of: sql) == type(of: String()), "First argument is not a String, got \(type(of: sql))")
-
- var arx = Array(ar)
- arx.removeFirst()
-
- // Pop out an array from an array.
- if (arx.count == 1 && arx[0]! is Array<Any>) {
- arx = arx[0] as! [Any?]
- }
-
- return (sql, arx)
- }
-
-
-
- @discardableResult func executeUpdate(_ sqlStringAndBindings : Any?...) throws -> Bool {
-
-
- let (sql, args) = pullSQLFromArgumnets(sqlStringAndBindings)
-
- return try executeUpdate(sql, argArray: args, argDictionary: nil)
-
- }
-
- /*
- @discardableResult func executeUpdate(_ sql : String, items : Any...) throws -> Bool {
-
- if (!databaseExists()) {
- throw FMDBError.sqlite3ErrorCode(-1)
- }
-
- // print("type: ", type(of: items)) // type: Array<Any>
-
- #warning("We need to unpack items if items[0] is an array")
-
-
- var pStmt : OpaquePointer?
-
- let rc = sqlite3_prepare_v2(_db, sql, -1, &pStmt, nil)
-
- if (SQLITE_OK != rc) {
- if (logsErrors) {
- print("DB Error: ", self.lastErrorCode(), self.lastErrorMessage()!);
- print("DB Query: ", sql);
- print("DB Path: ", _databasePath!);
- }
-
-
- sqlite3_finalize(pStmt);
-
- throw FMDBError.sqlite3ErrorCode(rc)
-
- }
-
-
- let statement = FMStatement()
- statement._statement = pStmt
-
- if (try bind(statement: statement, argArray: items, argDict: nil)) {
-
- }
-
- let rs = FMResultSet.resultSet(withStatement: statement, parentDatabase: self, shouldAutoClose: true);
-
- _openResultSets.append(rs)
-
- let stepCode = { return try? rs.internalStep() }()
-
- #warning("Should we just close out the rs here?")
-
-
- return stepCode == SQLITE_DONE;
- }
- */
-
-
- @discardableResult func executeQuery(_ sqlStringAndBindings : Any...) throws -> FMResultSet {
-
-
- let (sql, args) = pullSQLFromArgumnets(sqlStringAndBindings)
-
- return try executeQuery(sql, argArray: args, argDictionary: nil, shouldBind: true)
-
- }
- /*
- @discardableResult func executeQuery(_ sql : String, items : Any...) throws -> FMResultSet {
-
- if (!databaseExists()) {
- throw FMDBError.sqlite3ErrorCode(-1)
- }
-
- let rc : Int32
- var pStmt : OpaquePointer?
-
- rc = sqlite3_prepare_v2(_db, sql, -1, &pStmt, nil)
-
- if (SQLITE_OK != rc) {
- if (logsErrors) {
- print("DB Error: ", self.lastErrorCode(), self.lastErrorMessage()!);
- print("DB Query: ", sql);
- print("DB Path: ", _databasePath!);
- }
-
-
- sqlite3_finalize(pStmt);
-
- throw FMDBError.sqlite3ErrorCode(rc)
- }
-
- let statement = FMStatement()
- statement._statement = pStmt
-
- var bindingItems = items
-
- if (items.count == 1 && type(of: items[0]) == type(of: Array<Any>())) {
- // need to unpack! We can't bind arrays and this was just passed down from another method
- bindingItems = items[0] as! [Any]
- }
-
-
- if (try bind(statement: statement, argArray: bindingItems, argDict: nil)) {
-
- }
-
- let rs = FMResultSet.resultSet(withStatement: statement, parentDatabase: self, shouldAutoClose: true);
-
- _openResultSets.append(rs)
-
- return rs;
- }
- */
-
- public func setMaxBusyRetryTimeInterval(_ timeout : TimeInterval) {
-
- _maxBusyRetryTimeInterval = timeout
-
- if (_db == nil) {
- return
- }
-
- if (timeout > 0) {
-
- func handler(_ myself: UnsafeMutableRawPointer?, _ count: Int32) -> Int32 {
-
- let me = unsafeBitCast(myself, to: FMDatabase.self)
-
- if (count == 0) {
- me._startBusyRetryTime = NSDate().timeIntervalSinceReferenceDate
- return 1;
- }
-
-
- let delta = NSDate().timeIntervalSinceReferenceDate - me._startBusyRetryTime
-
- if (delta < me._maxBusyRetryTimeInterval) {
- let requestedSleepInMillseconds = arc4random_uniform(50) + 50;
- let actualSleepInMilliseconds = sqlite3_sleep(Int32(requestedSleepInMillseconds));
-
- if (actualSleepInMilliseconds != requestedSleepInMillseconds) {
- print("WARNING: Requested sleep of \(requestedSleepInMillseconds) milliseconds, but SQLite returned \(actualSleepInMilliseconds). Maybe SQLite wasn't built with HAVE_USLEEP=1?");
- }
- return 1;
- }
-
- return 0
- }
-
-
- sqlite3_busy_handler(_db, handler, unsafeBitCast(self, to: UnsafeMutableRawPointer.self));
- }
- else {
- // turn it off otherwise
- sqlite3_busy_handler(_db, nil, nil);
- }
- }
-
- func sqliteHandle() -> OpaquePointer? {
- return _db
- }
-
-
- func resultSetDidClose(resultSet: FMResultSet) {
- _openResultSets.removeAll(where: {$0 == resultSet})
- }
-
- @discardableResult func rollback() throws -> Bool {
- let b = try executeUpdate("rollback transaction")
- if (b) {
- _isInTransaction = false
- }
- return b
- }
-
- @discardableResult func commit() throws -> Bool {
- let b = try executeUpdate("commit transaction")
- if (b) {
- _isInTransaction = false
- }
- return b
- }
-
- @discardableResult func beginTransaction() throws -> Bool {
- return try beginExclusiveTransaction()
- }
-
- @discardableResult func beginDeferredTransaction() throws -> Bool {
- let b = try executeUpdate("begin deferred transaction")
- if (b) {
- _isInTransaction = true
- }
- return b
- }
-
- @discardableResult func beginImmediateTransaction() throws -> Bool {
- let b = try executeUpdate("begin immediate transaction")
- if (b) {
- _isInTransaction = true
- }
- return b
- }
-
- @discardableResult func beginExclusiveTransaction() throws -> Bool {
- let b = try executeUpdate("begin exclusive transaction")
- if (b) {
- _isInTransaction = true
- }
- return b
- }
-
-
- func inTransaction() -> Bool {
- _isInTransaction
- }
- @discardableResult func interrupt() -> Bool {
- if (_db != nil) {
- sqlite3_interrupt(_db);
- return true;
- }
-
- return false;
- }
-
-
-
-
-
- // Extras?
-
- func getTableSchema(_ name:String) throws -> FMResultSet {
-
- let s = "pragma table_info('\(name)')"
-
- return try executeQuery(s)
- }
-
- func boolForQuery(_ sqlStringAndBindings : Any...) throws -> Bool {
-
- let (sql, args) = pullSQLFromArgumnets(sqlStringAndBindings)
-
- let rs = try executeQuery(sql, argArray: args, argDictionary: nil, shouldBind: true)
-
- if (try !rs.next()) {
- return false
- }
-
- return rs.bool(0)
- }
-
- func intForQuery(_ sqlStringAndBindings : Any...) throws -> Int32 {
-
- let (sql, args) = pullSQLFromArgumnets(sqlStringAndBindings)
-
- let rs = try executeQuery(sql, argArray: args, argDictionary: nil, shouldBind: true)
-
- if (try !rs.next()) {
- return 0
- }
-
- return rs.int(0)
- }
-
- func dateForQuery(_ sqlStringAndBindings : Any...) throws -> NSDate? {
-
- let (sql, args) = pullSQLFromArgumnets(sqlStringAndBindings)
-
- let rs = try executeQuery(sql, argArray: args, argDictionary: nil, shouldBind: true)
-
-
- if (try !rs.next()) {
- return nil
- }
-
- return rs.date(0)
- }
-
- func changes() -> Int32 {
-
- if (_isExecutingStatement) {
- warnInUse()
- return 0;
- }
-
-
- _isExecutingStatement = true;
-
- let ret = sqlite3_changes(_db);
-
- _isExecutingStatement = false;
-
- return ret;
- }
-
-
- func validateSQL(_ sql : String) throws {
-
- var pStmt : OpaquePointer?
- let rc = sqlite3_prepare_v2(_db, sql, -1, &pStmt, nil)
- sqlite3_finalize(pStmt);
-
- if (rc != SQLITE_OK) {
- throw FMDBError.sqlite3ErrorCode(rc)
- }
-
- }
-
- func tableExists(_ tableName : String) -> Bool {
-
- do {
-
- let tableNameLower = tableName.lowercased()
-
- let rs = try self.executeQuery("select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableNameLower)
-
- let returnBool = try rs.next()
-
- rs.close()
-
- return returnBool
- }
- catch {
- return false
- }
-
- }
-
- func columnExists(_ columnName : String, inTable: String) -> Bool {
-
- do {
-
- let lowerTableName = inTable.lowercased()
- let lowerColumnName = columnName.lowercased()
-
- var returnBool = false
-
- let rs = try self.getTableSchema(lowerTableName)
-
- while (try rs.next()) {
-
- if (rs.string("name")?.lowercased() == lowerColumnName) {
- returnBool = true
- break
- }
- }
-
- rs.close()
-
- return returnBool
- }
- catch {
- return false
- }
-
- }
-
- func getSchema() -> FMResultSet? {
-
- var rs : FMResultSet?
-
- do {
-
- 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")
- }
- catch {
-
- print("getSchema has failed.")
- }
-
- return rs;
- }
-
- func setUserVersion(_ version: Int32) {
-
-
- var rs : FMResultSet?
-
- do {
-
- rs = try executeQuery("pragma user_version = \(version)")
- try rs?.next()
- rs?.close()
- }
- catch {
- print("setUserVersion has failed.")
- }
-
- }
-
- func userVersion() -> Int32 {
-
- var rs : FMResultSet
-
- do {
-
- rs = try executeQuery("pragma user_version")
- if (try rs.next()) {
- let r = rs.int(0)
- rs.close()
- return r;
- }
- }
- catch {
- print("userVersion has failed.")
- }
-
- return 0
- }
-
- func setApplicationID(_ appID:UInt32) {
-
- do {
- let rs = try executeQuery("pragma application_id=\(appID)")
- try rs.next()
- rs.close()
- }
- catch {
- print("setApplicationID has failed.")
- }
-
- }
-
-
- func applicationID() -> UInt32 {
- do {
-
- var r : UInt32 = 0
-
- let rs = try executeQuery("pragma application_id")
- if (try rs.next()) {
- r = UInt32(rs.longLongInt(0))
- }
-
- rs.close()
-
- return r
-
- }
- catch {
- print("applicationID has failed.")
- }
-
- return 0
- }
-
- func setApplicationIDString(_ appID : String) {
-
- if (appID.count != 4) {
- print("setApplicationIDString: string passed is not exactly 4 chars long. (was \(appID.count))");
- }
-
-
- // var r : FourCharCode = 0
- // for char in appID.utf16 {
- // r = (r << 8) + FourCharCode(char)
- // }
- //
-
- let typeCode = NSHFSTypeCodeFromFileType("'\(appID)'")
-
- setApplicationID(typeCode)
-
- }
-
- func applicationIDString() -> String? {
-
- let s = NSFileTypeForHFSTypeCode(applicationID())
-
- if let s = s {
-
- if (s.count != 6) {
- return nil
- }
-
- let lowerBound = s.index(s.startIndex, offsetBy: 1)
- let upperBound = s.index(s.startIndex, offsetBy: 5)
-
- return String(s[lowerBound..<upperBound])
-
- //return NSString(string:s).substring(with: NSMakeRange(1, 4))
- }
-
- return nil
-
-
-
-
-
-
-
-
- }
-
- func warnInUse() {
-
- print("The FMDatabase \(self) is currently in use.");
-
- if (crashOnErrors) {
- assert(false, "The FMDatabase \(self) is currently in use.");
- abort();
- }
- }
-
-
- }
|