Dactyloidae iOS initial commit

This commit is contained in:
wuggy 2026-06-26 21:04:09 -07:00
commit 7154a0497e
2123 changed files with 197052 additions and 0 deletions

View file

@ -0,0 +1,60 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
public struct Blob {
public let bytes: [UInt8]
public init(bytes: [UInt8]) {
self.bytes = bytes
}
public init(bytes: UnsafeRawPointer, length: Int) {
let i8bufptr = UnsafeBufferPointer(start: bytes.assumingMemoryBound(to: UInt8.self), count: length)
self.init(bytes: [UInt8](i8bufptr))
}
public func toHex() -> String {
return bytes.map {
($0 < 16 ? "0" : "") + String($0, radix: 16, uppercase: false)
}.joined(separator: "")
}
}
extension Blob : CustomStringConvertible {
public var description: String {
return "x'\(toHex())'"
}
}
extension Blob : Equatable {
}
public func ==(lhs: Blob, rhs: Blob) -> Bool {
return lhs.bytes == rhs.bytes
}

View file

@ -0,0 +1,756 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation.NSUUID
import Dispatch
#if SQLITE_SWIFT_STANDALONE
import sqlite3
#elseif SQLITE_SWIFT_SQLCIPHER
import SQLCipher
#else
import SQLite3
#endif
/// A connection to SQLite.
public final class Connection {
/// The location of a SQLite database.
public enum Location {
/// An in-memory database (equivalent to `.URI(":memory:")`).
///
/// See: <https://www.sqlite.org/inmemorydb.html#sharedmemdb>
case inMemory
/// A temporary, file-backed database (equivalent to `.URI("")`).
///
/// See: <https://www.sqlite.org/inmemorydb.html#temp_db>
case temporary
/// A database located at the given URI filename (or path).
///
/// See: <https://www.sqlite.org/uri.html>
///
/// - Parameter filename: A URI filename
case uri(String)
}
/// An SQL operation passed to update callbacks.
public enum Operation {
/// An INSERT operation.
case insert
/// An UPDATE operation.
case update
/// A DELETE operation.
case delete
fileprivate init(rawValue:Int32) {
switch rawValue {
case SQLITE_INSERT:
self = .insert
case SQLITE_UPDATE:
self = .update
case SQLITE_DELETE:
self = .delete
default:
fatalError("unhandled operation code: \(rawValue)")
}
}
}
public var handle: OpaquePointer { return _handle! }
fileprivate var _handle: OpaquePointer? = nil
/// Initializes a new SQLite connection.
///
/// - Parameters:
///
/// - location: The location of the database. Creates a new database if it
/// doesnt already exist (unless in read-only mode).
///
/// Default: `.InMemory`.
///
/// - readonly: Whether or not to open the database in a read-only state.
///
/// Default: `false`.
///
/// - Returns: A new database connection.
public init(_ location: Location = .inMemory, readonly: Bool = false) throws {
let flags = readonly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE
try check(sqlite3_open_v2(location.description, &_handle, flags | SQLITE_OPEN_FULLMUTEX, nil))
queue.setSpecific(key: Connection.queueKey, value: queueContext)
}
/// Initializes a new connection to a database.
///
/// - Parameters:
///
/// - filename: The location of the database. Creates a new database if
/// it doesnt already exist (unless in read-only mode).
///
/// - readonly: Whether or not to open the database in a read-only state.
///
/// Default: `false`.
///
/// - Throws: `Result.Error` iff a connection cannot be established.
///
/// - Returns: A new database connection.
public convenience init(_ filename: String, readonly: Bool = false) throws {
try self.init(.uri(filename), readonly: readonly)
}
deinit {
sqlite3_close(handle)
}
// MARK: -
/// Whether or not the database was opened in a read-only state.
public var readonly: Bool { return sqlite3_db_readonly(handle, nil) == 1 }
/// The last rowid inserted into the database via this connection.
public var lastInsertRowid: Int64 {
return sqlite3_last_insert_rowid(handle)
}
/// The last number of changes (inserts, updates, or deletes) made to the
/// database via this connection.
public var changes: Int {
return Int(sqlite3_changes(handle))
}
/// The total number of changes (inserts, updates, or deletes) made to the
/// database via this connection.
public var totalChanges: Int {
return Int(sqlite3_total_changes(handle))
}
// MARK: - Execute
/// Executes a batch of SQL statements.
///
/// - Parameter SQL: A batch of zero or more semicolon-separated SQL
/// statements.
///
/// - Throws: `Result.Error` if query execution fails.
public func execute(_ SQL: String) throws {
_ = try sync { try self.check(sqlite3_exec(self.handle, SQL, nil, nil, nil)) }
}
// MARK: - Prepare
/// Prepares a single SQL statement (with optional parameter bindings).
///
/// - Parameters:
///
/// - statement: A single SQL statement.
///
/// - bindings: A list of parameters to bind to the statement.
///
/// - Returns: A prepared statement.
public func prepare(_ statement: String, _ bindings: Binding?...) throws -> Statement {
if !bindings.isEmpty { return try prepare(statement, bindings) }
return try Statement(self, statement)
}
/// Prepares a single SQL statement and binds parameters to it.
///
/// - Parameters:
///
/// - statement: A single SQL statement.
///
/// - bindings: A list of parameters to bind to the statement.
///
/// - Returns: A prepared statement.
public func prepare(_ statement: String, _ bindings: [Binding?]) throws -> Statement {
return try prepare(statement).bind(bindings)
}
/// Prepares a single SQL statement and binds parameters to it.
///
/// - Parameters:
///
/// - statement: A single SQL statement.
///
/// - bindings: A dictionary of named parameters to bind to the statement.
///
/// - Returns: A prepared statement.
public func prepare(_ statement: String, _ bindings: [String: Binding?]) throws -> Statement {
return try prepare(statement).bind(bindings)
}
// MARK: - Run
/// Runs a single SQL statement (with optional parameter bindings).
///
/// - Parameters:
///
/// - statement: A single SQL statement.
///
/// - bindings: A list of parameters to bind to the statement.
///
/// - Throws: `Result.Error` if query execution fails.
///
/// - Returns: The statement.
@discardableResult public func run(_ statement: String, _ bindings: Binding?...) throws -> Statement {
return try run(statement, bindings)
}
/// Prepares, binds, and runs a single SQL statement.
///
/// - Parameters:
///
/// - statement: A single SQL statement.
///
/// - bindings: A list of parameters to bind to the statement.
///
/// - Throws: `Result.Error` if query execution fails.
///
/// - Returns: The statement.
@discardableResult public func run(_ statement: String, _ bindings: [Binding?]) throws -> Statement {
return try prepare(statement).run(bindings)
}
/// Prepares, binds, and runs a single SQL statement.
///
/// - Parameters:
///
/// - statement: A single SQL statement.
///
/// - bindings: A dictionary of named parameters to bind to the statement.
///
/// - Throws: `Result.Error` if query execution fails.
///
/// - Returns: The statement.
@discardableResult public func run(_ statement: String, _ bindings: [String: Binding?]) throws -> Statement {
return try prepare(statement).run(bindings)
}
// MARK: - Scalar
/// Runs a single SQL statement (with optional parameter bindings),
/// returning the first value of the first row.
///
/// - Parameters:
///
/// - statement: A single SQL statement.
///
/// - bindings: A list of parameters to bind to the statement.
///
/// - Returns: The first value of the first row returned.
public func scalar(_ statement: String, _ bindings: Binding?...) throws -> Binding? {
return try scalar(statement, bindings)
}
/// Runs a single SQL statement (with optional parameter bindings),
/// returning the first value of the first row.
///
/// - Parameters:
///
/// - statement: A single SQL statement.
///
/// - bindings: A list of parameters to bind to the statement.
///
/// - Returns: The first value of the first row returned.
public func scalar(_ statement: String, _ bindings: [Binding?]) throws -> Binding? {
return try prepare(statement).scalar(bindings)
}
/// Runs a single SQL statement (with optional parameter bindings),
/// returning the first value of the first row.
///
/// - Parameters:
///
/// - statement: A single SQL statement.
///
/// - bindings: A dictionary of named parameters to bind to the statement.
///
/// - Returns: The first value of the first row returned.
public func scalar(_ statement: String, _ bindings: [String: Binding?]) throws -> Binding? {
return try prepare(statement).scalar(bindings)
}
// MARK: - Transactions
/// The mode in which a transaction acquires a lock.
public enum TransactionMode : String {
/// Defers locking the database till the first read/write executes.
case deferred = "DEFERRED"
/// Immediately acquires a reserved lock on the database.
case immediate = "IMMEDIATE"
/// Immediately acquires an exclusive lock on all databases.
case exclusive = "EXCLUSIVE"
}
// TODO: Consider not requiring a throw to roll back?
/// Runs a transaction with the given mode.
///
/// - Note: Transactions cannot be nested. To nest transactions, see
/// `savepoint()`, instead.
///
/// - Parameters:
///
/// - mode: The mode in which a transaction acquires a lock.
///
/// Default: `.Deferred`
///
/// - block: A closure to run SQL statements within the transaction.
/// The transaction will be committed when the block returns. The block
/// must throw to roll the transaction back.
///
/// - Throws: `Result.Error`, and rethrows.
public func transaction(_ mode: TransactionMode = .deferred, block: @escaping () throws -> Void) throws {
try transaction("BEGIN \(mode.rawValue) TRANSACTION", block, "COMMIT TRANSACTION", or: "ROLLBACK TRANSACTION")
}
// TODO: Consider not requiring a throw to roll back?
// TODO: Consider removing ability to set a name?
/// Runs a transaction with the given savepoint name (if omitted, it will
/// generate a UUID).
///
/// - SeeAlso: `transaction()`.
///
/// - Parameters:
///
/// - savepointName: A unique identifier for the savepoint (optional).
///
/// - block: A closure to run SQL statements within the transaction.
/// The savepoint will be released (committed) when the block returns.
/// The block must throw to roll the savepoint back.
///
/// - Throws: `SQLite.Result.Error`, and rethrows.
public func savepoint(_ name: String = UUID().uuidString, block: @escaping () throws -> Void) throws {
let name = name.quote("'")
let savepoint = "SAVEPOINT \(name)"
try transaction(savepoint, block, "RELEASE \(savepoint)", or: "ROLLBACK TO \(savepoint)")
}
fileprivate func transaction(_ begin: String, _ block: @escaping () throws -> Void, _ commit: String, or rollback: String) throws {
return try sync {
try self.run(begin)
do {
try block()
} catch {
try self.run(rollback)
throw error
}
try self.run(commit)
}
}
/// Interrupts any long-running queries.
public func interrupt() {
sqlite3_interrupt(handle)
}
// MARK: - Handlers
/// The number of seconds a connection will attempt to retry a statement
/// after encountering a busy signal (lock).
public var busyTimeout: Double = 0 {
didSet {
sqlite3_busy_timeout(handle, Int32(busyTimeout * 1_000))
}
}
/// Sets a handler to call after encountering a busy signal (lock).
///
/// - Parameter callback: This block is executed during a lock in which a
/// busy error would otherwise be returned. Its passed the number of
/// times its been called for this lock. If it returns `true`, it will
/// try again. If it returns `false`, no further attempts will be made.
public func busyHandler(_ callback: ((_ tries: Int) -> Bool)?) {
guard let callback = callback else {
sqlite3_busy_handler(handle, nil, nil)
busyHandler = nil
return
}
let box: BusyHandler = { callback(Int($0)) ? 1 : 0 }
sqlite3_busy_handler(handle, { callback, tries in
unsafeBitCast(callback, to: BusyHandler.self)(tries)
}, unsafeBitCast(box, to: UnsafeMutableRawPointer.self))
busyHandler = box
}
fileprivate typealias BusyHandler = @convention(block) (Int32) -> Int32
fileprivate var busyHandler: BusyHandler?
/// Sets a handler to call when a statement is executed with the compiled
/// SQL.
///
/// - Parameter callback: This block is invoked when a statement is executed
/// with the compiled SQL as its argument.
///
/// db.trace { SQL in print(SQL) }
public func trace(_ callback: ((String) -> Void)?) {
#if SQLITE_SWIFT_SQLCIPHER
trace_v1(callback)
#else
if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
trace_v2(callback)
} else {
trace_v1(callback)
}
#endif
}
fileprivate func trace_v1(_ callback: ((String) -> Void)?) {
guard let callback = callback else {
sqlite3_trace(handle, nil /* xCallback */, nil /* pCtx */)
trace = nil
return
}
let box: Trace = { (pointer: UnsafeRawPointer) in
callback(String(cString: pointer.assumingMemoryBound(to: UInt8.self)))
}
sqlite3_trace(handle,
{
(C: UnsafeMutableRawPointer?, SQL: UnsafePointer<Int8>?) in
if let C = C, let SQL = SQL {
unsafeBitCast(C, to: Trace.self)(SQL)
}
},
unsafeBitCast(box, to: UnsafeMutableRawPointer.self)
)
trace = box
}
fileprivate typealias Trace = @convention(block) (UnsafeRawPointer) -> Void
fileprivate var trace: Trace?
/// Registers a callback to be invoked whenever a row is inserted, updated,
/// or deleted in a rowid table.
///
/// - Parameter callback: A callback invoked with the `Operation` (one of
/// `.Insert`, `.Update`, or `.Delete`), database name, table name, and
/// rowid.
public func updateHook(_ callback: ((_ operation: Operation, _ db: String, _ table: String, _ rowid: Int64) -> Void)?) {
guard let callback = callback else {
sqlite3_update_hook(handle, nil, nil)
updateHook = nil
return
}
let box: UpdateHook = {
callback(
Operation(rawValue: $0),
String(cString: $1),
String(cString: $2),
$3
)
}
sqlite3_update_hook(handle, { callback, operation, db, table, rowid in
unsafeBitCast(callback, to: UpdateHook.self)(operation, db!, table!, rowid)
}, unsafeBitCast(box, to: UnsafeMutableRawPointer.self))
updateHook = box
}
fileprivate typealias UpdateHook = @convention(block) (Int32, UnsafePointer<Int8>, UnsafePointer<Int8>, Int64) -> Void
fileprivate var updateHook: UpdateHook?
/// Registers a callback to be invoked whenever a transaction is committed.
///
/// - Parameter callback: A callback invoked whenever a transaction is
/// committed. If this callback throws, the transaction will be rolled
/// back.
public func commitHook(_ callback: (() throws -> Void)?) {
guard let callback = callback else {
sqlite3_commit_hook(handle, nil, nil)
commitHook = nil
return
}
let box: CommitHook = {
do {
try callback()
} catch {
return 1
}
return 0
}
sqlite3_commit_hook(handle, { callback in
unsafeBitCast(callback, to: CommitHook.self)()
}, unsafeBitCast(box, to: UnsafeMutableRawPointer.self))
commitHook = box
}
fileprivate typealias CommitHook = @convention(block) () -> Int32
fileprivate var commitHook: CommitHook?
/// Registers a callback to be invoked whenever a transaction rolls back.
///
/// - Parameter callback: A callback invoked when a transaction is rolled
/// back.
public func rollbackHook(_ callback: (() -> Void)?) {
guard let callback = callback else {
sqlite3_rollback_hook(handle, nil, nil)
rollbackHook = nil
return
}
let box: RollbackHook = { callback() }
sqlite3_rollback_hook(handle, { callback in
unsafeBitCast(callback, to: RollbackHook.self)()
}, unsafeBitCast(box, to: UnsafeMutableRawPointer.self))
rollbackHook = box
}
fileprivate typealias RollbackHook = @convention(block) () -> Void
fileprivate var rollbackHook: RollbackHook?
/// Creates or redefines a custom SQL function.
///
/// - Parameters:
///
/// - function: The name of the function to create or redefine.
///
/// - argumentCount: The number of arguments that the function takes. If
/// `nil`, the function may take any number of arguments.
///
/// Default: `nil`
///
/// - deterministic: Whether or not the function is deterministic (_i.e._
/// the function always returns the same result for a given input).
///
/// Default: `false`
///
/// - block: A block of code to run when the function is called. The block
/// is called with an array of raw SQL values mapped to the functions
/// parameters and should return a raw SQL value (or nil).
public func createFunction(_ function: String, argumentCount: UInt? = nil, deterministic: Bool = false, _ block: @escaping (_ args: [Binding?]) -> Binding?) {
let argc = argumentCount.map { Int($0) } ?? -1
let box: Function = { context, argc, argv in
let arguments: [Binding?] = (0..<Int(argc)).map { idx in
let value = argv![idx]
switch sqlite3_value_type(value) {
case SQLITE_BLOB:
return Blob(bytes: sqlite3_value_blob(value), length: Int(sqlite3_value_bytes(value)))
case SQLITE_FLOAT:
return sqlite3_value_double(value)
case SQLITE_INTEGER:
return sqlite3_value_int64(value)
case SQLITE_NULL:
return nil
case SQLITE_TEXT:
return String(cString: UnsafePointer(sqlite3_value_text(value)))
case let type:
fatalError("unsupported value type: \(type)")
}
}
let result = block(arguments)
if let result = result as? Blob {
sqlite3_result_blob(context, result.bytes, Int32(result.bytes.count), nil)
} else if let result = result as? Double {
sqlite3_result_double(context, result)
} else if let result = result as? Int64 {
sqlite3_result_int64(context, result)
} else if let result = result as? String {
sqlite3_result_text(context, result, Int32(result.characters.count), SQLITE_TRANSIENT)
} else if result == nil {
sqlite3_result_null(context)
} else {
fatalError("unsupported result type: \(result)")
}
}
var flags = SQLITE_UTF8
if deterministic {
flags |= SQLITE_DETERMINISTIC
}
sqlite3_create_function_v2(handle, function, Int32(argc), flags, unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { context, argc, value in
let function = unsafeBitCast(sqlite3_user_data(context), to: Function.self)
function(context, argc, value)
}, nil, nil, nil)
if functions[function] == nil { self.functions[function] = [:] }
functions[function]?[argc] = box
}
fileprivate typealias Function = @convention(block) (OpaquePointer?, Int32, UnsafeMutablePointer<OpaquePointer?>?) -> Void
fileprivate var functions = [String: [Int: Function]]()
/// Defines a new collating sequence.
///
/// - Parameters:
///
/// - collation: The name of the collation added.
///
/// - block: A collation function that takes two strings and returns the
/// comparison result.
public func createCollation(_ collation: String, _ block: @escaping (_ lhs: String, _ rhs: String) -> ComparisonResult) throws {
let box: Collation = { (lhs: UnsafeRawPointer, rhs: UnsafeRawPointer) in
let lstr = String(cString: lhs.assumingMemoryBound(to: UInt8.self))
let rstr = String(cString: rhs.assumingMemoryBound(to: UInt8.self))
return Int32(block(lstr, rstr).rawValue)
}
try check(sqlite3_create_collation_v2(handle, collation, SQLITE_UTF8,
unsafeBitCast(box, to: UnsafeMutableRawPointer.self),
{ (callback: UnsafeMutableRawPointer?, _, lhs: UnsafeRawPointer?, _, rhs: UnsafeRawPointer?) in /* xCompare */
if let lhs = lhs, let rhs = rhs {
return unsafeBitCast(callback, to: Collation.self)(lhs, rhs)
} else {
fatalError("sqlite3_create_collation_v2 callback called with NULL pointer")
}
}, nil /* xDestroy */))
collations[collation] = box
}
fileprivate typealias Collation = @convention(block) (UnsafeRawPointer, UnsafeRawPointer) -> Int32
fileprivate var collations = [String: Collation]()
// MARK: - Error Handling
func sync<T>(_ block: @escaping () throws -> T) rethrows -> T {
var success: T?
var failure: Error?
let box: () -> Void = {
do {
success = try block()
} catch {
failure = error
}
}
if DispatchQueue.getSpecific(key: Connection.queueKey) == queueContext {
box()
} else {
queue.sync(execute: box) // FIXME: rdar://problem/21389236
}
if let failure = failure {
try { () -> Void in throw failure }()
}
return success!
}
@discardableResult func check(_ resultCode: Int32, statement: Statement? = nil) throws -> Int32 {
guard let error = Result(errorCode: resultCode, connection: self, statement: statement) else {
return resultCode
}
throw error
}
fileprivate var queue = DispatchQueue(label: "SQLite.Database", attributes: [])
fileprivate static let queueKey = DispatchSpecificKey<Int>()
fileprivate lazy var queueContext: Int = unsafeBitCast(self, to: Int.self)
}
extension Connection : CustomStringConvertible {
public var description: String {
return String(cString: sqlite3_db_filename(handle, nil))
}
}
extension Connection.Location : CustomStringConvertible {
public var description: String {
switch self {
case .inMemory:
return ":memory:"
case .temporary:
return ""
case .uri(let URI):
return URI
}
}
}
public enum Result : Error {
fileprivate static let successCodes: Set = [SQLITE_OK, SQLITE_ROW, SQLITE_DONE]
case error(message: String, code: Int32, statement: Statement?)
init?(errorCode: Int32, connection: Connection, statement: Statement? = nil) {
guard !Result.successCodes.contains(errorCode) else { return nil }
let message = String(cString: sqlite3_errmsg(connection.handle))
self = .error(message: message, code: errorCode, statement: statement)
}
}
extension Result : CustomStringConvertible {
public var description: String {
switch self {
case let .error(message, errorCode, statement):
if let statement = statement {
return "\(message) (\(statement)) (code: \(errorCode))"
} else {
return "\(message) (code: \(errorCode))"
}
}
}
}
#if !SQLITE_SWIFT_SQLCIPHER
@available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *)
extension Connection {
fileprivate func trace_v2(_ callback: ((String) -> Void)?) {
guard let callback = callback else {
// If the X callback is NULL or if the M mask is zero, then tracing is disabled.
sqlite3_trace_v2(handle, 0 /* mask */, nil /* xCallback */, nil /* pCtx */)
trace = nil
return
}
let box: Trace = { (pointer: UnsafeRawPointer) in
callback(String(cString: pointer.assumingMemoryBound(to: UInt8.self)))
}
sqlite3_trace_v2(handle,
UInt32(SQLITE_TRACE_STMT) /* mask */,
{
// A trace callback is invoked with four arguments: callback(T,C,P,X).
// The T argument is one of the SQLITE_TRACE constants to indicate why the
// callback was invoked. The C argument is a copy of the context pointer.
// The P and X arguments are pointers whose meanings depend on T.
(T: UInt32, C: UnsafeMutableRawPointer?, P: UnsafeMutableRawPointer?, X: UnsafeMutableRawPointer?) in
if let P = P,
let expandedSQL = sqlite3_expanded_sql(OpaquePointer(P)) {
unsafeBitCast(C, to: Trace.self)(expandedSQL)
sqlite3_free(expandedSQL)
}
return Int32(0) // currently ignored
},
unsafeBitCast(box, to: UnsafeMutableRawPointer.self) /* pCtx */
)
trace = box
}
}
#endif

View file

@ -0,0 +1,297 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#if SQLITE_SWIFT_STANDALONE
import sqlite3
#elseif SQLITE_SWIFT_SQLCIPHER
import SQLCipher
#else
import SQLite3
#endif
/// A single SQL statement.
public final class Statement {
fileprivate var handle: OpaquePointer? = nil
fileprivate let connection: Connection
init(_ connection: Connection, _ SQL: String) throws {
self.connection = connection
try connection.check(sqlite3_prepare_v2(connection.handle, SQL, -1, &handle, nil))
}
deinit {
sqlite3_finalize(handle)
}
public lazy var columnCount: Int = Int(sqlite3_column_count(self.handle))
public lazy var columnNames: [String] = (0..<Int32(self.columnCount)).map {
String(cString: sqlite3_column_name(self.handle, $0))
}
/// A cursor pointing to the current row.
public lazy var row: Cursor = Cursor(self)
/// Binds a list of parameters to a statement.
///
/// - Parameter values: A list of parameters to bind to the statement.
///
/// - Returns: The statement object (useful for chaining).
public func bind(_ values: Binding?...) -> Statement {
return bind(values)
}
/// Binds a list of parameters to a statement.
///
/// - Parameter values: A list of parameters to bind to the statement.
///
/// - Returns: The statement object (useful for chaining).
public func bind(_ values: [Binding?]) -> Statement {
if values.isEmpty { return self }
reset()
guard values.count == Int(sqlite3_bind_parameter_count(handle)) else {
fatalError("\(sqlite3_bind_parameter_count(handle)) values expected, \(values.count) passed")
}
for idx in 1...values.count { bind(values[idx - 1], atIndex: idx) }
return self
}
/// Binds a dictionary of named parameters to a statement.
///
/// - Parameter values: A dictionary of named parameters to bind to the
/// statement.
///
/// - Returns: The statement object (useful for chaining).
public func bind(_ values: [String: Binding?]) -> Statement {
reset()
for (name, value) in values {
let idx = sqlite3_bind_parameter_index(handle, name)
guard idx > 0 else {
fatalError("parameter not found: \(name)")
}
bind(value, atIndex: Int(idx))
}
return self
}
fileprivate func bind(_ value: Binding?, atIndex idx: Int) {
if value == nil {
sqlite3_bind_null(handle, Int32(idx))
} else if let value = value as? Blob {
sqlite3_bind_blob(handle, Int32(idx), value.bytes, Int32(value.bytes.count), SQLITE_TRANSIENT)
} else if let value = value as? Double {
sqlite3_bind_double(handle, Int32(idx), value)
} else if let value = value as? Int64 {
sqlite3_bind_int64(handle, Int32(idx), value)
} else if let value = value as? String {
sqlite3_bind_text(handle, Int32(idx), value, -1, SQLITE_TRANSIENT)
} else if let value = value as? Int {
self.bind(value.datatypeValue, atIndex: idx)
} else if let value = value as? Bool {
self.bind(value.datatypeValue, atIndex: idx)
} else if let value = value {
fatalError("tried to bind unexpected value \(value)")
}
}
/// - Parameter bindings: A list of parameters to bind to the statement.
///
/// - Throws: `Result.Error` if query execution fails.
///
/// - Returns: The statement object (useful for chaining).
@discardableResult public func run(_ bindings: Binding?...) throws -> Statement {
guard bindings.isEmpty else {
return try run(bindings)
}
reset(clearBindings: false)
repeat {} while try step()
return self
}
/// - Parameter bindings: A list of parameters to bind to the statement.
///
/// - Throws: `Result.Error` if query execution fails.
///
/// - Returns: The statement object (useful for chaining).
@discardableResult public func run(_ bindings: [Binding?]) throws -> Statement {
return try bind(bindings).run()
}
/// - Parameter bindings: A dictionary of named parameters to bind to the
/// statement.
///
/// - Throws: `Result.Error` if query execution fails.
///
/// - Returns: The statement object (useful for chaining).
@discardableResult public func run(_ bindings: [String: Binding?]) throws -> Statement {
return try bind(bindings).run()
}
/// - Parameter bindings: A list of parameters to bind to the statement.
///
/// - Returns: The first value of the first row returned.
public func scalar(_ bindings: Binding?...) throws -> Binding? {
guard bindings.isEmpty else {
return try scalar(bindings)
}
reset(clearBindings: false)
_ = try step()
return row[0]
}
/// - Parameter bindings: A list of parameters to bind to the statement.
///
/// - Returns: The first value of the first row returned.
public func scalar(_ bindings: [Binding?]) throws -> Binding? {
return try bind(bindings).scalar()
}
/// - Parameter bindings: A dictionary of named parameters to bind to the
/// statement.
///
/// - Returns: The first value of the first row returned.
public func scalar(_ bindings: [String: Binding?]) throws -> Binding? {
return try bind(bindings).scalar()
}
public func step() throws -> Bool {
return try connection.sync { try self.connection.check(sqlite3_step(self.handle)) == SQLITE_ROW }
}
fileprivate func reset(clearBindings shouldClear: Bool = true) {
sqlite3_reset(handle)
if (shouldClear) { sqlite3_clear_bindings(handle) }
}
}
extension Statement : Sequence {
public func makeIterator() -> Statement {
reset(clearBindings: false)
return self
}
}
extension Statement : IteratorProtocol {
public func next() -> [Binding?]? {
return try! step() ? Array(row) : nil
}
}
extension Statement : CustomStringConvertible {
public var description: String {
return String(cString: sqlite3_sql(handle))
}
}
public struct Cursor {
fileprivate let handle: OpaquePointer
fileprivate let columnCount: Int
fileprivate init(_ statement: Statement) {
handle = statement.handle!
columnCount = statement.columnCount
}
public subscript(idx: Int) -> Double {
return sqlite3_column_double(handle, Int32(idx))
}
public subscript(idx: Int) -> Int64 {
return sqlite3_column_int64(handle, Int32(idx))
}
public subscript(idx: Int) -> String {
return String(cString: UnsafePointer(sqlite3_column_text(handle, Int32(idx))))
}
public subscript(idx: Int) -> Blob {
if let pointer = sqlite3_column_blob(handle, Int32(idx)) {
let length = Int(sqlite3_column_bytes(handle, Int32(idx)))
return Blob(bytes: pointer, length: length)
} else {
// The return value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
// https://www.sqlite.org/c3ref/column_blob.html
return Blob(bytes: [])
}
}
// MARK: -
public subscript(idx: Int) -> Bool {
return Bool.fromDatatypeValue(self[idx])
}
public subscript(idx: Int) -> Int {
return Int.fromDatatypeValue(self[idx])
}
}
/// Cursors provide direct access to a statements current row.
extension Cursor : Sequence {
public subscript(idx: Int) -> Binding? {
switch sqlite3_column_type(handle, Int32(idx)) {
case SQLITE_BLOB:
return self[idx] as Blob
case SQLITE_FLOAT:
return self[idx] as Double
case SQLITE_INTEGER:
return self[idx] as Int64
case SQLITE_NULL:
return nil
case SQLITE_TEXT:
return self[idx] as String
case let type:
fatalError("unsupported column type: \(type)")
}
}
public func makeIterator() -> AnyIterator<Binding?> {
var idx = 0
return AnyIterator {
if idx >= self.columnCount {
return Optional<Binding?>.none
} else {
idx += 1
return self[idx - 1]
}
}
}
}

View file

@ -0,0 +1,132 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
/// - Warning: `Binding` is a protocol that SQLite.swift uses internally to
/// directly map SQLite types to Swift types.
///
/// Do not conform custom types to the Binding protocol. See the `Value`
/// protocol, instead.
public protocol Binding {}
public protocol Number : Binding {}
public protocol Value : Expressible { // extensions cannot have inheritance clauses
associatedtype ValueType = Self
associatedtype Datatype : Binding
static var declaredDatatype: String { get }
static func fromDatatypeValue(_ datatypeValue: Datatype) -> ValueType
var datatypeValue: Datatype { get }
}
extension Double : Number, Value {
public static let declaredDatatype = "REAL"
public static func fromDatatypeValue(_ datatypeValue: Double) -> Double {
return datatypeValue
}
public var datatypeValue: Double {
return self
}
}
extension Int64 : Number, Value {
public static let declaredDatatype = "INTEGER"
public static func fromDatatypeValue(_ datatypeValue: Int64) -> Int64 {
return datatypeValue
}
public var datatypeValue: Int64 {
return self
}
}
extension String : Binding, Value {
public static let declaredDatatype = "TEXT"
public static func fromDatatypeValue(_ datatypeValue: String) -> String {
return datatypeValue
}
public var datatypeValue: String {
return self
}
}
extension Blob : Binding, Value {
public static let declaredDatatype = "BLOB"
public static func fromDatatypeValue(_ datatypeValue: Blob) -> Blob {
return datatypeValue
}
public var datatypeValue: Blob {
return self
}
}
// MARK: -
extension Bool : Binding, Value {
public static var declaredDatatype = Int64.declaredDatatype
public static func fromDatatypeValue(_ datatypeValue: Int64) -> Bool {
return datatypeValue != 0
}
public var datatypeValue: Int64 {
return self ? 1 : 0
}
}
extension Int : Number, Value {
public static var declaredDatatype = Int64.declaredDatatype
public static func fromDatatypeValue(_ datatypeValue: Int64) -> Int {
return Int(datatypeValue)
}
public var datatypeValue: Int64 {
return Int64(self)
}
}

View file

@ -0,0 +1,61 @@
#if SQLITE_SWIFT_SQLCIPHER
import SQLCipher
/// Extension methods for [SQLCipher](https://www.zetetic.net/sqlcipher/).
/// @see [sqlcipher api](https://www.zetetic.net/sqlcipher/sqlcipher-api/)
extension Connection {
/// Specify the key for an encrypted database. This routine should be
/// called right after sqlite3_open().
///
/// @param key The key to use.The key itself can be a passphrase, which is converted to a key
/// using [PBKDF2](https://en.wikipedia.org/wiki/PBKDF2) key derivation. The result
/// is used as the encryption key for the database.
///
/// Alternatively, it is possible to specify an exact byte sequence using a blob literal.
/// With this method, it is the calling application's responsibility to ensure that the data
/// provided is a 64 character hex string, which will be converted directly to 32 bytes (256 bits)
/// of key data.
/// e.g. x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'
/// @param db name of the database, defaults to 'main'
public func key(_ key: String, db: String = "main") throws {
try _key_v2(db: db, keyPointer: key, keySize: key.utf8.count)
}
public func key(_ key: Blob, db: String = "main") throws {
try _key_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count)
}
/// Change the key on an open database. If the current database is not encrypted, this routine
/// will encrypt it.
/// To change the key on an existing encrypted database, it must first be unlocked with the
/// current encryption key. Once the database is readable and writeable, rekey can be used
/// to re-encrypt every page in the database with a new key.
public func rekey(_ key: String, db: String = "main") throws {
try _rekey_v2(db: db, keyPointer: key, keySize: key.utf8.count)
}
public func rekey(_ key: Blob, db: String = "main") throws {
try _rekey_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count)
}
// MARK: - private
private func _key_v2(db: String, keyPointer: UnsafePointer<UInt8>, keySize: Int) throws {
try check(sqlite3_key_v2(handle, db, keyPointer, Int32(keySize)))
try cipher_key_check()
}
private func _rekey_v2(db: String, keyPointer: UnsafePointer<UInt8>, keySize: Int) throws {
try check(sqlite3_rekey_v2(handle, db, keyPointer, Int32(keySize)))
}
// When opening an existing database, sqlite3_key_v2 will not immediately throw an error if
// the key provided is incorrect. To test that the database can be successfully opened with the
// provided key, it is necessary to perform some operation on the database (i.e. read from it).
private func cipher_key_check() throws {
try scalar("SELECT count(*) FROM sqlite_master;")
}
}
#endif

View file

@ -0,0 +1,346 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#if SWIFT_PACKAGE
import SQLiteObjc
#endif
extension Module {
public static func FTS4(_ column: Expressible, _ more: Expressible...) -> Module {
return FTS4([column] + more)
}
public static func FTS4(_ columns: [Expressible] = [], tokenize tokenizer: Tokenizer? = nil) -> Module {
return FTS4(FTS4Config().columns(columns).tokenizer(tokenizer))
}
public static func FTS4(_ config: FTS4Config) -> Module {
return Module(name: "fts4", arguments: config.arguments())
}
}
extension VirtualTable {
/// Builds an expression appended with a `MATCH` query against the given
/// pattern.
///
/// let emails = VirtualTable("emails")
///
/// emails.filter(emails.match("Hello"))
/// // SELECT * FROM "emails" WHERE "emails" MATCH 'Hello'
///
/// - Parameter pattern: A pattern to match.
///
/// - Returns: An expression appended with a `MATCH` query against the given
/// pattern.
public func match(_ pattern: String) -> Expression<Bool> {
return "MATCH".infix(tableName(), pattern)
}
public func match(_ pattern: Expression<String>) -> Expression<Bool> {
return "MATCH".infix(tableName(), pattern)
}
public func match(_ pattern: Expression<String?>) -> Expression<Bool?> {
return "MATCH".infix(tableName(), pattern)
}
/// Builds a copy of the query with a `WHERE MATCH` clause.
///
/// let emails = VirtualTable("emails")
///
/// emails.match("Hello")
/// // SELECT * FROM "emails" WHERE "emails" MATCH 'Hello'
///
/// - Parameter pattern: A pattern to match.
///
/// - Returns: A query with the given `WHERE MATCH` clause applied.
public func match(_ pattern: String) -> QueryType {
return filter(match(pattern))
}
public func match(_ pattern: Expression<String>) -> QueryType {
return filter(match(pattern))
}
public func match(_ pattern: Expression<String?>) -> QueryType {
return filter(match(pattern))
}
}
public struct Tokenizer {
public static let Simple = Tokenizer("simple")
public static let Porter = Tokenizer("porter")
public static func Unicode61(removeDiacritics: Bool? = nil, tokenchars: Set<Character> = [], separators: Set<Character> = []) -> Tokenizer {
var arguments = [String]()
if let removeDiacritics = removeDiacritics {
arguments.append("removeDiacritics=\(removeDiacritics ? 1 : 0)".quote())
}
if !tokenchars.isEmpty {
let joined = tokenchars.map { String($0) }.joined(separator: "")
arguments.append("tokenchars=\(joined)".quote())
}
if !separators.isEmpty {
let joined = separators.map { String($0) }.joined(separator: "")
arguments.append("separators=\(joined)".quote())
}
return Tokenizer("unicode61", arguments)
}
public static func Custom(_ name: String) -> Tokenizer {
return Tokenizer(Tokenizer.moduleName.quote(), [name.quote()])
}
public let name: String
public let arguments: [String]
fileprivate init(_ name: String, _ arguments: [String] = []) {
self.name = name
self.arguments = arguments
}
fileprivate static let moduleName = "SQLite.swift"
}
extension Tokenizer : CustomStringConvertible {
public var description: String {
return ([name] + arguments).joined(separator: " ")
}
}
extension Connection {
public func registerTokenizer(_ submoduleName: String, next: @escaping (String) -> (String, Range<String.Index>)?) throws {
try check(_SQLiteRegisterTokenizer(handle, Tokenizer.moduleName, submoduleName) { input, offset, length in
let string = String(cString: input)
guard let (token, range) = next(string) else { return nil }
let view = string.utf8
offset.pointee += string.substring(to: range.lowerBound).utf8.count
length.pointee = Int32(view.distance(from: range.lowerBound.samePosition(in: view), to: range.upperBound.samePosition(in: view)))
return token
})
}
}
/// Configuration options shared between the [FTS4](https://www.sqlite.org/fts3.html) and
/// [FTS5](https://www.sqlite.org/fts5.html) extensions.
open class FTSConfig {
public enum ColumnOption {
/// [The notindexed= option](https://www.sqlite.org/fts3.html#section_6_5)
case unindexed
}
typealias ColumnDefinition = (Expressible, options: [ColumnOption])
var columnDefinitions = [ColumnDefinition]()
var tokenizer: Tokenizer?
var prefixes = [Int]()
var externalContentSchema: SchemaType?
var isContentless: Bool = false
/// Adds a column definition
@discardableResult open func column(_ column: Expressible, _ options: [ColumnOption] = []) -> Self {
self.columnDefinitions.append((column, options))
return self
}
@discardableResult open func columns(_ columns: [Expressible]) -> Self {
for column in columns {
self.column(column)
}
return self
}
/// [Tokenizers](https://www.sqlite.org/fts3.html#tokenizer)
open func tokenizer(_ tokenizer: Tokenizer?) -> Self {
self.tokenizer = tokenizer
return self
}
/// [The prefix= option](https://www.sqlite.org/fts3.html#section_6_6)
open func prefix(_ prefix: [Int]) -> Self {
self.prefixes += prefix
return self
}
/// [The content= option](https://www.sqlite.org/fts3.html#section_6_2)
open func externalContent(_ schema: SchemaType) -> Self {
self.externalContentSchema = schema
return self
}
/// [Contentless FTS4 Tables](https://www.sqlite.org/fts3.html#section_6_2_1)
open func contentless() -> Self {
self.isContentless = true
return self
}
func formatColumnDefinitions() -> [Expressible] {
return columnDefinitions.map { $0.0 }
}
func arguments() -> [Expressible] {
return options().arguments
}
func options() -> Options {
var options = Options()
options.append(formatColumnDefinitions())
if let tokenizer = tokenizer {
options.append("tokenize", value: Expression<Void>(literal: tokenizer.description))
}
options.appendCommaSeparated("prefix", values:prefixes.sorted().map { String($0) })
if isContentless {
options.append("content", value: "")
} else if let externalContentSchema = externalContentSchema {
options.append("content", value: externalContentSchema.tableName())
}
return options
}
struct Options {
var arguments = [Expressible]()
@discardableResult mutating func append(_ columns: [Expressible]) -> Options {
arguments.append(contentsOf: columns)
return self
}
@discardableResult mutating func appendCommaSeparated(_ key: String, values: [String]) -> Options {
if values.isEmpty {
return self
} else {
return append(key, value: values.joined(separator: ","))
}
}
@discardableResult mutating func append(_ key: String, value: CustomStringConvertible?) -> Options {
return append(key, value: value?.description)
}
@discardableResult mutating func append(_ key: String, value: String?) -> Options {
return append(key, value: value.map { Expression<String>($0) })
}
@discardableResult mutating func append(_ key: String, value: Expressible?) -> Options {
if let value = value {
arguments.append("=".join([Expression<Void>(literal: key), value]))
}
return self
}
}
}
/// Configuration for the [FTS4](https://www.sqlite.org/fts3.html) extension.
open class FTS4Config : FTSConfig {
/// [The matchinfo= option](https://www.sqlite.org/fts3.html#section_6_4)
public enum MatchInfo : CustomStringConvertible {
case fts3
public var description: String {
return "fts3"
}
}
/// [FTS4 options](https://www.sqlite.org/fts3.html#fts4_options)
public enum Order : CustomStringConvertible {
/// Data structures are optimized for returning results in ascending order by docid (default)
case asc
/// FTS4 stores its data in such a way as to optimize returning results in descending order by docid.
case desc
public var description: String {
switch self {
case .asc: return "asc"
case .desc: return "desc"
}
}
}
var compressFunction: String?
var uncompressFunction: String?
var languageId: String?
var matchInfo: MatchInfo?
var order: Order?
override public init() {
}
/// [The compress= and uncompress= options](https://www.sqlite.org/fts3.html#section_6_1)
open func compress(_ functionName: String) -> Self {
self.compressFunction = functionName
return self
}
/// [The compress= and uncompress= options](https://www.sqlite.org/fts3.html#section_6_1)
open func uncompress(_ functionName: String) -> Self {
self.uncompressFunction = functionName
return self
}
/// [The languageid= option](https://www.sqlite.org/fts3.html#section_6_3)
open func languageId(_ columnName: String) -> Self {
self.languageId = columnName
return self
}
/// [The matchinfo= option](https://www.sqlite.org/fts3.html#section_6_4)
open func matchInfo(_ matchInfo: MatchInfo) -> Self {
self.matchInfo = matchInfo
return self
}
/// [FTS4 options](https://www.sqlite.org/fts3.html#fts4_options)
open func order(_ order: Order) -> Self {
self.order = order
return self
}
override func options() -> Options {
var options = super.options()
for (column, _) in (columnDefinitions.filter { $0.options.contains(.unindexed) }) {
options.append("notindexed", value: column)
}
options.append("languageid", value: languageId)
options.append("compress", value: compressFunction)
options.append("uncompress", value: uncompressFunction)
options.append("matchinfo", value: matchInfo)
options.append("order", value: order)
return options
}
}

View file

@ -0,0 +1,97 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
extension Module {
public static func FTS5(_ config: FTS5Config) -> Module {
return Module(name: "fts5", arguments: config.arguments())
}
}
/// Configuration for the [FTS5](https://www.sqlite.org/fts5.html) extension.
///
/// **Note:** this is currently only applicable when using SQLite.swift together with a FTS5-enabled version
/// of SQLite.
open class FTS5Config : FTSConfig {
public enum Detail : CustomStringConvertible {
/// store rowid, column number, term offset
case full
/// store rowid, column number
case column
/// store rowid
case none
public var description: String {
switch self {
case .full: return "full"
case .column: return "column"
case .none: return "none"
}
}
}
var detail: Detail?
var contentRowId: Expressible?
var columnSize: Int?
override public init() {
}
/// [External Content Tables](https://www.sqlite.org/fts5.html#section_4_4_2)
open func contentRowId(_ column: Expressible) -> Self {
self.contentRowId = column
return self
}
/// [The Columnsize Option](https://www.sqlite.org/fts5.html#section_4_5)
open func columnSize(_ size: Int) -> Self {
self.columnSize = size
return self
}
/// [The Detail Option](https://www.sqlite.org/fts5.html#section_4_6)
open func detail(_ detail: Detail) -> Self {
self.detail = detail
return self
}
override func options() -> Options {
var options = super.options()
options.append("content_rowid", value: contentRowId)
if let columnSize = columnSize {
options.append("columnsize", value: Expression<Int>(value: columnSize))
}
options.append("detail", value: detail)
return options
}
override func formatColumnDefinitions() -> [Expressible] {
return columnDefinitions.map { definition in
if definition.options.contains(.unindexed) {
return " ".join([definition.0, Expression<Void>(literal: "UNINDEXED")])
} else {
return definition.0
}
}
}
}

View file

@ -0,0 +1,37 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
extension Module {
public static func RTree<T : Value, U : Value>(_ primaryKey: Expression<T>, _ pairs: (Expression<U>, Expression<U>)...) -> Module where T.Datatype == Int64, U.Datatype == Double {
var arguments: [Expressible] = [primaryKey]
for pair in pairs {
arguments.append(contentsOf: [pair.0, pair.1] as [Expressible])
}
return Module(name: "rtree", arguments: arguments)
}
}

View file

@ -0,0 +1,108 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
extension Data : Value {
public static var declaredDatatype: String {
return Blob.declaredDatatype
}
public static func fromDatatypeValue(_ dataValue: Blob) -> Data {
return Data(bytes: dataValue.bytes)
}
public var datatypeValue: Blob {
return withUnsafeBytes { (pointer: UnsafePointer<UInt8>) -> Blob in
return Blob(bytes: pointer, length: count)
}
}
}
extension Date : Value {
public static var declaredDatatype: String {
return String.declaredDatatype
}
public static func fromDatatypeValue(_ stringValue: String) -> Date {
return dateFormatter.date(from: stringValue)!
}
public var datatypeValue: String {
return dateFormatter.string(from: self)
}
}
/// A global date formatter used to serialize and deserialize `NSDate` objects.
/// If multiple date formats are used in an applications database(s), use a
/// custom `Value` type per additional format.
public var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter
}()
// FIXME: rdar://problem/18673897 // subscript<T>
extension QueryType {
public subscript(column: Expression<Data>) -> Expression<Data> {
return namespace(column)
}
public subscript(column: Expression<Data?>) -> Expression<Data?> {
return namespace(column)
}
public subscript(column: Expression<Date>) -> Expression<Date> {
return namespace(column)
}
public subscript(column: Expression<Date?>) -> Expression<Date?> {
return namespace(column)
}
}
extension Row {
public subscript(column: Expression<Data>) -> Data {
return get(column)
}
public subscript(column: Expression<Data?>) -> Data? {
return get(column)
}
public subscript(column: Expression<Date>) -> Date {
return get(column)
}
public subscript(column: Expression<Date?>) -> Date? {
return get(column)
}
}

View file

@ -0,0 +1,130 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#if SQLITE_SWIFT_STANDALONE
import sqlite3
#elseif SQLITE_SWIFT_SQLCIPHER
import SQLCipher
#else
import SQLite3
#endif
public typealias Star = (Expression<Binding>?, Expression<Binding>?) -> Expression<Void>
public func *(_: Expression<Binding>?, _: Expression<Binding>?) -> Expression<Void> {
return Expression(literal: "*")
}
public protocol _OptionalType {
associatedtype WrappedType
}
extension Optional : _OptionalType {
public typealias WrappedType = Wrapped
}
// let SQLITE_STATIC = unsafeBitCast(0, sqlite3_destructor_type.self)
let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
extension String {
func quote(_ mark: Character = "\"") -> String {
let escaped = characters.reduce("") { string, character in
string + (character == mark ? "\(mark)\(mark)" : "\(character)")
}
return "\(mark)\(escaped)\(mark)"
}
func join(_ expressions: [Expressible]) -> Expressible {
var (template, bindings) = ([String](), [Binding?]())
for expressible in expressions {
let expression = expressible.expression
template.append(expression.template)
bindings.append(contentsOf: expression.bindings)
}
return Expression<Void>(template.joined(separator: self), bindings)
}
func infix<T>(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression<T> {
let expression = Expression<T>(" \(self) ".join([lhs, rhs]).expression)
guard wrap else {
return expression
}
return "".wrap(expression)
}
func prefix(_ expressions: Expressible) -> Expressible {
return "\(self) ".wrap(expressions) as Expression<Void>
}
func prefix(_ expressions: [Expressible]) -> Expressible {
return "\(self) ".wrap(expressions) as Expression<Void>
}
func wrap<T>(_ expression: Expressible) -> Expression<T> {
return Expression("\(self)(\(expression.expression.template))", expression.expression.bindings)
}
func wrap<T>(_ expressions: [Expressible]) -> Expression<T> {
return wrap(", ".join(expressions))
}
}
func infix<T>(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true, function: String = #function) -> Expression<T> {
return function.infix(lhs, rhs, wrap: wrap)
}
func wrap<T>(_ expression: Expressible, function: String = #function) -> Expression<T> {
return function.wrap(expression)
}
func wrap<T>(_ expressions: [Expressible], function: String = #function) -> Expression<T> {
return function.wrap(", ".join(expressions))
}
func transcode(_ literal: Binding?) -> String {
guard let literal = literal else { return "NULL" }
switch literal {
case let blob as Blob:
return blob.description
case let string as String:
return string.quote("'")
case let binding:
return "\(binding)"
}
}
func value<A: Value>(_ v: Binding) -> A {
return A.fromDatatypeValue(v as! A.Datatype) as! A
}
func value<A: Value>(_ v: Binding?) -> A {
return value(v!)
}

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.11.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View file

@ -0,0 +1,6 @@
@import Foundation;
FOUNDATION_EXPORT double SQLiteVersionNumber;
FOUNDATION_EXPORT const unsigned char SQLiteVersionString[];
#import <SQLite/SQLite-Bridging.h>

View file

@ -0,0 +1,251 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
extension ExpressionType where UnderlyingType : Value {
/// Builds a copy of the expression prefixed with the `DISTINCT` keyword.
///
/// let name = Expression<String>("name")
/// name.distinct
/// // DISTINCT "name"
///
/// - Returns: A copy of the expression prefixed with the `DISTINCT`
/// keyword.
public var distinct: Expression<UnderlyingType> {
return Expression("DISTINCT \(template)", bindings)
}
/// Builds a copy of the expression wrapped with the `count` aggregate
/// function.
///
/// let name = Expression<String?>("name")
/// name.count
/// // count("name")
/// name.distinct.count
/// // count(DISTINCT "name")
///
/// - Returns: A copy of the expression wrapped with the `count` aggregate
/// function.
public var count: Expression<Int> {
return wrap(self)
}
}
extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.WrappedType : Value {
/// Builds a copy of the expression prefixed with the `DISTINCT` keyword.
///
/// let name = Expression<String?>("name")
/// name.distinct
/// // DISTINCT "name"
///
/// - Returns: A copy of the expression prefixed with the `DISTINCT`
/// keyword.
public var distinct: Expression<UnderlyingType> {
return Expression("DISTINCT \(template)", bindings)
}
/// Builds a copy of the expression wrapped with the `count` aggregate
/// function.
///
/// let name = Expression<String?>("name")
/// name.count
/// // count("name")
/// name.distinct.count
/// // count(DISTINCT "name")
///
/// - Returns: A copy of the expression wrapped with the `count` aggregate
/// function.
public var count: Expression<Int> {
return wrap(self)
}
}
extension ExpressionType where UnderlyingType : Value, UnderlyingType.Datatype : Comparable {
/// Builds a copy of the expression wrapped with the `max` aggregate
/// function.
///
/// let age = Expression<Int>("age")
/// age.max
/// // max("age")
///
/// - Returns: A copy of the expression wrapped with the `max` aggregate
/// function.
public var max: Expression<UnderlyingType?> {
return wrap(self)
}
/// Builds a copy of the expression wrapped with the `min` aggregate
/// function.
///
/// let age = Expression<Int>("age")
/// age.min
/// // min("age")
///
/// - Returns: A copy of the expression wrapped with the `min` aggregate
/// function.
public var min: Expression<UnderlyingType?> {
return wrap(self)
}
}
extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.WrappedType : Value, UnderlyingType.WrappedType.Datatype : Comparable {
/// Builds a copy of the expression wrapped with the `max` aggregate
/// function.
///
/// let age = Expression<Int?>("age")
/// age.max
/// // max("age")
///
/// - Returns: A copy of the expression wrapped with the `max` aggregate
/// function.
public var max: Expression<UnderlyingType> {
return wrap(self)
}
/// Builds a copy of the expression wrapped with the `min` aggregate
/// function.
///
/// let age = Expression<Int?>("age")
/// age.min
/// // min("age")
///
/// - Returns: A copy of the expression wrapped with the `min` aggregate
/// function.
public var min: Expression<UnderlyingType> {
return wrap(self)
}
}
extension ExpressionType where UnderlyingType : Value, UnderlyingType.Datatype : Number {
/// Builds a copy of the expression wrapped with the `avg` aggregate
/// function.
///
/// let salary = Expression<Double>("salary")
/// salary.average
/// // avg("salary")
///
/// - Returns: A copy of the expression wrapped with the `min` aggregate
/// function.
public var average: Expression<Double?> {
return "avg".wrap(self)
}
/// Builds a copy of the expression wrapped with the `sum` aggregate
/// function.
///
/// let salary = Expression<Double>("salary")
/// salary.sum
/// // sum("salary")
///
/// - Returns: A copy of the expression wrapped with the `min` aggregate
/// function.
public var sum: Expression<UnderlyingType?> {
return wrap(self)
}
/// Builds a copy of the expression wrapped with the `total` aggregate
/// function.
///
/// let salary = Expression<Double>("salary")
/// salary.total
/// // total("salary")
///
/// - Returns: A copy of the expression wrapped with the `min` aggregate
/// function.
public var total: Expression<Double> {
return wrap(self)
}
}
extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.WrappedType : Value, UnderlyingType.WrappedType.Datatype : Number {
/// Builds a copy of the expression wrapped with the `avg` aggregate
/// function.
///
/// let salary = Expression<Double?>("salary")
/// salary.average
/// // avg("salary")
///
/// - Returns: A copy of the expression wrapped with the `min` aggregate
/// function.
public var average: Expression<Double?> {
return "avg".wrap(self)
}
/// Builds a copy of the expression wrapped with the `sum` aggregate
/// function.
///
/// let salary = Expression<Double?>("salary")
/// salary.sum
/// // sum("salary")
///
/// - Returns: A copy of the expression wrapped with the `min` aggregate
/// function.
public var sum: Expression<UnderlyingType> {
return wrap(self)
}
/// Builds a copy of the expression wrapped with the `total` aggregate
/// function.
///
/// let salary = Expression<Double?>("salary")
/// salary.total
/// // total("salary")
///
/// - Returns: A copy of the expression wrapped with the `min` aggregate
/// function.
public var total: Expression<Double> {
return wrap(self)
}
}
extension ExpressionType where UnderlyingType == Int {
static func count(_ star: Star) -> Expression<UnderlyingType> {
return wrap(star(nil, nil))
}
}
/// Builds an expression representing `count(*)` (when called with the `*`
/// function literal).
///
/// count(*)
/// // count(*)
///
/// - Returns: An expression returning `count(*)` (when called with the `*`
/// function literal).
public func count(_ star: Star) -> Expression<Int> {
return Expression.count(star)
}

View file

@ -0,0 +1,69 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
/// A collating function used to compare to strings.
///
/// - SeeAlso: <https://www.sqlite.org/datatype3.html#collation>
public enum Collation {
/// Compares string by raw data.
case binary
/// Like binary, but folds uppercase ASCII letters into their lowercase
/// equivalents.
case nocase
/// Like binary, but strips trailing space.
case rtrim
/// A custom collating sequence identified by the given string, registered
/// using `Database.create(collation:)`
case custom(String)
}
extension Collation : Expressible {
public var expression: Expression<Void> {
return Expression(literal: description)
}
}
extension Collation : CustomStringConvertible {
public var description : String {
switch self {
case .binary:
return "BINARY"
case .nocase:
return "NOCASE"
case .rtrim:
return "RTRIM"
case .custom(let collation):
return collation.quote()
}
}
}

View file

@ -0,0 +1,683 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation.NSData
extension ExpressionType where UnderlyingType : Number {
/// Builds a copy of the expression wrapped with the `abs` function.
///
/// let x = Expression<Int>("x")
/// x.absoluteValue
/// // abs("x")
///
/// - Returns: A copy of the expression wrapped with the `abs` function.
public var absoluteValue : Expression<UnderlyingType> {
return "abs".wrap(self)
}
}
extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.WrappedType : Number {
/// Builds a copy of the expression wrapped with the `abs` function.
///
/// let x = Expression<Int?>("x")
/// x.absoluteValue
/// // abs("x")
///
/// - Returns: A copy of the expression wrapped with the `abs` function.
public var absoluteValue : Expression<UnderlyingType> {
return "abs".wrap(self)
}
}
extension ExpressionType where UnderlyingType == Double {
/// Builds a copy of the expression wrapped with the `round` function.
///
/// let salary = Expression<Double>("salary")
/// salary.round()
/// // round("salary")
/// salary.round(2)
/// // round("salary", 2)
///
/// - Returns: A copy of the expression wrapped with the `round` function.
public func round(_ precision: Int? = nil) -> Expression<UnderlyingType> {
guard let precision = precision else {
return wrap([self])
}
return wrap([self, Int(precision)])
}
}
extension ExpressionType where UnderlyingType == Double? {
/// Builds a copy of the expression wrapped with the `round` function.
///
/// let salary = Expression<Double>("salary")
/// salary.round()
/// // round("salary")
/// salary.round(2)
/// // round("salary", 2)
///
/// - Returns: A copy of the expression wrapped with the `round` function.
public func round(_ precision: Int? = nil) -> Expression<UnderlyingType> {
guard let precision = precision else {
return wrap(self)
}
return wrap([self, Int(precision)])
}
}
extension ExpressionType where UnderlyingType : Value, UnderlyingType.Datatype == Int64 {
/// Builds an expression representing the `random` function.
///
/// Expression<Int>.random()
/// // random()
///
/// - Returns: An expression calling the `random` function.
public static func random() -> Expression<UnderlyingType> {
return "random".wrap([])
}
}
extension ExpressionType where UnderlyingType == Data {
/// Builds an expression representing the `randomblob` function.
///
/// Expression<Int>.random(16)
/// // randomblob(16)
///
/// - Parameter length: Length in bytes.
///
/// - Returns: An expression calling the `randomblob` function.
public static func random(_ length: Int) -> Expression<UnderlyingType> {
return "randomblob".wrap([])
}
/// Builds an expression representing the `zeroblob` function.
///
/// Expression<Int>.allZeros(16)
/// // zeroblob(16)
///
/// - Parameter length: Length in bytes.
///
/// - Returns: An expression calling the `zeroblob` function.
public static func allZeros(_ length: Int) -> Expression<UnderlyingType> {
return "zeroblob".wrap([])
}
/// Builds a copy of the expression wrapped with the `length` function.
///
/// let data = Expression<NSData>("data")
/// data.length
/// // length("data")
///
/// - Returns: A copy of the expression wrapped with the `length` function.
public var length: Expression<Int> {
return wrap(self)
}
}
extension ExpressionType where UnderlyingType == Data? {
/// Builds a copy of the expression wrapped with the `length` function.
///
/// let data = Expression<NSData?>("data")
/// data.length
/// // length("data")
///
/// - Returns: A copy of the expression wrapped with the `length` function.
public var length: Expression<Int?> {
return wrap(self)
}
}
extension ExpressionType where UnderlyingType == String {
/// Builds a copy of the expression wrapped with the `length` function.
///
/// let name = Expression<String>("name")
/// name.length
/// // length("name")
///
/// - Returns: A copy of the expression wrapped with the `length` function.
public var length: Expression<Int> {
return wrap(self)
}
/// Builds a copy of the expression wrapped with the `lower` function.
///
/// let name = Expression<String>("name")
/// name.lowercaseString
/// // lower("name")
///
/// - Returns: A copy of the expression wrapped with the `lower` function.
public var lowercaseString: Expression<UnderlyingType> {
return "lower".wrap(self)
}
/// Builds a copy of the expression wrapped with the `upper` function.
///
/// let name = Expression<String>("name")
/// name.uppercaseString
/// // lower("name")
///
/// - Returns: A copy of the expression wrapped with the `upper` function.
public var uppercaseString: Expression<UnderlyingType> {
return "upper".wrap(self)
}
/// Builds a copy of the expression appended with a `LIKE` query against the
/// given pattern.
///
/// let email = Expression<String>("email")
/// email.like("%@example.com")
/// // "email" LIKE '%@example.com'
/// email.like("99\\%@%", escape: "\\")
/// // "email" LIKE '99\%@%' ESCAPE '\'
///
/// - Parameters:
///
/// - pattern: A pattern to match.
///
/// - escape: An (optional) character designated for escaping
/// pattern-matching characters (*i.e.*, the `%` and `_` characters).
///
/// - Returns: A copy of the expression appended with a `LIKE` query against
/// the given pattern.
public func like(_ pattern: String, escape character: Character? = nil) -> Expression<Bool> {
guard let character = character else {
return "LIKE".infix(self, pattern)
}
return Expression("(\(template) LIKE ? ESCAPE ?)", bindings + [pattern, String(character)])
}
/// Builds a copy of the expression appended with a `GLOB` query against the
/// given pattern.
///
/// let path = Expression<String>("path")
/// path.glob("*.png")
/// // "path" GLOB '*.png'
///
/// - Parameter pattern: A pattern to match.
///
/// - Returns: A copy of the expression appended with a `GLOB` query against
/// the given pattern.
public func glob(_ pattern: String) -> Expression<Bool> {
return "GLOB".infix(self, pattern)
}
/// Builds a copy of the expression appended with a `MATCH` query against
/// the given pattern.
///
/// let title = Expression<String>("title")
/// title.match("swift AND programming")
/// // "title" MATCH 'swift AND programming'
///
/// - Parameter pattern: A pattern to match.
///
/// - Returns: A copy of the expression appended with a `MATCH` query
/// against the given pattern.
public func match(_ pattern: String) -> Expression<Bool> {
return "MATCH".infix(self, pattern)
}
/// Builds a copy of the expression appended with a `REGEXP` query against
/// the given pattern.
///
/// - Parameter pattern: A pattern to match.
///
/// - Returns: A copy of the expression appended with a `REGEXP` query
/// against the given pattern.
public func regexp(_ pattern: String) -> Expression<Bool> {
return "REGEXP".infix(self, pattern)
}
/// Builds a copy of the expression appended with a `COLLATE` clause with
/// the given sequence.
///
/// let name = Expression<String>("name")
/// name.collate(.Nocase)
/// // "name" COLLATE NOCASE
///
/// - Parameter collation: A collating sequence.
///
/// - Returns: A copy of the expression appended with a `COLLATE` clause
/// with the given sequence.
public func collate(_ collation: Collation) -> Expression<UnderlyingType> {
return "COLLATE".infix(self, collation)
}
/// Builds a copy of the expression wrapped with the `ltrim` function.
///
/// let name = Expression<String>("name")
/// name.ltrim()
/// // ltrim("name")
/// name.ltrim([" ", "\t"])
/// // ltrim("name", ' \t')
///
/// - Parameter characters: A set of characters to trim.
///
/// - Returns: A copy of the expression wrapped with the `ltrim` function.
public func ltrim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
guard let characters = characters else {
return wrap(self)
}
return wrap([self, String(characters)])
}
/// Builds a copy of the expression wrapped with the `rtrim` function.
///
/// let name = Expression<String>("name")
/// name.rtrim()
/// // rtrim("name")
/// name.rtrim([" ", "\t"])
/// // rtrim("name", ' \t')
///
/// - Parameter characters: A set of characters to trim.
///
/// - Returns: A copy of the expression wrapped with the `rtrim` function.
public func rtrim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
guard let characters = characters else {
return wrap(self)
}
return wrap([self, String(characters)])
}
/// Builds a copy of the expression wrapped with the `trim` function.
///
/// let name = Expression<String>("name")
/// name.trim()
/// // trim("name")
/// name.trim([" ", "\t"])
/// // trim("name", ' \t')
///
/// - Parameter characters: A set of characters to trim.
///
/// - Returns: A copy of the expression wrapped with the `trim` function.
public func trim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
guard let characters = characters else {
return wrap([self])
}
return wrap([self, String(characters)])
}
/// Builds a copy of the expression wrapped with the `replace` function.
///
/// let email = Expression<String>("email")
/// email.replace("@mac.com", with: "@icloud.com")
/// // replace("email", '@mac.com', '@icloud.com')
///
/// - Parameters:
///
/// - pattern: A pattern to match.
///
/// - replacement: The replacement string.
///
/// - Returns: A copy of the expression wrapped with the `replace` function.
public func replace(_ pattern: String, with replacement: String) -> Expression<UnderlyingType> {
return "replace".wrap([self, pattern, replacement])
}
public func substring(_ location: Int, length: Int? = nil) -> Expression<UnderlyingType> {
guard let length = length else {
return "substr".wrap([self, location])
}
return "substr".wrap([self, location, length])
}
public subscript(range: Range<Int>) -> Expression<UnderlyingType> {
return substring(range.lowerBound, length: range.upperBound - range.lowerBound)
}
}
extension ExpressionType where UnderlyingType == String? {
/// Builds a copy of the expression wrapped with the `length` function.
///
/// let name = Expression<String?>("name")
/// name.length
/// // length("name")
///
/// - Returns: A copy of the expression wrapped with the `length` function.
public var length: Expression<Int?> {
return wrap(self)
}
/// Builds a copy of the expression wrapped with the `lower` function.
///
/// let name = Expression<String?>("name")
/// name.lowercaseString
/// // lower("name")
///
/// - Returns: A copy of the expression wrapped with the `lower` function.
public var lowercaseString: Expression<UnderlyingType> {
return "lower".wrap(self)
}
/// Builds a copy of the expression wrapped with the `upper` function.
///
/// let name = Expression<String?>("name")
/// name.uppercaseString
/// // lower("name")
///
/// - Returns: A copy of the expression wrapped with the `upper` function.
public var uppercaseString: Expression<UnderlyingType> {
return "upper".wrap(self)
}
/// Builds a copy of the expression appended with a `LIKE` query against the
/// given pattern.
///
/// let email = Expression<String?>("email")
/// email.like("%@example.com")
/// // "email" LIKE '%@example.com'
/// email.like("99\\%@%", escape: "\\")
/// // "email" LIKE '99\%@%' ESCAPE '\'
///
/// - Parameters:
///
/// - pattern: A pattern to match.
///
/// - escape: An (optional) character designated for escaping
/// pattern-matching characters (*i.e.*, the `%` and `_` characters).
///
/// - Returns: A copy of the expression appended with a `LIKE` query against
/// the given pattern.
public func like(_ pattern: String, escape character: Character? = nil) -> Expression<Bool?> {
guard let character = character else {
return "LIKE".infix(self, pattern)
}
return Expression("(\(template) LIKE ? ESCAPE ?)", bindings + [pattern, String(character)])
}
/// Builds a copy of the expression appended with a `GLOB` query against the
/// given pattern.
///
/// let path = Expression<String?>("path")
/// path.glob("*.png")
/// // "path" GLOB '*.png'
///
/// - Parameter pattern: A pattern to match.
///
/// - Returns: A copy of the expression appended with a `GLOB` query against
/// the given pattern.
public func glob(_ pattern: String) -> Expression<Bool?> {
return "GLOB".infix(self, pattern)
}
/// Builds a copy of the expression appended with a `MATCH` query against
/// the given pattern.
///
/// let title = Expression<String?>("title")
/// title.match("swift AND programming")
/// // "title" MATCH 'swift AND programming'
///
/// - Parameter pattern: A pattern to match.
///
/// - Returns: A copy of the expression appended with a `MATCH` query
/// against the given pattern.
public func match(_ pattern: String) -> Expression<Bool> {
return "MATCH".infix(self, pattern)
}
/// Builds a copy of the expression appended with a `REGEXP` query against
/// the given pattern.
///
/// - Parameter pattern: A pattern to match.
///
/// - Returns: A copy of the expression appended with a `REGEXP` query
/// against the given pattern.
public func regexp(_ pattern: String) -> Expression<Bool?> {
return "REGEXP".infix(self, pattern)
}
/// Builds a copy of the expression appended with a `COLLATE` clause with
/// the given sequence.
///
/// let name = Expression<String?>("name")
/// name.collate(.Nocase)
/// // "name" COLLATE NOCASE
///
/// - Parameter collation: A collating sequence.
///
/// - Returns: A copy of the expression appended with a `COLLATE` clause
/// with the given sequence.
public func collate(_ collation: Collation) -> Expression<UnderlyingType> {
return "COLLATE".infix(self, collation)
}
/// Builds a copy of the expression wrapped with the `ltrim` function.
///
/// let name = Expression<String?>("name")
/// name.ltrim()
/// // ltrim("name")
/// name.ltrim([" ", "\t"])
/// // ltrim("name", ' \t')
///
/// - Parameter characters: A set of characters to trim.
///
/// - Returns: A copy of the expression wrapped with the `ltrim` function.
public func ltrim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
guard let characters = characters else {
return wrap(self)
}
return wrap([self, String(characters)])
}
/// Builds a copy of the expression wrapped with the `rtrim` function.
///
/// let name = Expression<String?>("name")
/// name.rtrim()
/// // rtrim("name")
/// name.rtrim([" ", "\t"])
/// // rtrim("name", ' \t')
///
/// - Parameter characters: A set of characters to trim.
///
/// - Returns: A copy of the expression wrapped with the `rtrim` function.
public func rtrim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
guard let characters = characters else {
return wrap(self)
}
return wrap([self, String(characters)])
}
/// Builds a copy of the expression wrapped with the `trim` function.
///
/// let name = Expression<String?>("name")
/// name.trim()
/// // trim("name")
/// name.trim([" ", "\t"])
/// // trim("name", ' \t')
///
/// - Parameter characters: A set of characters to trim.
///
/// - Returns: A copy of the expression wrapped with the `trim` function.
public func trim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
guard let characters = characters else {
return wrap(self)
}
return wrap([self, String(characters)])
}
/// Builds a copy of the expression wrapped with the `replace` function.
///
/// let email = Expression<String?>("email")
/// email.replace("@mac.com", with: "@icloud.com")
/// // replace("email", '@mac.com', '@icloud.com')
///
/// - Parameters:
///
/// - pattern: A pattern to match.
///
/// - replacement: The replacement string.
///
/// - Returns: A copy of the expression wrapped with the `replace` function.
public func replace(_ pattern: String, with replacement: String) -> Expression<UnderlyingType> {
return "replace".wrap([self, pattern, replacement])
}
/// Builds a copy of the expression wrapped with the `substr` function.
///
/// let title = Expression<String?>("title")
/// title.substr(-100)
/// // substr("title", -100)
/// title.substr(0, length: 100)
/// // substr("title", 0, 100)
///
/// - Parameters:
///
/// - location: The substrings start index.
///
/// - length: An optional substring length.
///
/// - Returns: A copy of the expression wrapped with the `substr` function.
public func substring(_ location: Int, length: Int? = nil) -> Expression<UnderlyingType> {
guard let length = length else {
return "substr".wrap([self, location])
}
return "substr".wrap([self, location, length])
}
/// Builds a copy of the expression wrapped with the `substr` function.
///
/// let title = Expression<String?>("title")
/// title[0..<100]
/// // substr("title", 0, 100)
///
/// - Parameter range: The character index range of the substring.
///
/// - Returns: A copy of the expression wrapped with the `substr` function.
public subscript(range: Range<Int>) -> Expression<UnderlyingType> {
return substring(range.lowerBound, length: range.upperBound - range.lowerBound)
}
}
extension Collection where Iterator.Element : Value, IndexDistance == Int {
/// Builds a copy of the expression prepended with an `IN` check against the
/// collection.
///
/// let name = Expression<String>("name")
/// ["alice", "betty"].contains(name)
/// // "name" IN ('alice', 'betty')
///
/// - Parameter pattern: A pattern to match.
///
/// - Returns: A copy of the expression prepended with an `IN` check against
/// the collection.
public func contains(_ expression: Expression<Iterator.Element>) -> Expression<Bool> {
let templates = [String](repeating: "?", count: count).joined(separator: ", ")
return "IN".infix(expression, Expression<Void>("(\(templates))", map { $0.datatypeValue }))
}
/// Builds a copy of the expression prepended with an `IN` check against the
/// collection.
///
/// let name = Expression<String?>("name")
/// ["alice", "betty"].contains(name)
/// // "name" IN ('alice', 'betty')
///
/// - Parameter pattern: A pattern to match.
///
/// - Returns: A copy of the expression prepended with an `IN` check against
/// the collection.
public func contains(_ expression: Expression<Iterator.Element?>) -> Expression<Bool?> {
let templates = [String](repeating: "?", count: count).joined(separator: ", ")
return "IN".infix(expression, Expression<Void>("(\(templates))", map { $0.datatypeValue }))
}
}
/// Builds a copy of the given expressions wrapped with the `ifnull` function.
///
/// let name = Expression<String?>("name")
/// name ?? "An Anonymous Coward"
/// // ifnull("name", 'An Anonymous Coward')
///
/// - Parameters:
///
/// - optional: An optional expression.
///
/// - defaultValue: A fallback value for when the optional expression is
/// `nil`.
///
/// - Returns: A copy of the given expressions wrapped with the `ifnull`
/// function.
public func ??<V : Value>(optional: Expression<V?>, defaultValue: V) -> Expression<V> {
return "ifnull".wrap([optional, defaultValue])
}
/// Builds a copy of the given expressions wrapped with the `ifnull` function.
///
/// let nick = Expression<String?>("nick")
/// let name = Expression<String>("name")
/// nick ?? name
/// // ifnull("nick", "name")
///
/// - Parameters:
///
/// - optional: An optional expression.
///
/// - defaultValue: A fallback expression for when the optional expression is
/// `nil`.
///
/// - Returns: A copy of the given expressions wrapped with the `ifnull`
/// function.
public func ??<V : Value>(optional: Expression<V?>, defaultValue: Expression<V>) -> Expression<V> {
return "ifnull".wrap([optional, defaultValue])
}
/// Builds a copy of the given expressions wrapped with the `ifnull` function.
///
/// let nick = Expression<String?>("nick")
/// let name = Expression<String?>("name")
/// nick ?? name
/// // ifnull("nick", "name")
///
/// - Parameters:
///
/// - optional: An optional expression.
///
/// - defaultValue: A fallback expression for when the optional expression is
/// `nil`.
///
/// - Returns: A copy of the given expressions wrapped with the `ifnull`
/// function.
public func ??<V : Value>(optional: Expression<V?>, defaultValue: Expression<V?>) -> Expression<V> {
return "ifnull".wrap([optional, defaultValue])
}

View file

@ -0,0 +1,136 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
public extension Connection {
/// Creates or redefines a custom SQL function.
///
/// - Parameters:
///
/// - function: The name of the function to create or redefine.
///
/// - deterministic: Whether or not the function is deterministic (_i.e._
/// the function always returns the same result for a given input).
///
/// Default: `false`
///
/// - block: A block of code to run when the function is called.
/// The assigned types must be explicit.
///
/// - Returns: A closure returning an SQL expression to call the function.
public func createFunction<Z : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping () -> Z) throws -> (() -> Expression<Z>) {
let fn = try createFunction(function, 0, deterministic) { _ in block() }
return { fn([]) }
}
public func createFunction<Z : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping () -> Z?) throws -> (() -> Expression<Z?>) {
let fn = try createFunction(function, 0, deterministic) { _ in block() }
return { fn([]) }
}
// MARK: -
public func createFunction<Z : Value, A : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A) -> Z) throws -> ((Expression<A>) -> Expression<Z>) {
let fn = try createFunction(function, 1, deterministic) { args in block(value(args[0])) }
return { arg in fn([arg]) }
}
public func createFunction<Z : Value, A : Value>(function: String, deterministic: Bool = false, _ block: @escaping (A?) -> Z) throws -> ((Expression<A?>) -> Expression<Z>) {
let fn = try createFunction(function, 1, deterministic) { args in block(args[0].map(value)) }
return { arg in fn([arg]) }
}
public func createFunction<Z : Value, A : Value>(function: String, deterministic: Bool = false, _ block: @escaping (A) -> Z?) throws -> ((Expression<A>) -> Expression<Z?>) {
let fn = try createFunction(function, 1, deterministic) { args in block(value(args[0])) }
return { arg in fn([arg]) }
}
public func createFunction<Z : Value, A : Value>(function: String, deterministic: Bool = false, _ block: @escaping (A?) -> Z?) throws -> ((Expression<A?>) -> Expression<Z?>) {
let fn = try createFunction(function, 1, deterministic) { args in block(args[0].map(value)) }
return { arg in fn([arg]) }
}
// MARK: -
public func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A, B) -> Z) throws -> (Expression<A>, Expression<B>) -> Expression<Z> {
let fn = try createFunction(function, 1, deterministic) { args in block(value(args[0]), value(args[1])) }
return { a, b in fn([a, b]) }
}
public func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A?, B) -> Z) throws -> (Expression<A?>, Expression<B>) -> Expression<Z> {
let fn = try createFunction(function, 1, deterministic) { args in block(args[0].map(value), value(args[1])) }
return { a, b in fn([a, b]) }
}
public func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A, B?) -> Z) throws -> (Expression<A>, Expression<B?>) -> Expression<Z> {
let fn = try createFunction(function, 1, deterministic) { args in block(value(args[0]), args[1].map(value)) }
return { a, b in fn([a, b]) }
}
public func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A, B) -> Z?) throws -> (Expression<A>, Expression<B>) -> Expression<Z?> {
let fn = try createFunction(function, 1, deterministic) { args in block(value(args[0]), value(args[1])) }
return { a, b in fn([a, b]) }
}
public func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A?, B?) -> Z) throws -> (Expression<A?>, Expression<B?>) -> Expression<Z> {
let fn = try createFunction(function, 1, deterministic) { args in block(args[0].map(value), args[1].map(value)) }
return { a, b in fn([a, b]) }
}
public func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A?, B) -> Z?) throws -> (Expression<A?>, Expression<B>) -> Expression<Z?> {
let fn = try createFunction(function, 1, deterministic) { args in block(args[0].map(value), value(args[1])) }
return { a, b in fn([a, b]) }
}
public func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A, B?) -> Z?) throws -> (Expression<A>, Expression<B?>) -> Expression<Z?> {
let fn = try createFunction(function, 1, deterministic) { args in block(value(args[0]), args[1].map(value)) }
return { a, b in fn([a, b]) }
}
public func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A?, B?) -> Z?) throws -> (Expression<A?>, Expression<B?>) -> Expression<Z?> {
let fn = try createFunction(function, 1, deterministic) { args in block(args[0].map(value), args[1].map(value)) }
return { a, b in fn([a, b]) }
}
// MARK: -
fileprivate func createFunction<Z : Value>(_ function: String, _ argumentCount: UInt, _ deterministic: Bool, _ block: @escaping ([Binding?]) -> Z) throws -> (([Expressible]) -> Expression<Z>) {
createFunction(function, argumentCount: argumentCount, deterministic: deterministic) { arguments in
block(arguments).datatypeValue
}
return { arguments in
function.quote().wrap(", ".join(arguments))
}
}
fileprivate func createFunction<Z : Value>(_ function: String, _ argumentCount: UInt, _ deterministic: Bool, _ block: @escaping ([Binding?]) -> Z?) throws -> (([Expressible]) -> Expression<Z?>) {
createFunction(function, argumentCount: argumentCount, deterministic: deterministic) { arguments in
block(arguments)?.datatypeValue
}
return { arguments in
function.quote().wrap(", ".join(arguments))
}
}
}

View file

@ -0,0 +1,147 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
public protocol ExpressionType : Expressible { // extensions cannot have inheritance clauses
associatedtype UnderlyingType = Void
var template: String { get }
var bindings: [Binding?] { get }
init(_ template: String, _ bindings: [Binding?])
}
extension ExpressionType {
public init(literal: String) {
self.init(literal, [])
}
public init(_ identifier: String) {
self.init(literal: identifier.quote())
}
public init<U : ExpressionType>(_ expression: U) {
self.init(expression.template, expression.bindings)
}
}
/// An `Expression` represents a raw SQL fragment and any associated bindings.
public struct Expression<Datatype> : ExpressionType {
public typealias UnderlyingType = Datatype
public var template: String
public var bindings: [Binding?]
public init(_ template: String, _ bindings: [Binding?]) {
self.template = template
self.bindings = bindings
}
}
public protocol Expressible {
var expression: Expression<Void> { get }
}
extension Expressible {
// naïve compiler for statements that cant be bound, e.g., CREATE TABLE
// FIXME: use @testable and make internal
public func asSQL() -> String {
let expressed = expression
var idx = 0
return expressed.template.characters.reduce("") { template, character in
let transcoded: String
if character == "?" {
transcoded = transcode(expressed.bindings[idx])
idx += 1
} else {
transcoded = String(character)
}
return template + transcoded
}
}
}
extension ExpressionType {
public var expression: Expression<Void> {
return Expression(template, bindings)
}
public var asc: Expressible {
return " ".join([self, Expression<Void>(literal: "ASC")])
}
public var desc: Expressible {
return " ".join([self, Expression<Void>(literal: "DESC")])
}
}
extension ExpressionType where UnderlyingType : Value {
public init(value: UnderlyingType) {
self.init("?", [value.datatypeValue])
}
}
extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.WrappedType : Value {
public static var null: Self {
return self.init(value: nil)
}
public init(value: UnderlyingType.WrappedType?) {
self.init("?", [value?.datatypeValue])
}
}
extension Value {
public var expression: Expression<Void> {
return Expression(value: self).expression
}
}
public let rowid = Expression<Int64>("ROWID")
public func cast<T: Value, U: Value>(_ expression: Expression<T>) -> Expression<U> {
return Expression("CAST (\(expression.template) AS \(U.declaredDatatype))", expression.bindings)
}
public func cast<T: Value, U: Value>(_ expression: Expression<T?>) -> Expression<U?> {
return Expression("CAST (\(expression.template) AS \(U.declaredDatatype))", expression.bindings)
}

View file

@ -0,0 +1,541 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// TODO: use `@warn_unused_result` by the time operator functions support it
public func +(lhs: Expression<String>, rhs: Expression<String>) -> Expression<String> {
return "||".infix(lhs, rhs)
}
public func +(lhs: Expression<String>, rhs: Expression<String?>) -> Expression<String?> {
return "||".infix(lhs, rhs)
}
public func +(lhs: Expression<String?>, rhs: Expression<String>) -> Expression<String?> {
return "||".infix(lhs, rhs)
}
public func +(lhs: Expression<String?>, rhs: Expression<String?>) -> Expression<String?> {
return "||".infix(lhs, rhs)
}
public func +(lhs: Expression<String>, rhs: String) -> Expression<String> {
return "||".infix(lhs, rhs)
}
public func +(lhs: Expression<String?>, rhs: String) -> Expression<String?> {
return "||".infix(lhs, rhs)
}
public func +(lhs: String, rhs: Expression<String>) -> Expression<String> {
return "||".infix(lhs, rhs)
}
public func +(lhs: String, rhs: Expression<String?>) -> Expression<String?> {
return "||".infix(lhs, rhs)
}
// MARK: -
public func +<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func +<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func +<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func +<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func +<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func +<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func +<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func +<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func -<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func -<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func -<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func -<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func -<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func -<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func -<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func -<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func *<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func *<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func *<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func *<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func *<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func *<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func *<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func *<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func /<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func /<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func /<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func /<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func /<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func /<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func /<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return infix(lhs, rhs)
}
public func /<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return infix(lhs, rhs)
}
public prefix func -<V : Value>(rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return wrap(rhs)
}
public prefix func -<V : Value>(rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return wrap(rhs)
}
// MARK: -
public func %<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func %<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func %<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func %<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func %<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func %<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func %<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func %<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func <<<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func <<<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func <<<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func <<<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func <<<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func <<<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func <<<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func <<<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func >><V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func >><V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func >><V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func >><V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func >><V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func >><V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func >><V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func >><V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func &<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func &<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func &<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func &<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func &<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func &<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func &<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func &<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func |<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func |<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func |<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func |<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func |<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func |<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func |<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func |<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return infix(lhs, rhs)
}
public func ^<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return (~(lhs & rhs)) & (lhs | rhs)
}
public func ^<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return (~(lhs & rhs)) & (lhs | rhs)
}
public func ^<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
return (~(lhs & rhs)) & (lhs | rhs)
}
public func ^<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return (~(lhs & rhs)) & (lhs | rhs)
}
public func ^<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
return (~(lhs & rhs)) & (lhs | rhs)
}
public func ^<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
return (~(lhs & rhs)) & (lhs | rhs)
}
public func ^<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return (~(lhs & rhs)) & (lhs | rhs)
}
public func ^<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return (~(lhs & rhs)) & (lhs | rhs)
}
public prefix func ~<V : Value>(rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return wrap(rhs)
}
public prefix func ~<V : Value>(rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return wrap(rhs)
}
// MARK: -
public func ==<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
return "=".infix(lhs, rhs)
}
public func ==<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
return "=".infix(lhs, rhs)
}
public func ==<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Equatable {
return "=".infix(lhs, rhs)
}
public func ==<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
return "=".infix(lhs, rhs)
}
public func ==<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Equatable {
return "=".infix(lhs, rhs)
}
public func ==<V : Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype : Equatable {
guard let rhs = rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
return "=".infix(lhs, rhs)
}
public func ==<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
return "=".infix(lhs, rhs)
}
public func ==<V : Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
guard let lhs = lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
return "=".infix(lhs, rhs)
}
public func !=<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
return infix(lhs, rhs)
}
public func !=<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
return infix(lhs, rhs)
}
public func !=<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Equatable {
return infix(lhs, rhs)
}
public func !=<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
return infix(lhs, rhs)
}
public func !=<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Equatable {
return infix(lhs, rhs)
}
public func !=<V : Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype : Equatable {
guard let rhs = rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
return infix(lhs, rhs)
}
public func !=<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
return infix(lhs, rhs)
}
public func !=<V : Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
guard let lhs = lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
return infix(lhs, rhs)
}
public func ><V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func ><V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func ><V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func ><V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func ><V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func ><V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func ><V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func ><V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func >=<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func >=<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func >=<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func >=<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func >=<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func >=<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func >=<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func >=<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <=<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <=<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <=<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <=<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <=<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <=<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <=<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func <=<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return infix(lhs, rhs)
}
public func ~=<V : Value>(lhs: ClosedRange<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Binding & Comparable {
return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound as? Binding, lhs.upperBound as? Binding])
}
public func ~=<V : Value>(lhs: ClosedRange<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Binding & Comparable {
return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound as? Binding, lhs.upperBound as? Binding])
}
// MARK: -
public func &&(lhs: Expression<Bool>, rhs: Expression<Bool>) -> Expression<Bool> {
return "AND".infix(lhs, rhs)
}
public func &&(lhs: Expression<Bool>, rhs: Expression<Bool?>) -> Expression<Bool?> {
return "AND".infix(lhs, rhs)
}
public func &&(lhs: Expression<Bool?>, rhs: Expression<Bool>) -> Expression<Bool?> {
return "AND".infix(lhs, rhs)
}
public func &&(lhs: Expression<Bool?>, rhs: Expression<Bool?>) -> Expression<Bool?> {
return "AND".infix(lhs, rhs)
}
public func &&(lhs: Expression<Bool>, rhs: Bool) -> Expression<Bool> {
return "AND".infix(lhs, rhs)
}
public func &&(lhs: Expression<Bool?>, rhs: Bool) -> Expression<Bool?> {
return "AND".infix(lhs, rhs)
}
public func &&(lhs: Bool, rhs: Expression<Bool>) -> Expression<Bool> {
return "AND".infix(lhs, rhs)
}
public func &&(lhs: Bool, rhs: Expression<Bool?>) -> Expression<Bool?> {
return "AND".infix(lhs, rhs)
}
public func ||(lhs: Expression<Bool>, rhs: Expression<Bool>) -> Expression<Bool> {
return "OR".infix(lhs, rhs)
}
public func ||(lhs: Expression<Bool>, rhs: Expression<Bool?>) -> Expression<Bool?> {
return "OR".infix(lhs, rhs)
}
public func ||(lhs: Expression<Bool?>, rhs: Expression<Bool>) -> Expression<Bool?> {
return "OR".infix(lhs, rhs)
}
public func ||(lhs: Expression<Bool?>, rhs: Expression<Bool?>) -> Expression<Bool?> {
return "OR".infix(lhs, rhs)
}
public func ||(lhs: Expression<Bool>, rhs: Bool) -> Expression<Bool> {
return "OR".infix(lhs, rhs)
}
public func ||(lhs: Expression<Bool?>, rhs: Bool) -> Expression<Bool?> {
return "OR".infix(lhs, rhs)
}
public func ||(lhs: Bool, rhs: Expression<Bool>) -> Expression<Bool> {
return "OR".infix(lhs, rhs)
}
public func ||(lhs: Bool, rhs: Expression<Bool?>) -> Expression<Bool?> {
return "OR".infix(lhs, rhs)
}
public prefix func !(rhs: Expression<Bool>) -> Expression<Bool> {
return "NOT ".wrap(rhs)
}
public prefix func !(rhs: Expression<Bool?>) -> Expression<Bool?> {
return "NOT ".wrap(rhs)
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,519 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
extension SchemaType {
// MARK: - DROP TABLE / VIEW / VIRTUAL TABLE
public func drop(ifExists: Bool = false) -> String {
return drop("TABLE", tableName(), ifExists)
}
}
extension Table {
// MARK: - CREATE TABLE
public func create(temporary: Bool = false, ifNotExists: Bool = false, block: (TableBuilder) -> Void) -> String {
let builder = TableBuilder()
block(builder)
let clauses: [Expressible?] = [
create(Table.identifier, tableName(), temporary ? .Temporary : nil, ifNotExists),
"".wrap(builder.definitions) as Expression<Void>
]
return " ".join(clauses.flatMap { $0 }).asSQL()
}
public func create(_ query: QueryType, temporary: Bool = false, ifNotExists: Bool = false) -> String {
let clauses: [Expressible?] = [
create(Table.identifier, tableName(), temporary ? .Temporary : nil, ifNotExists),
Expression<Void>(literal: "AS"),
query
]
return " ".join(clauses.flatMap { $0 }).asSQL()
}
// MARK: - ALTER TABLE ADD COLUMN
public func addColumn<V : Value>(_ name: Expression<V>, check: Expression<Bool>? = nil, defaultValue: V) -> String {
return addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, nil))
}
public func addColumn<V : Value>(_ name: Expression<V>, check: Expression<Bool?>, defaultValue: V) -> String {
return addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, nil))
}
public func addColumn<V : Value>(_ name: Expression<V?>, check: Expression<Bool>? = nil, defaultValue: V? = nil) -> String {
return addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, nil))
}
public func addColumn<V : Value>(_ name: Expression<V?>, check: Expression<Bool?>, defaultValue: V? = nil) -> String {
return addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, nil))
}
public func addColumn<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil, references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
return addColumn(definition(name, V.declaredDatatype, nil, false, unique, check, nil, (table, other), nil))
}
public func addColumn<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>, references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
return addColumn(definition(name, V.declaredDatatype, nil, false, unique, check, nil, (table, other), nil))
}
public func addColumn<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
return addColumn(definition(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil))
}
public func addColumn<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
return addColumn(definition(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil))
}
public func addColumn<V : Value>(_ name: Expression<V>, check: Expression<Bool>? = nil, defaultValue: V, collate: Collation) -> String where V.Datatype == String {
return addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, collate))
}
public func addColumn<V : Value>(_ name: Expression<V>, check: Expression<Bool?>, defaultValue: V, collate: Collation) -> String where V.Datatype == String {
return addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, collate))
}
public func addColumn<V : Value>(_ name: Expression<V?>, check: Expression<Bool>? = nil, defaultValue: V? = nil, collate: Collation) -> String where V.Datatype == String {
return addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, collate))
}
public func addColumn<V : Value>(_ name: Expression<V?>, check: Expression<Bool?>, defaultValue: V? = nil, collate: Collation) -> String where V.Datatype == String {
return addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, collate))
}
fileprivate func addColumn(_ expression: Expressible) -> String {
return " ".join([
Expression<Void>(literal: "ALTER TABLE"),
tableName(),
Expression<Void>(literal: "ADD COLUMN"),
expression
]).asSQL()
}
// MARK: - ALTER TABLE RENAME TO
public func rename(_ to: Table) -> String {
return rename(to: to)
}
// MARK: - CREATE INDEX
public func createIndex(_ columns: Expressible...) -> String {
return createIndex(columns)
}
public func createIndex(_ columns: [Expressible], unique: Bool = false, ifNotExists: Bool = false) -> String {
let clauses: [Expressible?] = [
create("INDEX", indexName(columns), unique ? .Unique : nil, ifNotExists),
Expression<Void>(literal: "ON"),
tableName(qualified: false),
"".wrap(columns) as Expression<Void>
]
return " ".join(clauses.flatMap { $0 }).asSQL()
}
// MARK: - DROP INDEX
public func dropIndex(_ columns: Expressible...) -> String {
return dropIndex(columns)
}
public func dropIndex(_ columns: [Expressible], ifExists: Bool = false) -> String {
return drop("INDEX", indexName(columns), ifExists)
}
fileprivate func indexName(_ columns: [Expressible]) -> Expressible {
let string = (["index", clauses.from.name, "on"] + columns.map { $0.expression.template }).joined(separator: " ").lowercased()
let index = string.characters.reduce("") { underscored, character in
guard character != "\"" else {
return underscored
}
guard "a"..."z" ~= character || "0"..."9" ~= character else {
return underscored + "_"
}
return underscored + String(character)
}
return database(namespace: index)
}
}
extension View {
// MARK: - CREATE VIEW
public func create(_ query: QueryType, temporary: Bool = false, ifNotExists: Bool = false) -> String {
let clauses: [Expressible?] = [
create(View.identifier, tableName(), temporary ? .Temporary : nil, ifNotExists),
Expression<Void>(literal: "AS"),
query
]
return " ".join(clauses.flatMap { $0 }).asSQL()
}
// MARK: - DROP VIEW
public func drop(ifExists: Bool = false) -> String {
return drop("VIEW", tableName(), ifExists)
}
}
extension VirtualTable {
// MARK: - CREATE VIRTUAL TABLE
public func create(_ using: Module, ifNotExists: Bool = false) -> String {
let clauses: [Expressible?] = [
create(VirtualTable.identifier, tableName(), nil, ifNotExists),
Expression<Void>(literal: "USING"),
using
]
return " ".join(clauses.flatMap { $0 }).asSQL()
}
// MARK: - ALTER TABLE RENAME TO
public func rename(_ to: VirtualTable) -> String {
return rename(to: to)
}
}
public final class TableBuilder {
fileprivate var definitions = [Expressible]()
public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V>? = nil) {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, nil)
}
public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: V) {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, nil)
}
public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V>? = nil) {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, nil)
}
public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>, defaultValue: V) {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, nil)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V>? = nil) {
column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, nil)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V?>) {
column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, nil)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: V) {
column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, nil)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V>? = nil) {
column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, nil)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V?>) {
column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, nil)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: V) {
column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, nil)
}
public func column<V : Value>(_ name: Expression<V>, primaryKey: Bool, check: Expression<Bool>? = nil, defaultValue: Expression<V>? = nil) {
column(name, V.declaredDatatype, primaryKey ? .default : nil, false, false, check, defaultValue, nil, nil)
}
public func column<V : Value>(_ name: Expression<V>, primaryKey: Bool, check: Expression<Bool?>, defaultValue: Expression<V>? = nil) {
column(name, V.declaredDatatype, primaryKey ? .default : nil, false, false, check, defaultValue, nil, nil)
}
public func column<V : Value>(_ name: Expression<V>, primaryKey: PrimaryKey, check: Expression<Bool>? = nil) where V.Datatype == Int64 {
column(name, V.declaredDatatype, primaryKey, false, false, check, nil, nil, nil)
}
public func column<V : Value>(_ name: Expression<V>, primaryKey: PrimaryKey, check: Expression<Bool?>) where V.Datatype == Int64 {
column(name, V.declaredDatatype, primaryKey, false, false, check, nil, nil, nil)
}
public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil, references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
column(name, V.declaredDatatype, nil, false, unique, check, nil, (table, other), nil)
}
public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>, references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
column(name, V.declaredDatatype, nil, false, unique, check, nil, (table, other), nil)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
column(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
column(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil)
}
public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
}
public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: V, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
}
public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
}
public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>, defaultValue: V, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V?>, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: V, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V?>, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
}
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: V, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
}
fileprivate func column(_ name: Expressible, _ datatype: String, _ primaryKey: PrimaryKey?, _ null: Bool, _ unique: Bool, _ check: Expressible?, _ defaultValue: Expressible?, _ references: (QueryType, Expressible)?, _ collate: Collation?) {
definitions.append(definition(name, datatype, primaryKey, null, unique, check, defaultValue, references, collate))
}
// MARK: -
public func primaryKey<T : Value>(_ column: Expression<T>) {
primaryKey([column])
}
public func primaryKey<T : Value, U : Value>(_ compositeA: Expression<T>, _ b: Expression<U>) {
primaryKey([compositeA, b])
}
public func primaryKey<T : Value, U : Value, V : Value>(_ compositeA: Expression<T>, _ b: Expression<U>, _ c: Expression<V>) {
primaryKey([compositeA, b, c])
}
fileprivate func primaryKey(_ composite: [Expressible]) {
definitions.append("PRIMARY KEY".prefix(composite))
}
public func unique(_ columns: Expressible...) {
unique(columns)
}
public func unique(_ columns: [Expressible]) {
definitions.append("UNIQUE".prefix(columns))
}
public func check(_ condition: Expression<Bool>) {
check(Expression<Bool?>(condition))
}
public func check(_ condition: Expression<Bool?>) {
definitions.append("CHECK".prefix(condition))
}
public enum Dependency: String {
case noAction = "NO ACTION"
case restrict = "RESTRICT"
case setNull = "SET NULL"
case setDefault = "SET DEFAULT"
case cascade = "CASCADE"
}
public func foreignKey<T : Value>(_ column: Expression<T>, references table: QueryType, _ other: Expression<T>, update: Dependency? = nil, delete: Dependency? = nil) {
foreignKey(column, (table, other), update, delete)
}
public func foreignKey<T : Value>(_ column: Expression<T?>, references table: QueryType, _ other: Expression<T>, update: Dependency? = nil, delete: Dependency? = nil) {
foreignKey(column, (table, other), update, delete)
}
public func foreignKey<T : Value, U : Value>(_ composite: (Expression<T>, Expression<U>), references table: QueryType, _ other: (Expression<T>, Expression<U>), update: Dependency? = nil, delete: Dependency? = nil) {
let composite = ", ".join([composite.0, composite.1])
let references = (table, ", ".join([other.0, other.1]))
foreignKey(composite, references, update, delete)
}
public func foreignKey<T : Value, U : Value, V : Value>(_ composite: (Expression<T>, Expression<U>, Expression<V>), references table: QueryType, _ other: (Expression<T>, Expression<U>, Expression<V>), update: Dependency? = nil, delete: Dependency? = nil) {
let composite = ", ".join([composite.0, composite.1, composite.2])
let references = (table, ", ".join([other.0, other.1, other.2]))
foreignKey(composite, references, update, delete)
}
fileprivate func foreignKey(_ column: Expressible, _ references: (QueryType, Expressible), _ update: Dependency?, _ delete: Dependency?) {
let clauses: [Expressible?] = [
"FOREIGN KEY".prefix(column),
reference(references),
update.map { Expression<Void>(literal: "ON UPDATE \($0.rawValue)") },
delete.map { Expression<Void>(literal: "ON DELETE \($0.rawValue)") }
]
definitions.append(" ".join(clauses.flatMap { $0 }))
}
}
public enum PrimaryKey {
case `default`
case autoincrement
}
public struct Module {
fileprivate let name: String
fileprivate let arguments: [Expressible]
public init(_ name: String, _ arguments: [Expressible]) {
self.init(name: name.quote(), arguments: arguments)
}
init(name: String, arguments: [Expressible]) {
self.name = name
self.arguments = arguments
}
}
extension Module : Expressible {
public var expression: Expression<Void> {
return name.wrap(arguments)
}
}
// MARK: - Private
private extension QueryType {
func create(_ identifier: String, _ name: Expressible, _ modifier: Modifier?, _ ifNotExists: Bool) -> Expressible {
let clauses: [Expressible?] = [
Expression<Void>(literal: "CREATE"),
modifier.map { Expression<Void>(literal: $0.rawValue) },
Expression<Void>(literal: identifier),
ifNotExists ? Expression<Void>(literal: "IF NOT EXISTS") : nil,
name
]
return " ".join(clauses.flatMap { $0 })
}
func rename(to: Self) -> String {
return " ".join([
Expression<Void>(literal: "ALTER TABLE"),
tableName(),
Expression<Void>(literal: "RENAME TO"),
Expression<Void>(to.clauses.from.name)
]).asSQL()
}
func drop(_ identifier: String, _ name: Expressible, _ ifExists: Bool) -> String {
let clauses: [Expressible?] = [
Expression<Void>(literal: "DROP \(identifier)"),
ifExists ? Expression<Void>(literal: "IF EXISTS") : nil,
name
]
return " ".join(clauses.flatMap { $0 }).asSQL()
}
}
private func definition(_ column: Expressible, _ datatype: String, _ primaryKey: PrimaryKey?, _ null: Bool, _ unique: Bool, _ check: Expressible?, _ defaultValue: Expressible?, _ references: (QueryType, Expressible)?, _ collate: Collation?) -> Expressible {
let clauses: [Expressible?] = [
column,
Expression<Void>(literal: datatype),
primaryKey.map { Expression<Void>(literal: $0 == .autoincrement ? "PRIMARY KEY AUTOINCREMENT" : "PRIMARY KEY") },
null ? nil : Expression<Void>(literal: "NOT NULL"),
unique ? Expression<Void>(literal: "UNIQUE") : nil,
check.map { " ".join([Expression<Void>(literal: "CHECK"), $0]) },
defaultValue.map { "DEFAULT".prefix($0) },
references.map(reference),
collate.map { " ".join([Expression<Void>(literal: "COLLATE"), $0]) }
]
return " ".join(clauses.flatMap { $0 })
}
private func reference(_ primary: (QueryType, Expressible)) -> Expressible {
return " ".join([
Expression<Void>(literal: "REFERENCES"),
primary.0.tableName(qualified: false),
"".wrap(primary.1) as Expression<Void>
])
}
private enum Modifier : String {
case Unique = "UNIQUE"
case Temporary = "TEMPORARY"
}

View file

@ -0,0 +1,277 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
precedencegroup ColumnAssignment {
associativity: left
assignment: true
lowerThan: AssignmentPrecedence
}
infix operator <- : ColumnAssignment
public struct Setter {
let column: Expressible
let value: Expressible
fileprivate init<V : Value>(column: Expression<V>, value: Expression<V>) {
self.column = column
self.value = value
}
fileprivate init<V : Value>(column: Expression<V>, value: V) {
self.column = column
self.value = value
}
fileprivate init<V : Value>(column: Expression<V?>, value: Expression<V>) {
self.column = column
self.value = value
}
fileprivate init<V : Value>(column: Expression<V?>, value: Expression<V?>) {
self.column = column
self.value = value
}
fileprivate init<V : Value>(column: Expression<V?>, value: V?) {
self.column = column
self.value = Expression<V?>(value: value)
}
}
extension Setter : Expressible {
public var expression: Expression<Void> {
return "=".infix(column, value, wrap: false)
}
}
public func <-<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter {
return Setter(column: column, value: value)
}
public func <-<V : Value>(column: Expression<V>, value: V) -> Setter {
return Setter(column: column, value: value)
}
public func <-<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter {
return Setter(column: column, value: value)
}
public func <-<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter {
return Setter(column: column, value: value)
}
public func <-<V : Value>(column: Expression<V?>, value: V?) -> Setter {
return Setter(column: column, value: value)
}
public func +=(column: Expression<String>, value: Expression<String>) -> Setter {
return column <- column + value
}
public func +=(column: Expression<String>, value: String) -> Setter {
return column <- column + value
}
public func +=(column: Expression<String?>, value: Expression<String>) -> Setter {
return column <- column + value
}
public func +=(column: Expression<String?>, value: Expression<String?>) -> Setter {
return column <- column + value
}
public func +=(column: Expression<String?>, value: String) -> Setter {
return column <- column + value
}
public func +=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype : Number {
return column <- column + value
}
public func +=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype : Number {
return column <- column + value
}
public func +=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype : Number {
return column <- column + value
}
public func +=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype : Number {
return column <- column + value
}
public func +=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype : Number {
return column <- column + value
}
public func -=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype : Number {
return column <- column - value
}
public func -=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype : Number {
return column <- column - value
}
public func -=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype : Number {
return column <- column - value
}
public func -=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype : Number {
return column <- column - value
}
public func -=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype : Number {
return column <- column - value
}
public func *=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype : Number {
return column <- column * value
}
public func *=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype : Number {
return column <- column * value
}
public func *=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype : Number {
return column <- column * value
}
public func *=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype : Number {
return column <- column * value
}
public func *=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype : Number {
return column <- column * value
}
public func /=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype : Number {
return column <- column / value
}
public func /=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype : Number {
return column <- column / value
}
public func /=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype : Number {
return column <- column / value
}
public func /=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype : Number {
return column <- column / value
}
public func /=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype : Number {
return column <- column / value
}
public func %=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
return column <- column % value
}
public func %=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
return column <- column % value
}
public func %=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
return column <- column % value
}
public func %=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
return column <- column % value
}
public func %=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
return column <- column % value
}
public func <<=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
return column <- column << value
}
public func <<=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
return column <- column << value
}
public func <<=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
return column <- column << value
}
public func <<=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
return column <- column << value
}
public func <<=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
return column <- column << value
}
public func >>=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
return column <- column >> value
}
public func >>=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
return column <- column >> value
}
public func >>=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
return column <- column >> value
}
public func >>=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
return column <- column >> value
}
public func >>=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
return column <- column >> value
}
public func &=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
return column <- column & value
}
public func &=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
return column <- column & value
}
public func &=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
return column <- column & value
}
public func &=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
return column <- column & value
}
public func &=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
return column <- column & value
}
public func |=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
return column <- column | value
}
public func |=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
return column <- column | value
}
public func |=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
return column <- column | value
}
public func |=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
return column <- column | value
}
public func |=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
return column <- column | value
}
public func ^=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
return column <- column ^ value
}
public func ^=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
return column <- column ^ value
}
public func ^=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
return column <- column ^ value
}
public func ^=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
return column <- column ^ value
}
public func ^=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
return column <- column ^ value
}
public postfix func ++<V : Value>(column: Expression<V>) -> Setter where V.Datatype == Int64 {
return Expression<Int>(column) += 1
}
public postfix func ++<V : Value>(column: Expression<V?>) -> Setter where V.Datatype == Int64 {
return Expression<Int>(column) += 1
}
public postfix func --<V : Value>(column: Expression<V>) -> Setter where V.Datatype == Int64 {
return Expression<Int>(column) -= 1
}
public postfix func --<V : Value>(column: Expression<V?>) -> Setter where V.Datatype == Int64 {
return Expression<Int>(column) -= 1
}