// // SQLite.swift // Swifter // // Copyright (c) 2014-2016 Damian KoĊ‚akowski. All rights reserved. // import Foundation enum SQLiteError: ErrorType { case OpenFailed(String?) case ExecFailed(String?) } public class SQLite { private let databaseConnection: COpaquePointer public static func open(path: String) throws -> SQLite { var databaseConnection = COpaquePointer() let openResult = path.withCString { sqlite3_open($0, &databaseConnection) } guard openResult == SQLITE_OK else { throw SQLiteError.OpenFailed(String.fromCString(sqlite3_errmsg(databaseConnection))) } return SQLite(databaseConnection) } private init(_ databaseConnection: COpaquePointer) { self.databaseConnection = databaseConnection } private struct ExecCContext { var callback: ([String: String] -> Void)? } public func exec(sql: String) throws { try exec(sql, callback: nil) } public func exec(sql: String, callback: ([String: String] -> Void)?) throws { var errorMessagePointer = UnsafeMutablePointer() var execCContext = ExecCContext(callback: callback) let execResult = sql.withCString { sqlite3_exec(databaseConnection, $0, { (context, count, values, names) -> Int32 in var content = [String: String]() for i in 0..(context).memory.callback { callback(content) } return SQLITE_OK }, &execCContext, &errorMessagePointer) } guard execResult == SQLITE_OK else { let errorDetails = String.fromCString(errorMessagePointer) sqlite3_free(errorMessagePointer) throw SQLiteError.ExecFailed(errorDetails) } } public func enumerate(sql: String) throws -> StatmentSequence { var statement = COpaquePointer() let prepeareResult = sql.withCString { sqlite3_prepare_v2(databaseConnection, $0, Int32(sql.utf8.count), &statement, nil) } guard prepeareResult == SQLITE_OK else { throw SQLiteError.ExecFailed(String.fromCString(sqlite3_errmsg(databaseConnection))) } return StatmentSequence(statement: statement) } public struct StatmentGenerator: GeneratorType { public let statement: COpaquePointer public func next() -> [String: String]? { switch sqlite3_step(statement) { case SQLITE_ROW: var content = [String: String]() for i in 0..(sqlite3_column_name(statement, i))), let value = String.fromCString(UnsafePointer(sqlite3_column_text(statement, i))) { content[name] = value } } return content default: sqlite3_finalize(statement) return nil } } } public struct StatmentSequence: SequenceType { public let statement: COpaquePointer public func generate() -> StatmentGenerator { return StatmentGenerator(statement: statement) } } public func close() throws { sqlite3_close(databaseConnection) } }