Push from command line

This commit is contained in:
Deniz Duezgoeren
2019-08-12 11:20:21 +02:00
parent 3a919dcb23
commit f1345eac14
512 changed files with 103288 additions and 1930 deletions
+60
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
}
+750
View File
@@ -0,0 +1,750 @@
//
// 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
import Dispatch
#if SQLITE_SWIFT_STANDALONE
import sqlite3
#elseif SQLITE_SWIFT_SQLCIPHER
import SQLCipher
#elseif os(Linux)
import CSQLite
#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: () 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: () 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: () throws -> Void, _ commit: String, or rollback: String) throws {
return try sync {
try self.run(begin)
do {
try block()
try self.run(commit)
} catch {
try self.run(rollback)
throw error
}
}
}
/// 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 || os(Linux)
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.count), SQLITE_TRANSIENT)
} else if result == nil {
sqlite3_result_null(context)
} else {
fatalError("unsupported result type: \(String(describing: result))")
}
}
var flags = SQLITE_UTF8
#if !os(Linux)
if deterministic {
flags |= SQLITE_DETERMINISTIC
}
#endif
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: () throws -> T) rethrows -> T {
if DispatchQueue.getSpecific(key: Connection.queueKey) == queueContext {
return try block()
} else {
return try queue.sync(execute: block)
}
}
@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]
/// Represents a SQLite specific [error code](https://sqlite.org/rescode.html)
///
/// - message: English-language text that describes the error
///
/// - code: SQLite [error code](https://sqlite.org/rescode.html#primary_result_code_list)
///
/// - statement: the statement which produced the error
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 && !os(Linux)
@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
+21
View File
@@ -0,0 +1,21 @@
import Foundation
public enum QueryError: Error, CustomStringConvertible {
case noSuchTable(name: String)
case noSuchColumn(name: String, columns: [String])
case ambiguousColumn(name: String, similar: [String])
case unexpectedNullValue(name: String)
public var description: String {
switch self {
case .noSuchTable(let name):
return "No such table: \(name)"
case .noSuchColumn(let name, let columns):
return "No such column `\(name)` in columns \(columns)"
case .ambiguousColumn(let name, let similar):
return "Ambiguous column `\(name)` (please disambiguate: \(similar))"
case .unexpectedNullValue(let name):
return "Unexpected null value for column `\(name)`"
}
}
}
+317
View File
@@ -0,0 +1,317 @@
//
// 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
#elseif os(Linux)
import CSQLite
#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
}
}
public protocol FailableIterator : IteratorProtocol {
func failableNext() throws -> Self.Element?
}
extension FailableIterator {
public func next() -> Element? {
return try! failableNext()
}
}
extension Array {
public init<I: FailableIterator>(_ failableIterator: I) throws where I.Element == Element {
self.init()
while let row = try failableIterator.failableNext() {
append(row)
}
}
}
extension Statement : FailableIterator {
public typealias Element = [Binding?]
public func failableNext() throws -> [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]
}
}
}
}
+132
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)
}
}
+352
View File
@@ -0,0 +1,352 @@
//
// 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: UnsafePointer<Int8>, offset: UnsafeMutablePointer<Int32>, length: UnsafeMutablePointer<Int32>) in
let string = String(cString: input)
guard let (token, range) = next(string) else { return nil }
let view:String.UTF8View = string.utf8
if let from = range.lowerBound.samePosition(in: view),
let to = range.upperBound.samePosition(in: view) {
offset.pointee += Int32(string[string.startIndex..<range.lowerBound].utf8.count)
length.pointee = Int32(view.distance(from: from, to: to))
return token
} else {
return nil
}
})
}
}
/// 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
}
}
+97
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
}
}
}
}
+37
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)
}
}
+70
View File
@@ -0,0 +1,70 @@
//
// 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(dataValue.bytes)
}
public var datatypeValue: Blob {
return withUnsafeBytes { (pointer: UnsafeRawBufferPointer) -> Blob in
return Blob(bytes: pointer.baseAddress!, 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
}()
+120
View File
@@ -0,0 +1,120 @@
//
// 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
#elseif os(Linux)
import CSQLite
#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 = 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 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!)
}
+6
View File
@@ -0,0 +1,6 @@
@import Foundation;
FOUNDATION_EXPORT double SQLiteVersionNumber;
FOUNDATION_EXPORT const unsigned char SQLiteVersionString[];
#import "SQLiteObjc.h"
@@ -0,0 +1,264 @@
//
// 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.
//
private enum Function: String {
case count
case max
case min
case avg
case sum
case total
func wrap<T>(_ expression: Expressible) -> Expression<T> {
return self.rawValue.wrap(expression)
}
}
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 Function.count.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 Function.count.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 Function.max.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 Function.min.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 Function.max.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 Function.min.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 Function.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 Function.sum.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 Function.total.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 Function.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 Function.sum.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 Function.total.wrap(self)
}
}
extension ExpressionType where UnderlyingType == Int {
static func count(_ star: Star) -> Expression<UnderlyingType> {
return Function.count.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)
}
+340
View File
@@ -0,0 +1,340 @@
//
// 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 QueryType {
/// Creates an `INSERT` statement by encoding the given object
/// This method converts any custom nested types to JSON data and does not handle any sort
/// of object relationships. If you want to support relationships between objects you will
/// have to provide your own Encodable implementations that encode the correct ids.
///
/// - Parameters:
///
/// - encodable: An encodable object to insert
///
/// - userInfo: User info to be passed to encoder
///
/// - otherSetters: Any other setters to include in the insert
///
/// - Returns: An `INSERT` statement fort the encodable object
public func insert(_ encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Insert {
let encoder = SQLiteEncoder(userInfo: userInfo)
try encodable.encode(to: encoder)
return self.insert(encoder.setters + otherSetters)
}
/// Creates an `UPDATE` statement by encoding the given object
/// This method converts any custom nested types to JSON data and does not handle any sort
/// of object relationships. If you want to support relationships between objects you will
/// have to provide your own Encodable implementations that encode the correct ids.
///
/// - Parameters:
///
/// - encodable: An encodable object to insert
///
/// - userInfo: User info to be passed to encoder
///
/// - otherSetters: Any other setters to include in the insert
///
/// - Returns: An `UPDATE` statement fort the encodable object
public func update(_ encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Update {
let encoder = SQLiteEncoder(userInfo: userInfo)
try encodable.encode(to: encoder)
return self.update(encoder.setters + otherSetters)
}
}
extension Row {
/// Decode an object from this row
/// This method expects any custom nested types to be in the form of JSON data and does not handle
/// any sort of object relationships. If you want to support relationships between objects you will
/// have to provide your own Decodable implementations that decodes the correct columns.
///
/// - Parameter: userInfo
///
/// - Returns: a decoded object from this row
public func decode<V: Decodable>(userInfo: [CodingUserInfoKey: Any] = [:]) throws -> V {
return try V(from: self.decoder(userInfo: userInfo))
}
public func decoder(userInfo: [CodingUserInfoKey: Any] = [:]) -> Decoder {
return SQLiteDecoder(row: self, userInfo: userInfo)
}
}
/// Generates a list of settings for an Encodable object
fileprivate class SQLiteEncoder: Encoder {
class SQLiteKeyedEncodingContainer<MyKey: CodingKey>: KeyedEncodingContainerProtocol {
typealias Key = MyKey
let encoder: SQLiteEncoder
let codingPath: [CodingKey] = []
init(encoder: SQLiteEncoder) {
self.encoder = encoder
}
func superEncoder() -> Swift.Encoder {
fatalError("SQLiteEncoding does not support super encoders")
}
func superEncoder(forKey key: Key) -> Swift.Encoder {
fatalError("SQLiteEncoding does not support super encoders")
}
func encodeNil(forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
self.encoder.setters.append(Expression<String?>(key.stringValue) <- nil)
}
func encode(_ value: Int, forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
self.encoder.setters.append(Expression(key.stringValue) <- value)
}
func encode(_ value: Bool, forKey key: Key) throws {
self.encoder.setters.append(Expression(key.stringValue) <- value)
}
func encode(_ value: Float, forKey key: Key) throws {
self.encoder.setters.append(Expression(key.stringValue) <- Double(value))
}
func encode(_ value: Double, forKey key: Key) throws {
self.encoder.setters.append(Expression(key.stringValue) <- value)
}
func encode(_ value: String, forKey key: Key) throws {
self.encoder.setters.append(Expression(key.stringValue) <- value)
}
func encode<T>(_ value: T, forKey key: Key) throws where T : Swift.Encodable {
if let data = value as? Data {
self.encoder.setters.append(Expression(key.stringValue) <- data)
}
else {
let encoded = try JSONEncoder().encode(value)
let string = String(data: encoded, encoding: .utf8)
self.encoder.setters.append(Expression(key.stringValue) <- string)
}
}
func encode(_ value: Int8, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int8 is not supported"))
}
func encode(_ value: Int16, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int16 is not supported"))
}
func encode(_ value: Int32, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int32 is not supported"))
}
func encode(_ value: Int64, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int64 is not supported"))
}
func encode(_ value: UInt, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt is not supported"))
}
func encode(_ value: UInt8, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt8 is not supported"))
}
func encode(_ value: UInt16, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt16 is not supported"))
}
func encode(_ value: UInt32, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt32 is not supported"))
}
func encode(_ value: UInt64, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt64 is not supported"))
}
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
fatalError("encoding a nested container is not supported")
}
func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
fatalError("encoding nested values is not supported")
}
}
fileprivate var setters: [Setter] = []
let codingPath: [CodingKey] = []
let userInfo: [CodingUserInfoKey: Any]
init(userInfo: [CodingUserInfoKey: Any]) {
self.userInfo = userInfo
}
func singleValueContainer() -> SingleValueEncodingContainer {
fatalError("not supported")
}
func unkeyedContainer() -> UnkeyedEncodingContainer {
fatalError("not supported")
}
func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
return KeyedEncodingContainer(SQLiteKeyedEncodingContainer(encoder: self))
}
}
fileprivate class SQLiteDecoder : Decoder {
class SQLiteKeyedDecodingContainer<MyKey: CodingKey> : KeyedDecodingContainerProtocol {
typealias Key = MyKey
let codingPath: [CodingKey] = []
let row: Row
init(row: Row) {
self.row = row
}
var allKeys: [Key] {
return self.row.columnNames.keys.compactMap({Key(stringValue: $0)})
}
func contains(_ key: Key) -> Bool {
return self.row.hasValue(for: key.stringValue)
}
func decodeNil(forKey key: Key) throws -> Bool {
return !self.contains(key)
}
func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
return try self.row.get(Expression(key.stringValue))
}
func decode(_ type: Int.Type, forKey key: Key) throws -> Int {
return try self.row.get(Expression(key.stringValue))
}
func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an Int8 is not supported"))
}
func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an Int16 is not supported"))
}
func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an Int32 is not supported"))
}
func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt64 is not supported"))
}
func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt is not supported"))
}
func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt8 is not supported"))
}
func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt16 is not supported"))
}
func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt32 is not supported"))
}
func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt64 is not supported"))
}
func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
return Float(try self.row.get(Expression<Double>(key.stringValue)))
}
func decode(_ type: Double.Type, forKey key: Key) throws -> Double {
return try self.row.get(Expression(key.stringValue))
}
func decode(_ type: String.Type, forKey key: Key) throws -> String {
return try self.row.get(Expression(key.stringValue))
}
func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T: Swift.Decodable {
if type == Data.self {
let data = try self.row.get(Expression<Data>(key.stringValue))
return data as! T
}
guard let JSONString = try self.row.get(Expression<String?>(key.stringValue)) else {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "an unsupported type was found"))
}
guard let data = JSONString.data(using: .utf8) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "invalid utf8 data found"))
}
return try JSONDecoder().decode(type, from: data)
}
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding nested containers is not supported"))
}
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding unkeyed containers is not supported"))
}
func superDecoder() throws -> Swift.Decoder {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding super encoders containers is not supported"))
}
func superDecoder(forKey key: Key) throws -> Swift.Decoder {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding super decoders is not supported"))
}
}
let row: Row
let codingPath: [CodingKey] = []
let userInfo: [CodingUserInfoKey: Any]
init(row: Row, userInfo: [CodingUserInfoKey: Any]) {
self.row = row
self.userInfo = userInfo
}
func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey {
return KeyedDecodingContainer(SQLiteKeyedDecodingContainer(row: self.row))
}
func unkeyedContainer() throws -> UnkeyedDecodingContainer {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an unkeyed container is not supported"))
}
func singleValueContainer() throws -> SingleValueDecodingContainer {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding a single value container is not supported"))
}
}
+69
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()
}
}
}
+796
View File
@@ -0,0 +1,796 @@
//
// 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
private enum Function: String {
case abs
case round
case random
case randomblob
case zeroblob
case length
case lower
case upper
case ltrim
case rtrim
case trim
case replace
case substr
case like = "LIKE"
case `in` = "IN"
case glob = "GLOB"
case match = "MATCH"
case regexp = "REGEXP"
case collate = "COLLATE"
case ifnull
func infix<T>(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression<T> {
return self.rawValue.infix(lhs, rhs, wrap: wrap)
}
func wrap<T>(_ expression: Expressible) -> Expression<T> {
return self.rawValue.wrap(expression)
}
func wrap<T>(_ expressions: [Expressible]) -> Expression<T> {
return self.rawValue.wrap(", ".join(expressions))
}
}
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 Function.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 Function.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 Function.round.wrap([self])
}
return Function.round.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 Function.round.wrap(self)
}
return Function.round.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 Function.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 Function.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 Function.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 Function.length.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 Function.length.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 Function.length.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 Function.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 Function.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 `LIKE` query against the
/// given pattern.
///
/// let email = Expression<String>("email")
/// let pattern = Expression<String>("pattern")
/// email.like(pattern)
/// // "email" LIKE "pattern"
///
/// - 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: Expression<String>, escape character: Character? = nil) -> Expression<Bool> {
guard let character = character else {
return Function.like.infix(self, pattern)
}
let like: Expression<Bool> = Function.like.infix(self, pattern, wrap: false)
return Expression("(\(like.template) ESCAPE ?)", like.bindings + [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 Function.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 Function.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 Function.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 Function.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 Function.ltrim.wrap(self)
}
return Function.ltrim.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 Function.rtrim.wrap(self)
}
return Function.rtrim.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 Function.trim.wrap([self])
}
return Function.trim.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 Function.replace.wrap([self, pattern, replacement])
}
public func substring(_ location: Int, length: Int? = nil) -> Expression<UnderlyingType> {
guard let length = length else {
return Function.substr.wrap([self, location])
}
return Function.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 Function.length.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 Function.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 Function.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 Function.like.infix(self, pattern)
}
return Expression("(\(template) LIKE ? ESCAPE ?)", bindings + [pattern, String(character)])
}
/// Builds a copy of the expression appended with a `LIKE` query against the
/// given pattern.
///
/// let email = Expression<String>("email")
/// let pattern = Expression<String>("pattern")
/// email.like(pattern)
/// // "email" LIKE "pattern"
///
/// - 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: Expression<String>, escape character: Character? = nil) -> Expression<Bool?> {
guard let character = character else {
return Function.like.infix(self, pattern)
}
let like: Expression<Bool> = Function.like.infix(self, pattern, wrap: false)
return Expression("(\(like.template) ESCAPE ?)", like.bindings + [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 Function.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 Function.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 Function.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 Function.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 Function.ltrim.wrap(self)
}
return Function.ltrim.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 Function.rtrim.wrap(self)
}
return Function.rtrim.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 Function.trim.wrap(self)
}
return Function.trim.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 Function.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 Function.substr.wrap([self, location])
}
return Function.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 {
/// 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 Function.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 Function.in.infix(expression, Expression<Void>("(\(templates))", map { $0.datatypeValue }))
}
}
extension String {
/// Builds a copy of the expression appended with a `LIKE` query against the
/// given pattern.
///
/// let email = "some@thing.com"
/// let pattern = Expression<String>("pattern")
/// email.like(pattern)
/// // 'some@thing.com' LIKE "pattern"
///
/// - 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: Expression<String>, escape character: Character? = nil) -> Expression<Bool> {
guard let character = character else {
return Function.like.infix(self, pattern)
}
let like: Expression<Bool> = Function.like.infix(self, pattern, wrap: false)
return Expression("(\(like.template) ESCAPE ?)", like.bindings + [String(character)])
}
}
/// 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 Function.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 Function.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 Function.ifnull.wrap([optional, defaultValue])
}
@@ -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.
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([]) }
}
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: -
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]) }
}
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]) }
}
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]) }
}
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: -
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, 2, deterministic) { args in block(value(args[0]), value(args[1])) }
return { a, b in fn([a, b]) }
}
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, 2, deterministic) { args in block(args[0].map(value), value(args[1])) }
return { a, b in fn([a, b]) }
}
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, 2, deterministic) { args in block(value(args[0]), args[1].map(value)) }
return { a, b in fn([a, b]) }
}
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, 2, deterministic) { args in block(value(args[0]), value(args[1])) }
return { a, b in fn([a, b]) }
}
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, 2, deterministic) { args in block(args[0].map(value), args[1].map(value)) }
return { a, b in fn([a, b]) }
}
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, 2, deterministic) { args in block(args[0].map(value), value(args[1])) }
return { a, b in fn([a, b]) }
}
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, 2, deterministic) { args in block(value(args[0]), args[1].map(value)) }
return { a, b in fn([a, b]) }
}
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, 2, 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))
}
}
}
@@ -0,0 +1,106 @@
//
// 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
/// All five date and time functions take a time string as an argument.
/// The time string is followed by zero or more modifiers.
/// The strftime() function also takes a format string as its first argument.
///
/// https://www.sqlite.org/lang_datefunc.html
public class DateFunctions {
/// The date() function returns the date in this format: YYYY-MM-DD.
public static func date(_ timestring: String, _ modifiers: String...) -> Expression<Date?> {
return timefunction("date", timestring: timestring, modifiers: modifiers)
}
/// The time() function returns the time as HH:MM:SS.
public static func time(_ timestring: String, _ modifiers: String...) -> Expression<Date?> {
return timefunction("time", timestring: timestring, modifiers: modifiers)
}
/// The datetime() function returns "YYYY-MM-DD HH:MM:SS".
public static func datetime(_ timestring: String, _ modifiers: String...) -> Expression<Date?> {
return timefunction("datetime", timestring: timestring, modifiers: modifiers)
}
/// The julianday() function returns the Julian day -
/// the number of days since noon in Greenwich on November 24, 4714 B.C.
public static func julianday(_ timestring: String, _ modifiers: String...) -> Expression<Date?> {
return timefunction("julianday", timestring: timestring, modifiers: modifiers)
}
/// The strftime() routine returns the date formatted according to the format string specified as the first argument.
public static func strftime(_ format: String, _ timestring: String, _ modifiers: String...) -> Expression<Date?> {
if !modifiers.isEmpty {
let templates = [String](repeating: "?", count: modifiers.count).joined(separator: ", ")
return Expression("strftime(?, ?, \(templates))", [format, timestring] + modifiers)
}
return Expression("strftime(?, ?)", [format, timestring])
}
private static func timefunction(_ name: String, timestring: String, modifiers: [String]) -> Expression<Date?> {
if !modifiers.isEmpty {
let templates = [String](repeating: "?", count: modifiers.count).joined(separator: ", ")
return Expression("\(name)(?, \(templates))", [timestring] + modifiers)
}
return Expression("\(name)(?)", [timestring])
}
}
extension Date {
public var date: Expression<Date?> {
return DateFunctions.date(dateFormatter.string(from: self))
}
public var time: Expression<Date?> {
return DateFunctions.time(dateFormatter.string(from: self))
}
public var datetime: Expression<Date?> {
return DateFunctions.datetime(dateFormatter.string(from: self))
}
public var julianday: Expression<Date?> {
return DateFunctions.julianday(dateFormatter.string(from: self))
}
}
extension Expression where UnderlyingType == Date {
public var date: Expression<Date> {
return Expression<Date>("date(\(template))", bindings)
}
public var time: Expression<Date> {
return Expression<Date>("time(\(template))", bindings)
}
public var datetime: Expression<Date> {
return Expression<Date>("datetime(\(template))", bindings)
}
public var julianday: Expression<Date> {
return Expression<Date>("julianday(\(template))", bindings)
}
}
+147
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: make internal (0.13.0)
public func asSQL() -> String {
let expressed = expression
var idx = 0
return expressed.template.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)
}
+605
View File
@@ -0,0 +1,605 @@
//
// 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
private enum Operator: String {
case plus = "+"
case minus = "-"
case or = "OR"
case and = "AND"
case not = "NOT "
case mul = "*"
case div = "/"
case mod = "%"
case bitwiseLeft = "<<"
case bitwiseRight = ">>"
case bitwiseAnd = "&"
case bitwiseOr = "|"
case bitwiseXor = "~"
case eq = "="
case neq = "!="
case gt = ">"
case lt = "<"
case gte = ">="
case lte = "<="
case concatenate = "||"
func infix<T>(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression<T> {
return self.rawValue.infix(lhs, rhs, wrap: wrap)
}
func wrap<T>(_ expression: Expressible) -> Expression<T> {
return self.rawValue.wrap(expression)
}
}
public func +(lhs: Expression<String>, rhs: Expression<String>) -> Expression<String> {
return Operator.concatenate.infix(lhs, rhs)
}
public func +(lhs: Expression<String>, rhs: Expression<String?>) -> Expression<String?> {
return Operator.concatenate.infix(lhs, rhs)
}
public func +(lhs: Expression<String?>, rhs: Expression<String>) -> Expression<String?> {
return Operator.concatenate.infix(lhs, rhs)
}
public func +(lhs: Expression<String?>, rhs: Expression<String?>) -> Expression<String?> {
return Operator.concatenate.infix(lhs, rhs)
}
public func +(lhs: Expression<String>, rhs: String) -> Expression<String> {
return Operator.concatenate.infix(lhs, rhs)
}
public func +(lhs: Expression<String?>, rhs: String) -> Expression<String?> {
return Operator.concatenate.infix(lhs, rhs)
}
public func +(lhs: String, rhs: Expression<String>) -> Expression<String> {
return Operator.concatenate.infix(lhs, rhs)
}
public func +(lhs: String, rhs: Expression<String?>) -> Expression<String?> {
return Operator.concatenate.infix(lhs, rhs)
}
// MARK: -
public func +<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return Operator.plus.infix(lhs, rhs)
}
public func +<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.plus.infix(lhs, rhs)
}
public func +<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype : Number {
return Operator.plus.infix(lhs, rhs)
}
public func +<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.plus.infix(lhs, rhs)
}
public func +<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype : Number {
return Operator.plus.infix(lhs, rhs)
}
public func +<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype : Number {
return Operator.plus.infix(lhs, rhs)
}
public func +<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return Operator.plus.infix(lhs, rhs)
}
public func +<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.plus.infix(lhs, rhs)
}
public func -<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return Operator.minus.infix(lhs, rhs)
}
public func -<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.minus.infix(lhs, rhs)
}
public func -<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype : Number {
return Operator.minus.infix(lhs, rhs)
}
public func -<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.minus.infix(lhs, rhs)
}
public func -<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype : Number {
return Operator.minus.infix(lhs, rhs)
}
public func -<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype : Number {
return Operator.minus.infix(lhs, rhs)
}
public func -<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return Operator.minus.infix(lhs, rhs)
}
public func -<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.minus.infix(lhs, rhs)
}
public func *<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return Operator.mul.infix(lhs, rhs)
}
public func *<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.mul.infix(lhs, rhs)
}
public func *<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype : Number {
return Operator.mul.infix(lhs, rhs)
}
public func *<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.mul.infix(lhs, rhs)
}
public func *<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype : Number {
return Operator.mul.infix(lhs, rhs)
}
public func *<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype : Number {
return Operator.mul.infix(lhs, rhs)
}
public func *<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return Operator.mul.infix(lhs, rhs)
}
public func *<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.mul.infix(lhs, rhs)
}
public func /<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return Operator.div.infix(lhs, rhs)
}
public func /<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.div.infix(lhs, rhs)
}
public func /<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype : Number {
return Operator.div.infix(lhs, rhs)
}
public func /<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.div.infix(lhs, rhs)
}
public func /<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype : Number {
return Operator.div.infix(lhs, rhs)
}
public func /<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype : Number {
return Operator.div.infix(lhs, rhs)
}
public func /<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return Operator.div.infix(lhs, rhs)
}
public func /<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.div.infix(lhs, rhs)
}
public prefix func -<V : Value>(rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
return Operator.minus.wrap(rhs)
}
public prefix func -<V : Value>(rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
return Operator.minus.wrap(rhs)
}
// MARK: -
public func %<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return Operator.mod.infix(lhs, rhs)
}
public func %<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.mod.infix(lhs, rhs)
}
public func %<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.mod.infix(lhs, rhs)
}
public func %<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.mod.infix(lhs, rhs)
}
public func %<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
return Operator.mod.infix(lhs, rhs)
}
public func %<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
return Operator.mod.infix(lhs, rhs)
}
public func %<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return Operator.mod.infix(lhs, rhs)
}
public func %<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.mod.infix(lhs, rhs)
}
public func <<<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return Operator.bitwiseLeft.infix(lhs, rhs)
}
public func <<<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseLeft.infix(lhs, rhs)
}
public func <<<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseLeft.infix(lhs, rhs)
}
public func <<<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseLeft.infix(lhs, rhs)
}
public func <<<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
return Operator.bitwiseLeft.infix(lhs, rhs)
}
public func <<<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseLeft.infix(lhs, rhs)
}
public func <<<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return Operator.bitwiseLeft.infix(lhs, rhs)
}
public func <<<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseLeft.infix(lhs, rhs)
}
public func >><V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return Operator.bitwiseRight.infix(lhs, rhs)
}
public func >><V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseRight.infix(lhs, rhs)
}
public func >><V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseRight.infix(lhs, rhs)
}
public func >><V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseRight.infix(lhs, rhs)
}
public func >><V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
return Operator.bitwiseRight.infix(lhs, rhs)
}
public func >><V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseRight.infix(lhs, rhs)
}
public func >><V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return Operator.bitwiseRight.infix(lhs, rhs)
}
public func >><V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseRight.infix(lhs, rhs)
}
public func &<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return Operator.bitwiseAnd.infix(lhs, rhs)
}
public func &<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseAnd.infix(lhs, rhs)
}
public func &<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseAnd.infix(lhs, rhs)
}
public func &<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseAnd.infix(lhs, rhs)
}
public func &<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
return Operator.bitwiseAnd.infix(lhs, rhs)
}
public func &<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseAnd.infix(lhs, rhs)
}
public func &<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return Operator.bitwiseAnd.infix(lhs, rhs)
}
public func &<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseAnd.infix(lhs, rhs)
}
public func |<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return Operator.bitwiseOr.infix(lhs, rhs)
}
public func |<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseOr.infix(lhs, rhs)
}
public func |<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseOr.infix(lhs, rhs)
}
public func |<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseOr.infix(lhs, rhs)
}
public func |<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
return Operator.bitwiseOr.infix(lhs, rhs)
}
public func |<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseOr.infix(lhs, rhs)
}
public func |<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
return Operator.bitwiseOr.infix(lhs, rhs)
}
public func |<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseOr.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 Operator.bitwiseXor.wrap(rhs)
}
public prefix func ~<V : Value>(rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
return Operator.bitwiseXor.wrap(rhs)
}
// MARK: -
public func ==<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
return Operator.eq.infix(lhs, rhs)
}
public func ==<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
return Operator.eq.infix(lhs, rhs)
}
public func ==<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Equatable {
return Operator.eq.infix(lhs, rhs)
}
public func ==<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
return Operator.eq.infix(lhs, rhs)
}
public func ==<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Equatable {
return Operator.eq.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 Operator.eq.infix(lhs, rhs)
}
public func ==<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
return Operator.eq.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 Operator.eq.infix(lhs, rhs)
}
public func !=<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
return Operator.neq.infix(lhs, rhs)
}
public func !=<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
return Operator.neq.infix(lhs, rhs)
}
public func !=<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Equatable {
return Operator.neq.infix(lhs, rhs)
}
public func !=<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
return Operator.neq.infix(lhs, rhs)
}
public func !=<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Equatable {
return Operator.neq.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 Operator.neq.infix(lhs, rhs)
}
public func !=<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
return Operator.neq.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 Operator.neq.infix(lhs, rhs)
}
public func ><V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return Operator.gt.infix(lhs, rhs)
}
public func ><V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.gt.infix(lhs, rhs)
}
public func ><V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.gt.infix(lhs, rhs)
}
public func ><V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.gt.infix(lhs, rhs)
}
public func ><V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Comparable {
return Operator.gt.infix(lhs, rhs)
}
public func ><V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.gt.infix(lhs, rhs)
}
public func ><V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return Operator.gt.infix(lhs, rhs)
}
public func ><V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.gt.infix(lhs, rhs)
}
public func >=<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return Operator.gte.infix(lhs, rhs)
}
public func >=<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.gte.infix(lhs, rhs)
}
public func >=<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.gte.infix(lhs, rhs)
}
public func >=<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.gte.infix(lhs, rhs)
}
public func >=<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Comparable {
return Operator.gte.infix(lhs, rhs)
}
public func >=<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.gte.infix(lhs, rhs)
}
public func >=<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return Operator.gte.infix(lhs, rhs)
}
public func >=<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.gte.infix(lhs, rhs)
}
public func <<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return Operator.lt.infix(lhs, rhs)
}
public func <<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.lt.infix(lhs, rhs)
}
public func <<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.lt.infix(lhs, rhs)
}
public func <<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.lt.infix(lhs, rhs)
}
public func <<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Comparable {
return Operator.lt.infix(lhs, rhs)
}
public func <<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.lt.infix(lhs, rhs)
}
public func <<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return Operator.lt.infix(lhs, rhs)
}
public func <<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.lt.infix(lhs, rhs)
}
public func <=<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return Operator.lte.infix(lhs, rhs)
}
public func <=<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.lte.infix(lhs, rhs)
}
public func <=<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.lte.infix(lhs, rhs)
}
public func <=<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.lte.infix(lhs, rhs)
}
public func <=<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Comparable {
return Operator.lte.infix(lhs, rhs)
}
public func <=<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.lte.infix(lhs, rhs)
}
public func <=<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
return Operator.lte.infix(lhs, rhs)
}
public func <=<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
return Operator.lte.infix(lhs, rhs)
}
public func ~=<V : Value>(lhs: ClosedRange<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: ClosedRange<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: Range<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", rhs.bindings + [lhs.lowerBound.datatypeValue] + rhs.bindings + [lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: Range<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", rhs.bindings + [lhs.lowerBound.datatypeValue] + rhs.bindings + [lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: PartialRangeThrough<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) <= ?", rhs.bindings + [lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: PartialRangeThrough<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) <= ?", rhs.bindings + [lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: PartialRangeUpTo<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) < ?", rhs.bindings + [lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: PartialRangeUpTo<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) < ?", rhs.bindings + [lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: PartialRangeFrom<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) >= ?", rhs.bindings + [lhs.lowerBound.datatypeValue])
}
public func ~=<V : Value>(lhs: PartialRangeFrom<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) >= ?", rhs.bindings + [lhs.lowerBound.datatypeValue])
}
// MARK: -
public func &&(lhs: Expression<Bool>, rhs: Expression<Bool>) -> Expression<Bool> {
return Operator.and.infix(lhs, rhs)
}
public func &&(lhs: Expression<Bool>, rhs: Expression<Bool?>) -> Expression<Bool?> {
return Operator.and.infix(lhs, rhs)
}
public func &&(lhs: Expression<Bool?>, rhs: Expression<Bool>) -> Expression<Bool?> {
return Operator.and.infix(lhs, rhs)
}
public func &&(lhs: Expression<Bool?>, rhs: Expression<Bool?>) -> Expression<Bool?> {
return Operator.and.infix(lhs, rhs)
}
public func &&(lhs: Expression<Bool>, rhs: Bool) -> Expression<Bool> {
return Operator.and.infix(lhs, rhs)
}
public func &&(lhs: Expression<Bool?>, rhs: Bool) -> Expression<Bool?> {
return Operator.and.infix(lhs, rhs)
}
public func &&(lhs: Bool, rhs: Expression<Bool>) -> Expression<Bool> {
return Operator.and.infix(lhs, rhs)
}
public func &&(lhs: Bool, rhs: Expression<Bool?>) -> Expression<Bool?> {
return Operator.and.infix(lhs, rhs)
}
public func ||(lhs: Expression<Bool>, rhs: Expression<Bool>) -> Expression<Bool> {
return Operator.or.infix(lhs, rhs)
}
public func ||(lhs: Expression<Bool>, rhs: Expression<Bool?>) -> Expression<Bool?> {
return Operator.or.infix(lhs, rhs)
}
public func ||(lhs: Expression<Bool?>, rhs: Expression<Bool>) -> Expression<Bool?> {
return Operator.or.infix(lhs, rhs)
}
public func ||(lhs: Expression<Bool?>, rhs: Expression<Bool?>) -> Expression<Bool?> {
return Operator.or.infix(lhs, rhs)
}
public func ||(lhs: Expression<Bool>, rhs: Bool) -> Expression<Bool> {
return Operator.or.infix(lhs, rhs)
}
public func ||(lhs: Expression<Bool?>, rhs: Bool) -> Expression<Bool?> {
return Operator.or.infix(lhs, rhs)
}
public func ||(lhs: Bool, rhs: Expression<Bool>) -> Expression<Bool> {
return Operator.or.infix(lhs, rhs)
}
public func ||(lhs: Bool, rhs: Expression<Bool?>) -> Expression<Bool?> {
return Operator.or.infix(lhs, rhs)
}
public prefix func !(rhs: Expression<Bool>) -> Expression<Bool> {
return Operator.not.wrap(rhs)
}
public prefix func !(rhs: Expression<Bool?>) -> Expression<Bool?> {
return Operator.not.wrap(rhs)
}
File diff suppressed because it is too large Load Diff
+517
View File
@@ -0,0 +1,517 @@
//
// 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, withoutRowid: 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>,
withoutRowid ? Expression<Void>(literal: "WITHOUT ROWID") : nil
]
return " ".join(clauses.compactMap { $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.compactMap { $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..., 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.compactMap { $0 }).asSQL()
}
// MARK: - DROP INDEX
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.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.compactMap { $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.compactMap { $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, true, 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, true, 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, true, 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, true, 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, true, 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, true, 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])
}
public func primaryKey<T : Value, U : Value, V : Value, W : Value>(_ compositeA: Expression<T>, _ b: Expression<U>, _ c: Expression<V>, _ d: Expression<W>) {
primaryKey([compositeA, b, c, d])
}
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.compactMap { $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.compactMap { $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.compactMap { $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.compactMap { $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"
}
+277
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
}
+138
View File
@@ -0,0 +1,138 @@
//
// 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 "SQLiteObjc.h"
#import "fts3_tokenizer.h"
#pragma mark - FTS
typedef struct __SQLiteTokenizer {
sqlite3_tokenizer base;
__unsafe_unretained _SQLiteTokenizerNextCallback callback;
} __SQLiteTokenizer;
typedef struct __SQLiteTokenizerCursor {
void * base;
const char * input;
int inputOffset;
int inputLength;
int idx;
} __SQLiteTokenizerCursor;
static NSMutableDictionary * __SQLiteTokenizerMap;
static int __SQLiteTokenizerCreate(int argc, const char * const * argv, sqlite3_tokenizer ** ppTokenizer) {
__SQLiteTokenizer * tokenizer = (__SQLiteTokenizer *)sqlite3_malloc(sizeof(__SQLiteTokenizer));
if (!tokenizer) {
return SQLITE_NOMEM;
}
memset(tokenizer, 0, sizeof(* tokenizer));
NSString * key = [NSString stringWithUTF8String:argv[0]];
tokenizer->callback = [__SQLiteTokenizerMap objectForKey:key];
if (!tokenizer->callback) {
return SQLITE_ERROR;
}
*ppTokenizer = &tokenizer->base;
return SQLITE_OK;
}
static int __SQLiteTokenizerDestroy(sqlite3_tokenizer * pTokenizer) {
sqlite3_free(pTokenizer);
return SQLITE_OK;
}
static int __SQLiteTokenizerOpen(sqlite3_tokenizer * pTokenizer, const char * pInput, int nBytes, sqlite3_tokenizer_cursor ** ppCursor) {
__SQLiteTokenizerCursor * cursor = (__SQLiteTokenizerCursor *)sqlite3_malloc(sizeof(__SQLiteTokenizerCursor));
if (!cursor) {
return SQLITE_NOMEM;
}
cursor->input = pInput;
cursor->inputOffset = 0;
cursor->inputLength = 0;
cursor->idx = 0;
*ppCursor = (sqlite3_tokenizer_cursor *)cursor;
return SQLITE_OK;
}
static int __SQLiteTokenizerClose(sqlite3_tokenizer_cursor * pCursor) {
sqlite3_free(pCursor);
return SQLITE_OK;
}
static int __SQLiteTokenizerNext(sqlite3_tokenizer_cursor * pCursor, const char ** ppToken, int * pnBytes, int * piStartOffset, int * piEndOffset, int * piPosition) {
__SQLiteTokenizerCursor * cursor = (__SQLiteTokenizerCursor *)pCursor;
__SQLiteTokenizer * tokenizer = (__SQLiteTokenizer *)cursor->base;
cursor->inputOffset += cursor->inputLength;
const char * input = cursor->input + cursor->inputOffset;
const char * token = [tokenizer->callback(input, &cursor->inputOffset, &cursor->inputLength) cStringUsingEncoding:NSUTF8StringEncoding];
if (!token) {
return SQLITE_DONE;
}
*ppToken = token;
*pnBytes = (int)strlen(token);
*piStartOffset = cursor->inputOffset;
*piEndOffset = cursor->inputOffset + cursor->inputLength;
*piPosition = cursor->idx++;
return SQLITE_OK;
}
static const sqlite3_tokenizer_module __SQLiteTokenizerModule = {
0,
__SQLiteTokenizerCreate,
__SQLiteTokenizerDestroy,
__SQLiteTokenizerOpen,
__SQLiteTokenizerClose,
__SQLiteTokenizerNext
};
int _SQLiteRegisterTokenizer(sqlite3 *db, const char * moduleName, const char * submoduleName, _SQLiteTokenizerNextCallback callback) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__SQLiteTokenizerMap = [NSMutableDictionary new];
});
sqlite3_stmt * stmt;
int status = sqlite3_prepare_v2(db, "SELECT fts3_tokenizer(?, ?)", -1, &stmt, 0);
if (status != SQLITE_OK ){
return status;
}
const sqlite3_tokenizer_module * pModule = &__SQLiteTokenizerModule;
sqlite3_bind_text(stmt, 1, moduleName, -1, SQLITE_STATIC);
sqlite3_bind_blob(stmt, 2, &pModule, sizeof(pModule), SQLITE_STATIC);
sqlite3_step(stmt);
status = sqlite3_finalize(stmt);
if (status != SQLITE_OK ){
return status;
}
[__SQLiteTokenizerMap setObject:[callback copy] forKey:[NSString stringWithUTF8String:submoduleName]];
return SQLITE_OK;
}
+161
View File
@@ -0,0 +1,161 @@
/*
** 2006 July 10
**
** The author disclaims copyright to this source code.
**
*************************************************************************
** Defines the interface to tokenizers used by fulltext-search. There
** are three basic components:
**
** sqlite3_tokenizer_module is a singleton defining the tokenizer
** interface functions. This is essentially the class structure for
** tokenizers.
**
** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
** including customization information defined at creation time.
**
** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
** tokens from a particular input.
*/
#ifndef _FTS3_TOKENIZER_H_
#define _FTS3_TOKENIZER_H_
/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
** If tokenizers are to be allowed to call sqlite3_*() functions, then
** we will need a way to register the API consistently.
*/
#import "sqlite3.h"
/*
** Structures used by the tokenizer interface. When a new tokenizer
** implementation is registered, the caller provides a pointer to
** an sqlite3_tokenizer_module containing pointers to the callback
** functions that make up an implementation.
**
** When an fts3 table is created, it passes any arguments passed to
** the tokenizer clause of the CREATE VIRTUAL TABLE statement to the
** sqlite3_tokenizer_module.xCreate() function of the requested tokenizer
** implementation. The xCreate() function in turn returns an
** sqlite3_tokenizer structure representing the specific tokenizer to
** be used for the fts3 table (customized by the tokenizer clause arguments).
**
** To tokenize an input buffer, the sqlite3_tokenizer_module.xOpen()
** method is called. It returns an sqlite3_tokenizer_cursor object
** that may be used to tokenize a specific input buffer based on
** the tokenization rules supplied by a specific sqlite3_tokenizer
** object.
*/
typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
typedef struct sqlite3_tokenizer sqlite3_tokenizer;
typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
struct sqlite3_tokenizer_module {
/*
** Structure version. Should always be set to 0 or 1.
*/
int iVersion;
/*
** Create a new tokenizer. The values in the argv[] array are the
** arguments passed to the "tokenizer" clause of the CREATE VIRTUAL
** TABLE statement that created the fts3 table. For example, if
** the following SQL is executed:
**
** CREATE .. USING fts3( ... , tokenizer <tokenizer-name> arg1 arg2)
**
** then argc is set to 2, and the argv[] array contains pointers
** to the strings "arg1" and "arg2".
**
** This method should return either SQLITE_OK (0), or an SQLite error
** code. If SQLITE_OK is returned, then *ppTokenizer should be set
** to point at the newly created tokenizer structure. The generic
** sqlite3_tokenizer.pModule variable should not be initialized by
** this callback. The caller will do so.
*/
int (*xCreate)(
int argc, /* Size of argv array */
const char *const*argv, /* Tokenizer argument strings */
sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */
);
/*
** Destroy an existing tokenizer. The fts3 module calls this method
** exactly once for each successful call to xCreate().
*/
int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
/*
** Create a tokenizer cursor to tokenize an input buffer. The caller
** is responsible for ensuring that the input buffer remains valid
** until the cursor is closed (using the xClose() method).
*/
int (*xOpen)(
sqlite3_tokenizer *pTokenizer, /* Tokenizer object */
const char *pInput, int nBytes, /* Input buffer */
sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */
);
/*
** Destroy an existing tokenizer cursor. The fts3 module calls this
** method exactly once for each successful call to xOpen().
*/
int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
/*
** Retrieve the next token from the tokenizer cursor pCursor. This
** method should either return SQLITE_OK and set the values of the
** "OUT" variables identified below, or SQLITE_DONE to indicate that
** the end of the buffer has been reached, or an SQLite error code.
**
** *ppToken should be set to point at a buffer containing the
** normalized version of the token (i.e. after any case-folding and/or
** stemming has been performed). *pnBytes should be set to the length
** of this buffer in bytes. The input text that generated the token is
** identified by the byte offsets returned in *piStartOffset and
** *piEndOffset. *piStartOffset should be set to the index of the first
** byte of the token in the input buffer. *piEndOffset should be set
** to the index of the first byte just past the end of the token in
** the input buffer.
**
** The buffer *ppToken is set to point at is managed by the tokenizer
** implementation. It is only required to be valid until the next call
** to xNext() or xClose().
*/
/* TODO(shess) current implementation requires pInput to be
** nul-terminated. This should either be fixed, or pInput/nBytes
** should be converted to zInput.
*/
int (*xNext)(
sqlite3_tokenizer_cursor *pCursor, /* Tokenizer cursor */
const char **ppToken, int *pnBytes, /* OUT: Normalized text for token */
int *piStartOffset, /* OUT: Byte offset of token in input buffer */
int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */
int *piPosition /* OUT: Number of tokens returned before this one */
);
/***********************************************************************
** Methods below this point are only available if iVersion>=1.
*/
/*
** Configure the language id of a tokenizer cursor.
*/
int (*xLanguageid)(sqlite3_tokenizer_cursor *pCsr, int iLangid);
};
struct sqlite3_tokenizer {
const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
/* Tokenizer implementations will typically add additional fields */
};
struct sqlite3_tokenizer_cursor {
sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
/* Tokenizer implementations will typically add additional fields */
};
int fts3_global_term_cnt(int iTerm, int iCol);
int fts3_term_cnt(int iTerm, int iCol);
#endif /* _FTS3_TOKENIZER_H_ */
@@ -0,0 +1,32 @@
//
// 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;
@import SQLite3;
NS_ASSUME_NONNULL_BEGIN
typedef NSString * _Nullable (^_SQLiteTokenizerNextCallback)(const char *input, int *inputOffset, int *inputLength);
int _SQLiteRegisterTokenizer(sqlite3 *db, const char *module, const char *tokenizer, _Nullable _SQLiteTokenizerNextCallback callback);
NS_ASSUME_NONNULL_END