750 lines
26 KiB
Swift
750 lines
26 KiB
Swift
#if !USING_BUILTIN_SQLITE
|
|
#if os(OSX)
|
|
import SQLiteMacOSX
|
|
#elseif os(iOS)
|
|
#if (arch(i386) || arch(x86_64))
|
|
import SQLiteiPhoneSimulator
|
|
#else
|
|
import SQLiteiPhoneOS
|
|
#endif
|
|
#elseif os(watchOS)
|
|
#if (arch(i386) || arch(x86_64))
|
|
import SQLiteWatchSimulator
|
|
#else
|
|
import SQLiteWatchOS
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
|
|
// MARK: - _SQLSelectQuery
|
|
|
|
/// This type is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public struct _SQLSelectQuery {
|
|
var selection: [_SQLSelectable]
|
|
var distinct: Bool
|
|
var source: _SQLSource?
|
|
var whereExpression: _SQLExpression?
|
|
var groupByExpressions: [_SQLExpression]
|
|
var orderings: [_SQLOrdering]
|
|
var reversed: Bool
|
|
var havingExpression: _SQLExpression?
|
|
var limit: _SQLLimit?
|
|
|
|
init(
|
|
select selection: [_SQLSelectable],
|
|
distinct: Bool = false,
|
|
from source: _SQLSource? = nil,
|
|
filter whereExpression: _SQLExpression? = nil,
|
|
groupBy groupByExpressions: [_SQLExpression] = [],
|
|
orderBy orderings: [_SQLOrdering] = [],
|
|
reversed: Bool = false,
|
|
having havingExpression: _SQLExpression? = nil,
|
|
limit: _SQLLimit? = nil)
|
|
{
|
|
self.selection = selection
|
|
self.distinct = distinct
|
|
self.source = source
|
|
self.whereExpression = whereExpression
|
|
self.groupByExpressions = groupByExpressions
|
|
self.orderings = orderings
|
|
self.reversed = reversed
|
|
self.havingExpression = havingExpression
|
|
self.limit = limit
|
|
}
|
|
|
|
func sql(inout arguments: StatementArguments?) -> String {
|
|
var sql = "SELECT"
|
|
|
|
if distinct {
|
|
sql += " DISTINCT"
|
|
}
|
|
|
|
assert(!selection.isEmpty)
|
|
sql += " " + selection.map { $0.resultColumnSQL(&arguments) }.joinWithSeparator(", ")
|
|
|
|
if let source = source {
|
|
sql += " FROM " + source.sql(&arguments)
|
|
}
|
|
|
|
if let whereExpression = whereExpression {
|
|
sql += " WHERE " + whereExpression.sql(&arguments)
|
|
}
|
|
|
|
if !groupByExpressions.isEmpty {
|
|
sql += " GROUP BY " + groupByExpressions.map { $0.sql(&arguments) }.joinWithSeparator(", ")
|
|
}
|
|
|
|
if let havingExpression = havingExpression {
|
|
sql += " HAVING " + havingExpression.sql(&arguments)
|
|
}
|
|
|
|
var orderings = self.orderings
|
|
if reversed {
|
|
if orderings.isEmpty {
|
|
// https://www.sqlite.org/lang_createtable.html#rowid
|
|
//
|
|
// > The rowid value can be accessed using one of the special
|
|
// > case-independent names "rowid", "oid", or "_rowid_" in
|
|
// > place of a column name. If a table contains a user defined
|
|
// > column named "rowid", "oid" or "_rowid_", then that name
|
|
// > always refers the explicitly declared column and cannot be
|
|
// > used to retrieve the integer rowid value.
|
|
//
|
|
// Here we assume that _rowid_ is not a custom column.
|
|
// TODO: support for user-defined _rowid_ column.
|
|
// TODO: support for WITHOUT ROWID tables.
|
|
orderings = [SQLColumn("_rowid_").desc]
|
|
} else {
|
|
orderings = orderings.map { $0.reversedSortDescriptor }
|
|
}
|
|
}
|
|
if !orderings.isEmpty {
|
|
sql += " ORDER BY " + orderings.map { $0.orderingSQL(&arguments) }.joinWithSeparator(", ")
|
|
}
|
|
|
|
if let limit = limit {
|
|
sql += " LIMIT " + limit.sql
|
|
}
|
|
|
|
return sql
|
|
}
|
|
|
|
/// Returns a query that counts the number of rows matched by self.
|
|
var countQuery: _SQLSelectQuery {
|
|
guard groupByExpressions.isEmpty && limit == nil else {
|
|
// SELECT ... GROUP BY ...
|
|
// SELECT ... LIMIT ...
|
|
return trivialCountQuery
|
|
}
|
|
|
|
guard let source = source, case .Table(name: let tableName, alias: let alias) = source else {
|
|
// SELECT ... FROM (something which is not a table)
|
|
return trivialCountQuery
|
|
}
|
|
|
|
assert(!selection.isEmpty)
|
|
if selection.count == 1 {
|
|
let selectable = self.selection[0]
|
|
switch selectable.sqlSelectableKind {
|
|
case .Star(sourceName: let sourceName):
|
|
guard !distinct else {
|
|
return trivialCountQuery
|
|
}
|
|
|
|
if let sourceName = sourceName {
|
|
guard sourceName == tableName || sourceName == alias else {
|
|
return trivialCountQuery
|
|
}
|
|
}
|
|
|
|
// SELECT * FROM tableName ...
|
|
// ->
|
|
// SELECT COUNT(*) FROM tableName ...
|
|
var countQuery = unorderedQuery
|
|
countQuery.selection = [_SQLExpression.Count(selectable)]
|
|
return countQuery
|
|
|
|
case .Expression(let expression):
|
|
// SELECT [DISTINCT] expr FROM tableName ...
|
|
if distinct {
|
|
// SELECT DISTINCT expr FROM tableName ...
|
|
// ->
|
|
// SELECT COUNT(DISTINCT expr) FROM tableName ...
|
|
var countQuery = unorderedQuery
|
|
countQuery.distinct = false
|
|
countQuery.selection = [_SQLExpression.CountDistinct(expression)]
|
|
return countQuery
|
|
} else {
|
|
// SELECT expr FROM tableName ...
|
|
// ->
|
|
// SELECT COUNT(*) FROM tableName ...
|
|
var countQuery = unorderedQuery
|
|
countQuery.selection = [_SQLExpression.Count(_SQLResultColumn.Star(nil))]
|
|
return countQuery
|
|
}
|
|
}
|
|
} else {
|
|
// SELECT [DISTINCT] expr1, expr2, ... FROM tableName ...
|
|
|
|
guard !distinct else {
|
|
return trivialCountQuery
|
|
}
|
|
|
|
// SELECT expr1, expr2, ... FROM tableName ...
|
|
// ->
|
|
// SELECT COUNT(*) FROM tableName ...
|
|
var countQuery = unorderedQuery
|
|
countQuery.selection = [_SQLExpression.Count(_SQLResultColumn.Star(nil))]
|
|
return countQuery
|
|
}
|
|
}
|
|
|
|
func makeDeleteStatement(db: Database) -> UpdateStatement {
|
|
guard groupByExpressions.isEmpty else {
|
|
fatalError("Can't delete query with GROUP BY expression")
|
|
}
|
|
|
|
guard havingExpression == nil else {
|
|
fatalError("Can't delete query with GROUP BY expression")
|
|
}
|
|
|
|
guard limit == nil else {
|
|
fatalError("Can't delete query with limit")
|
|
}
|
|
|
|
var sql = "DELETE"
|
|
var arguments: StatementArguments? = StatementArguments()
|
|
|
|
if let source = source {
|
|
sql += " FROM " + source.sql(&arguments)
|
|
}
|
|
|
|
if let whereExpression = whereExpression {
|
|
sql += " WHERE " + whereExpression.sql(&arguments)
|
|
}
|
|
|
|
let statement = try! db.updateStatement(sql)
|
|
statement.arguments = arguments!
|
|
return statement
|
|
}
|
|
|
|
// SELECT COUNT(*) FROM (self)
|
|
private var trivialCountQuery: _SQLSelectQuery {
|
|
return _SQLSelectQuery(
|
|
select: [_SQLExpression.Count(_SQLResultColumn.Star(nil))],
|
|
from: .Query(query: unorderedQuery, alias: nil))
|
|
}
|
|
|
|
/// Remove ordering
|
|
private var unorderedQuery: _SQLSelectQuery {
|
|
var query = self
|
|
query.reversed = false
|
|
query.orderings = []
|
|
return query
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: - _SQLSource
|
|
|
|
indirect enum _SQLSource {
|
|
case Table(name: String, alias: String?)
|
|
case Query(query: _SQLSelectQuery, alias: String?)
|
|
|
|
func sql(inout arguments: StatementArguments?) -> String {
|
|
switch self {
|
|
case .Table(let table, let alias):
|
|
if let alias = alias {
|
|
return table.quotedDatabaseIdentifier + " AS " + alias.quotedDatabaseIdentifier
|
|
} else {
|
|
return table.quotedDatabaseIdentifier
|
|
}
|
|
case .Query(let query, let alias):
|
|
if let alias = alias {
|
|
return "(" + query.sql(&arguments) + ") AS " + alias.quotedDatabaseIdentifier
|
|
} else {
|
|
return "(" + query.sql(&arguments) + ")"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: - _SQLOrdering
|
|
|
|
/// This protocol is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public protocol _SQLOrdering {
|
|
var reversedSortDescriptor: _SQLSortDescriptor { get }
|
|
func orderingSQL(inout arguments: StatementArguments?) -> String
|
|
}
|
|
|
|
/// This type is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public enum _SQLSortDescriptor {
|
|
case Asc(_SQLExpression)
|
|
case Desc(_SQLExpression)
|
|
}
|
|
|
|
extension _SQLSortDescriptor : _SQLOrdering {
|
|
|
|
/// This property is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public var reversedSortDescriptor: _SQLSortDescriptor {
|
|
switch self {
|
|
case .Asc(let expression):
|
|
return .Desc(expression)
|
|
case .Desc(let expression):
|
|
return .Asc(expression)
|
|
}
|
|
}
|
|
|
|
/// This method is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public func orderingSQL(inout arguments: StatementArguments?) -> String {
|
|
switch self {
|
|
case .Asc(let expression):
|
|
return expression.sql(&arguments) + " ASC"
|
|
case .Desc(let expression):
|
|
return expression.sql(&arguments) + " DESC"
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: - _SQLLimit
|
|
|
|
struct _SQLLimit {
|
|
let limit: Int
|
|
let offset: Int?
|
|
|
|
var sql: String {
|
|
if let offset = offset {
|
|
return "\(limit) OFFSET \(offset)"
|
|
} else {
|
|
return "\(limit)"
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: - _SpecificSQLExpressible
|
|
|
|
/// This protocol is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public protocol _SpecificSQLExpressible : SQLExpressible {
|
|
// SQLExpressible can be adopted by Swift standard types, and user
|
|
// types, through the DatabaseValueConvertible protocol which inherits
|
|
// from SQLExpressible.
|
|
//
|
|
// For example, Int adopts SQLExpressible through
|
|
// DatabaseValueConvertible.
|
|
//
|
|
// _SpecificSQLExpressible, on the other side, is not adopted by any
|
|
// Swift standard type or any user type. It is only adopted by GRDB types,
|
|
// such as SQLColumn and _SQLExpression.
|
|
//
|
|
// This separation lets us define functions and operators that do not
|
|
// spill out. The three declarations below have no chance overloading a
|
|
// Swift-defined operator, or a user-defined operator:
|
|
//
|
|
// - ==(SQLExpressible, _SpecificSQLExpressible)
|
|
// - ==(_SpecificSQLExpressible, SQLExpressible)
|
|
// - ==(_SpecificSQLExpressible, _SpecificSQLExpressible)
|
|
}
|
|
|
|
extension _SpecificSQLExpressible where Self: _SQLOrdering {
|
|
|
|
/// This property is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public var reversedSortDescriptor: _SQLSortDescriptor {
|
|
return .Desc(sqlExpression)
|
|
}
|
|
|
|
/// This method is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public func orderingSQL(inout arguments: StatementArguments?) -> String {
|
|
return sqlExpression.sql(&arguments)
|
|
}
|
|
}
|
|
|
|
extension _SpecificSQLExpressible where Self: _SQLSelectable {
|
|
|
|
/// This method is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public func resultColumnSQL(inout arguments: StatementArguments?) -> String {
|
|
return sqlExpression.sql(&arguments)
|
|
}
|
|
|
|
/// This method is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public func countedSQL(inout arguments: StatementArguments?) -> String {
|
|
return sqlExpression.sql(&arguments)
|
|
}
|
|
|
|
/// This property is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public var sqlSelectableKind: _SQLSelectableKind {
|
|
return .Expression(sqlExpression)
|
|
}
|
|
}
|
|
|
|
extension _SpecificSQLExpressible {
|
|
|
|
/// Returns a value that can be used as an argument to FetchRequest.order()
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public var asc: _SQLSortDescriptor {
|
|
return .Asc(sqlExpression)
|
|
}
|
|
|
|
/// Returns a value that can be used as an argument to FetchRequest.order()
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public var desc: _SQLSortDescriptor {
|
|
return .Desc(sqlExpression)
|
|
}
|
|
|
|
/// Returns a value that can be used as an argument to FetchRequest.select()
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public func aliased(alias: String) -> _SQLSelectable {
|
|
return _SQLResultColumn.Expression(expression: sqlExpression, alias: alias)
|
|
}
|
|
}
|
|
|
|
|
|
/// This type is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public indirect enum _SQLExpression {
|
|
/// For example: `name || 'rrr' AS pirateName`
|
|
case Literal(String, StatementArguments?)
|
|
|
|
/// For example: `1` or `'foo'`
|
|
case Value(DatabaseValueConvertible?) // TODO: switch to DatabaseValue?
|
|
|
|
/// For example: `name`, `table.name`
|
|
case Identifier(identifier: String, sourceName: String?)
|
|
|
|
/// For example: `name = 'foo' COLLATE NOCASE`
|
|
case Collate(_SQLExpression, String)
|
|
|
|
/// For example: `NOT condition`
|
|
case Not(_SQLExpression)
|
|
|
|
/// For example: `name = 'foo'`
|
|
case Equal(_SQLExpression, _SQLExpression)
|
|
|
|
/// For example: `name <> 'foo'`
|
|
case NotEqual(_SQLExpression, _SQLExpression)
|
|
|
|
/// For example: `name IS NULL`
|
|
case Is(_SQLExpression, _SQLExpression)
|
|
|
|
/// For example: `name IS NOT NULL`
|
|
case IsNot(_SQLExpression, _SQLExpression)
|
|
|
|
/// For example: `-value`
|
|
case PrefixOperator(String, _SQLExpression)
|
|
|
|
/// For example: `age + 1`
|
|
case InfixOperator(String, _SQLExpression, _SQLExpression)
|
|
|
|
/// For example: `id IN (1,2,3)`
|
|
case In([_SQLExpression], _SQLExpression)
|
|
|
|
/// For example `id IN (SELECT ...)`
|
|
case InSubQuery(_SQLSelectQuery, _SQLExpression)
|
|
|
|
/// For example `EXISTS (SELECT ...)`
|
|
case Exists(_SQLSelectQuery)
|
|
|
|
/// For example: `age BETWEEN 1 AND 2`
|
|
case Between(value: _SQLExpression, min: _SQLExpression, max: _SQLExpression)
|
|
|
|
/// For example: `LOWER(name)`
|
|
case Function(String, [_SQLExpression])
|
|
|
|
/// For example: `COUNT(*)`
|
|
case Count(_SQLSelectable)
|
|
|
|
/// For example: `COUNT(DISTINCT name)`
|
|
case CountDistinct(_SQLExpression)
|
|
|
|
///
|
|
func sql(inout arguments: StatementArguments?) -> String {
|
|
switch self {
|
|
case .Literal(let sql, let literalArguments):
|
|
if let literalArguments = literalArguments {
|
|
guard arguments != nil else {
|
|
fatalError("Not implemented")
|
|
}
|
|
arguments!.values.appendContentsOf(literalArguments.values)
|
|
for (name, value) in literalArguments.namedValues {
|
|
guard arguments!.namedValues[name] == nil else {
|
|
fatalError("argument \(String(reflecting: name)) can't be reused")
|
|
}
|
|
arguments!.namedValues[name] = value
|
|
}
|
|
}
|
|
return sql
|
|
|
|
case .Value(let value):
|
|
guard let value = value else {
|
|
return "NULL"
|
|
}
|
|
if arguments == nil {
|
|
return value.sqlLiteral
|
|
} else {
|
|
arguments!.values.append(value)
|
|
return "?"
|
|
}
|
|
|
|
case .Identifier(let identifier, let sourceName):
|
|
if let sourceName = sourceName {
|
|
return sourceName.quotedDatabaseIdentifier + "." + identifier.quotedDatabaseIdentifier
|
|
} else {
|
|
return identifier.quotedDatabaseIdentifier
|
|
}
|
|
|
|
case .Collate(let expression, let collation):
|
|
let sql = expression.sql(&arguments)
|
|
let chars = sql.characters
|
|
if chars.last! == ")" {
|
|
return String(chars.prefixUpTo(chars.endIndex.predecessor())) + " COLLATE " + collation + ")"
|
|
} else {
|
|
return sql + " COLLATE " + collation
|
|
}
|
|
|
|
case .Not(let condition):
|
|
switch condition {
|
|
case .Not(let expression):
|
|
return expression.sql(&arguments)
|
|
|
|
case .In(let expressions, let expression):
|
|
if expressions.isEmpty {
|
|
return "1"
|
|
} else {
|
|
return "(" + expression.sql(&arguments) + " NOT IN (" + (expressions.map { $0.sql(&arguments) } as [String]).joinWithSeparator(", ") + "))"
|
|
}
|
|
|
|
case .InSubQuery(let subQuery, let expression):
|
|
return "(" + expression.sql(&arguments) + " NOT IN (" + subQuery.sql(&arguments) + "))"
|
|
|
|
case .Exists(let subQuery):
|
|
return "(NOT EXISTS (" + subQuery.sql(&arguments) + "))"
|
|
|
|
case .Equal(let lhs, let rhs):
|
|
return _SQLExpression.NotEqual(lhs, rhs).sql(&arguments)
|
|
|
|
case .NotEqual(let lhs, let rhs):
|
|
return _SQLExpression.Equal(lhs, rhs).sql(&arguments)
|
|
|
|
case .Is(let lhs, let rhs):
|
|
return _SQLExpression.IsNot(lhs, rhs).sql(&arguments)
|
|
|
|
case .IsNot(let lhs, let rhs):
|
|
return _SQLExpression.Is(lhs, rhs).sql(&arguments)
|
|
|
|
default:
|
|
return "(NOT " + condition.sql(&arguments) + ")"
|
|
}
|
|
|
|
case .Equal(let lhs, let rhs):
|
|
switch (lhs, rhs) {
|
|
case (let lhs, .Value(let rhs)) where rhs == nil:
|
|
// Swiftism!
|
|
// Turn `filter(a == nil)` into `a IS NULL` since the intention is obviously to check for NULL. `a = NULL` would evaluate to NULL.
|
|
return "(" + lhs.sql(&arguments) + " IS NULL)"
|
|
case (.Value(let lhs), let rhs) where lhs == nil:
|
|
// Swiftism!
|
|
return "(" + rhs.sql(&arguments) + " IS NULL)"
|
|
default:
|
|
return "(" + lhs.sql(&arguments) + " = " + rhs.sql(&arguments) + ")"
|
|
}
|
|
|
|
case .NotEqual(let lhs, let rhs):
|
|
switch (lhs, rhs) {
|
|
case (let lhs, .Value(let rhs)) where rhs == nil:
|
|
// Swiftism!
|
|
// Turn `filter(a != nil)` into `a IS NOT NULL` since the intention is obviously to check for NULL. `a <> NULL` would evaluate to NULL.
|
|
return "(" + lhs.sql(&arguments) + " IS NOT NULL)"
|
|
case (.Value(let lhs), let rhs) where lhs == nil:
|
|
// Swiftism!
|
|
return "(" + rhs.sql(&arguments) + " IS NOT NULL)"
|
|
default:
|
|
return "(" + lhs.sql(&arguments) + " <> " + rhs.sql(&arguments) + ")"
|
|
}
|
|
|
|
case .Is(let lhs, let rhs):
|
|
switch (lhs, rhs) {
|
|
case (let lhs, .Value(let rhs)) where rhs == nil:
|
|
return "(" + lhs.sql(&arguments) + " IS NULL)"
|
|
case (.Value(let lhs), let rhs) where lhs == nil:
|
|
return "(" + rhs.sql(&arguments) + " IS NULL)"
|
|
default:
|
|
return "(" + lhs.sql(&arguments) + " IS " + rhs.sql(&arguments) + ")"
|
|
}
|
|
|
|
case .IsNot(let lhs, let rhs):
|
|
switch (lhs, rhs) {
|
|
case (let lhs, .Value(let rhs)) where rhs == nil:
|
|
return "(" + lhs.sql(&arguments) + " IS NOT NULL)"
|
|
case (.Value(let lhs), let rhs) where lhs == nil:
|
|
return "(" + rhs.sql(&arguments) + " IS NOT NULL)"
|
|
default:
|
|
return "(" + lhs.sql(&arguments) + " IS NOT " + rhs.sql(&arguments) + ")"
|
|
}
|
|
|
|
case .PrefixOperator(let SQLOperator, let value):
|
|
return SQLOperator + value.sql(&arguments)
|
|
|
|
case .InfixOperator(let SQLOperator, let lhs, let rhs):
|
|
return "(" + lhs.sql(&arguments) + " \(SQLOperator) " + rhs.sql(&arguments) + ")"
|
|
|
|
case .In(let expressions, let expression):
|
|
guard !expressions.isEmpty else {
|
|
return "0"
|
|
}
|
|
return "(" + expression.sql(&arguments) + " IN (" + (expressions.map { $0.sql(&arguments) } as [String]).joinWithSeparator(", ") + "))"
|
|
|
|
case .InSubQuery(let subQuery, let expression):
|
|
return "(" + expression.sql(&arguments) + " IN (" + subQuery.sql(&arguments) + "))"
|
|
|
|
case .Exists(let subQuery):
|
|
return "(EXISTS (" + subQuery.sql(&arguments) + "))"
|
|
|
|
case .Between(value: let value, min: let min, max: let max):
|
|
return "(" + value.sql(&arguments) + " BETWEEN " + min.sql(&arguments) + " AND " + max.sql(&arguments) + ")"
|
|
|
|
case .Function(let functionName, let functionArguments):
|
|
return functionName + "(" + (functionArguments.map { $0.sql(&arguments) } as [String]).joinWithSeparator(", ") + ")"
|
|
|
|
case .Count(let counted):
|
|
return "COUNT(" + counted.countedSQL(&arguments) + ")"
|
|
|
|
case .CountDistinct(let expression):
|
|
return "COUNT(DISTINCT " + expression.sql(&arguments) + ")"
|
|
}
|
|
}
|
|
}
|
|
|
|
extension _SQLExpression : _SpecificSQLExpressible {
|
|
|
|
/// This property is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public var sqlExpression: _SQLExpression {
|
|
return self
|
|
}
|
|
}
|
|
|
|
extension _SQLExpression : _SQLSelectable {}
|
|
extension _SQLExpression : _SQLOrdering {}
|
|
|
|
|
|
// MARK: - _SQLSelectable
|
|
|
|
/// This protocol is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public protocol _SQLSelectable {
|
|
func resultColumnSQL(inout arguments: StatementArguments?) -> String
|
|
func countedSQL(inout arguments: StatementArguments?) -> String
|
|
var sqlSelectableKind: _SQLSelectableKind { get }
|
|
}
|
|
|
|
/// This type is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public enum _SQLSelectableKind {
|
|
case Expression(_SQLExpression)
|
|
case Star(sourceName: String?)
|
|
}
|
|
|
|
enum _SQLResultColumn {
|
|
case Star(String?)
|
|
case Expression(expression: _SQLExpression, alias: String)
|
|
}
|
|
|
|
extension _SQLResultColumn : _SQLSelectable {
|
|
|
|
func resultColumnSQL(inout arguments: StatementArguments?) -> String {
|
|
switch self {
|
|
case .Star(let sourceName):
|
|
if let sourceName = sourceName {
|
|
return sourceName.quotedDatabaseIdentifier + ".*"
|
|
} else {
|
|
return "*"
|
|
}
|
|
case .Expression(expression: let expression, alias: let alias):
|
|
return expression.sql(&arguments) + " AS " + alias.quotedDatabaseIdentifier
|
|
}
|
|
}
|
|
|
|
func countedSQL(inout arguments: StatementArguments?) -> String {
|
|
switch self {
|
|
case .Star:
|
|
return "*"
|
|
case .Expression(expression: let expression, alias: _):
|
|
return expression.sql(&arguments)
|
|
}
|
|
}
|
|
|
|
var sqlSelectableKind: _SQLSelectableKind {
|
|
switch self {
|
|
case .Star(let sourceName):
|
|
return .Star(sourceName: sourceName)
|
|
case .Expression(expression: let expression, alias: _):
|
|
return .Expression(expression)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: - SQLColumn
|
|
|
|
/// A column in the database
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift#the-query-interface
|
|
public struct SQLColumn {
|
|
let sourceName: String?
|
|
|
|
/// The name of the column
|
|
public let name: String
|
|
|
|
/// Initializes a column given its name.
|
|
public init(_ name: String) {
|
|
self.name = name
|
|
self.sourceName = nil
|
|
}
|
|
|
|
init(_ name: String, sourceName: String?) {
|
|
self.name = name
|
|
self.sourceName = sourceName
|
|
}
|
|
}
|
|
|
|
extension SQLColumn : _SpecificSQLExpressible {
|
|
|
|
/// This property is an implementation detail of the query interface.
|
|
/// Do not use it directly.
|
|
///
|
|
/// See https://github.com/groue/GRDB.swift/#the-query-interface
|
|
public var sqlExpression: _SQLExpression {
|
|
return .Identifier(identifier: name, sourceName: sourceName)
|
|
}
|
|
}
|
|
|
|
extension SQLColumn : _SQLSelectable {}
|
|
extension SQLColumn : _SQLOrdering {}
|