add
This commit is contained in:
@@ -1,136 +0,0 @@
|
||||
/// An SQL subquery.
|
||||
///
|
||||
/// `SQLSubquery` is an opaque representation of an SQL subquery.
|
||||
public struct SQLSubquery {
|
||||
private var impl: Impl
|
||||
|
||||
private enum Impl {
|
||||
/// A literal SQL query
|
||||
case literal(SQL)
|
||||
|
||||
/// A query interface relation
|
||||
case relation(SQLRelation)
|
||||
}
|
||||
|
||||
static func literal(_ sqlLiteral: SQL) -> Self {
|
||||
self.init(impl: .literal(sqlLiteral))
|
||||
}
|
||||
|
||||
static func relation(_ relation: SQLRelation) -> Self {
|
||||
self.init(impl: .relation(relation))
|
||||
}
|
||||
}
|
||||
|
||||
extension SQLSubquery {
|
||||
/// The number of columns selected by the subquery.
|
||||
///
|
||||
/// This method makes it possible to find the columns of a CTE in a request
|
||||
/// that includes a CTE association:
|
||||
///
|
||||
/// // WITH cte AS (SELECT 1 AS a, 2 AS b)
|
||||
/// // SELECT player.*, cte.*
|
||||
/// // FROM player
|
||||
/// // JOIN cte
|
||||
/// let cte = CommonTableExpression(named: "cte", sql: "SELECT 1 AS a, 2 AS b")
|
||||
/// let request = Player
|
||||
/// .with(cte)
|
||||
/// .including(required: Player.association(to: cte))
|
||||
/// let row = try Row.fetchOne(db, request)!
|
||||
///
|
||||
/// // We know that "SELECT 1 AS a, 2 AS b" selects two columns,
|
||||
/// // so we can find cte columns in the row:
|
||||
/// row.scopes["cte"] // [a:1, b:2]
|
||||
func columnCount(_ db: Database) throws -> Int {
|
||||
switch impl {
|
||||
case let .literal(sqlLiteral):
|
||||
// Compile request. We can freely use the statement cache because we
|
||||
// do not execute the statement or modify its arguments.
|
||||
let context = SQLGenerationContext(db)
|
||||
let sql = try sqlLiteral.sql(context)
|
||||
let statement = try db.cachedStatement(sql: sql)
|
||||
return statement.columnCount
|
||||
|
||||
case let .relation(relation):
|
||||
return try SQLQueryGenerator(relation: relation).columnCount(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SQLSubquery {
|
||||
/// Returns the subquery SQL.
|
||||
///
|
||||
/// For example:
|
||||
///
|
||||
/// // SELECT *
|
||||
/// // FROM "player"
|
||||
/// // WHERE "score" = (SELECT MAX("score") FROM "player")
|
||||
/// let maxScore = Player.select(max(Column("score")))
|
||||
/// let players = try Player
|
||||
/// .filter(Column("score") == maxScore)
|
||||
/// .fetchAll(db)
|
||||
///
|
||||
/// - parameter context: An SQL generation context.
|
||||
/// - parameter singleResult: A hint that a single result row will be
|
||||
/// consumed. Implementations can optionally use it to optimize the
|
||||
/// generated SQL, for example by adding a `LIMIT 1` SQL clause.
|
||||
/// - returns: An SQL string.
|
||||
func sql(_ context: SQLGenerationContext) throws -> String {
|
||||
switch impl {
|
||||
case let .literal(sqlLiteral):
|
||||
return try sqlLiteral.sql(context)
|
||||
|
||||
case let .relation(relation):
|
||||
return try SQLQueryGenerator(relation: relation).requestSQL(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SQLSubqueryable
|
||||
|
||||
/// A type that can be used as SQL subquery.
|
||||
///
|
||||
/// Related SQLite documentation <https://www.sqlite.org/syntax/select-stmt.html>
|
||||
///
|
||||
/// ## Topics
|
||||
///
|
||||
/// ### Supporting Types
|
||||
///
|
||||
/// - ``SQLSubquery``
|
||||
public protocol SQLSubqueryable: SQLSpecificExpressible {
|
||||
var sqlSubquery: SQLSubquery { get }
|
||||
}
|
||||
|
||||
extension SQLSubquery: SQLSubqueryable {
|
||||
// Not a real deprecation, just a usage warning
|
||||
@available(*, deprecated, message: "Already SQLSubquery")
|
||||
public var sqlSubquery: SQLSubquery { self }
|
||||
}
|
||||
|
||||
extension SQLSubqueryable {
|
||||
/// Returns a subquery expression.
|
||||
public var sqlExpression: SQLExpression {
|
||||
.subquery(sqlSubquery)
|
||||
}
|
||||
}
|
||||
|
||||
extension SQLSubqueryable {
|
||||
/// Returns an expression that checks the inclusion of the expression in
|
||||
/// the subquery.
|
||||
///
|
||||
/// // 1000 IN (SELECT score FROM player)
|
||||
/// let request = Player.select(Column("score"), as: Int.self)
|
||||
/// let condition = request.contains(1000)
|
||||
public func contains(_ element: some SQLExpressible) -> SQLExpression {
|
||||
SQLCollection.subquery(sqlSubquery).contains(element.sqlExpression)
|
||||
}
|
||||
|
||||
/// Returns an expression that is true if and only if the subquery would
|
||||
/// return one or more rows.
|
||||
///
|
||||
/// // EXISTS (SELECT * FROM player WHERE name = 'Arthur')
|
||||
/// let request = Player.filter(Column("name") == "Arthur")
|
||||
/// let condition = request.exists()
|
||||
public func exists() -> SQLExpression {
|
||||
.exists(sqlSubquery)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user