/// 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 /// /// ## 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) } }