GRDB.swift/GRDB/QueryInterface/QueryInterfaceRequest.swift
2019-03-13 21:24:54 +01:00

642 lines
26 KiB
Swift

// QueryInterfaceRequest is the type of requests generated by TableRecord:
//
// struct Player: TableRecord { ... }
// let playerRequest = Player.all() // QueryInterfaceRequest<Player>
//
// It wraps an SQLSelectQuery, and has an attached type.
//
// The attached type helps decoding raw database values:
//
// try dbQueue.read { db in
// try playerRequest.fetchAll(db) // [Player]
// }
//
// The attached type also helps the compiler validate associated requests:
//
// playerRequest.including(required: Player.team) // OK
// fruitRequest.including(required: Player.team) // Does not compile
/// QueryInterfaceRequest is a request that generates SQL for you.
///
/// For example:
///
/// try dbQueue.read { db in
/// let request = Player
/// .filter(Column("score") > 1000)
/// .order(Column("name"))
/// let players = try request.fetchAll(db) // [Player]
/// }
///
/// See https://github.com/groue/GRDB.swift#the-query-interface
public struct QueryInterfaceRequest<T> {
var query: SQLSelectQuery
}
extension QueryInterfaceRequest : FetchRequest {
public typealias RowDecoder = T
/// Returns a tuple that contains a prepared statement that is ready to be
/// executed, and an eventual row adapter.
///
/// - parameter db: A database connection.
/// - returns: A prepared statement and an eventual row adapter.
/// :nodoc:
public func prepare(_ db: Database) throws -> (SelectStatement, RowAdapter?) {
return try SQLSelectQueryGenerator(query).prepare(db)
}
/// Returns the number of rows fetched by the request.
///
/// - parameter db: A database connection.
/// :nodoc:
public func fetchCount(_ db: Database) throws -> Int {
return try query.fetchCount(db)
}
/// Returns the database region that the request looks into.
///
/// - parameter db: A database connection.
/// :nodoc:
public func databaseRegion(_ db: Database) throws -> DatabaseRegion {
return try SQLSelectQueryGenerator(query).databaseRegion(db)
}
}
extension QueryInterfaceRequest : DerivableRequest, AggregatingRequest {
// MARK: Request Derivation
/// Creates a request which selects *selection*.
///
/// // SELECT id, email FROM player
/// var request = Player.all()
/// request = request.select([Column("id"), Column("email")])
///
/// Any previous selection is replaced:
///
/// // SELECT email FROM player
/// request
/// .select([Column("id")])
/// .select([Column("email")])
public func select(_ selection: [SQLSelectable]) -> QueryInterfaceRequest {
return mapQuery { $0.select(selection) }
}
/// Creates a request which selects *selection*, and fetches values of
/// type *type*.
///
/// try dbQueue.read { db in
/// // SELECT max(score) FROM player
/// let request = Player.all().select([max(Column("score"))], as: Int.self)
/// let maxScore: Int? = try request.fetchOne(db)
/// }
public func select<RowDecoder>(_ selection: [SQLSelectable], as type: RowDecoder.Type) -> QueryInterfaceRequest<RowDecoder> {
return mapQuery { $0.select(selection) }.asRequest(of: RowDecoder.self)
}
/// Creates a request which selects *selection*, and fetches values of
/// type *type*.
///
/// try dbQueue.read { db in
/// // SELECT max(score) FROM player
/// let request = Player.all().select(max(Column("score")), as: Int.self)
/// let maxScore: Int? = try request.fetchOne(db)
/// }
public func select<RowDecoder>(_ selection: SQLSelectable..., as type: RowDecoder.Type) -> QueryInterfaceRequest<RowDecoder> {
return select(selection, as: type)
}
/// Creates a request which selects *sql*, and fetches values of
/// type *type*.
///
/// try dbQueue.read { db in
/// // SELECT max(score) FROM player
/// let request = Player.all().select(sql: "max(score)", as: Int.self)
/// let maxScore: Int? = try request.fetchOne(db)
/// }
public func select<RowDecoder>(sql: String, arguments: StatementArguments = StatementArguments(), as type: RowDecoder.Type) -> QueryInterfaceRequest<RowDecoder> {
return select(literal: SQLLiteral(sql: sql, arguments: arguments), as: type)
}
/// Creates a request which selects an SQL *literal*, and fetches values of
/// type *type*.
///
/// try dbQueue.read { db in
/// // SELECT IFNULL(name, 'Anonymous') FROM player WHERE id = 42
/// let request = Player.
/// .filter(primaryKey: 42)
/// .select(
/// SQLLiteral(
/// sql: "IFNULL(name, ?)",
/// arguments: ["Anonymous"]),
/// as: String.self)
/// let name: String? = try request.fetchOne(db)
/// }
///
/// With Swift 5, you can safely embed raw values in your SQL queries,
/// without any risk of syntax errors or SQL injection:
///
/// try dbQueue.read { db in
/// // SELECT IFNULL(name, 'Anonymous') FROM player WHERE id = 42
/// let request = Player.
/// .filter(primaryKey: 42)
/// .select(
/// literal: "IFNULL(name, \("Anonymous"))",
/// as: String.self)
/// let name: String? = try request.fetchOne(db)
/// }
public func select<RowDecoder>(literal sqlLiteral: SQLLiteral, as type: RowDecoder.Type) -> QueryInterfaceRequest<RowDecoder> {
return select(SQLSelectionLiteral(literal: sqlLiteral), as: type)
}
/// Creates a request which appends *selection*.
///
/// // SELECT id, email, name FROM player
/// var request = Player.all()
/// request = request
/// .select([Column("id"), Column("email")])
/// .annotated(with: [Column("name")])
public func annotated(with selection: [SQLSelectable]) -> QueryInterfaceRequest {
return mapQuery { $0.annotated(with: selection) }
}
/// Creates a request which returns distinct rows.
///
/// // SELECT DISTINCT * FROM player
/// var request = Player.all()
/// request = request.distinct()
///
/// // SELECT DISTINCT name FROM player
/// var request = Player.select(Column("name"))
/// request = request.distinct()
public func distinct() -> QueryInterfaceRequest {
return mapQuery { $0.distinct() }
}
/// Creates a request with the provided *predicate promise* added to the
/// eventual set of already applied predicates.
///
/// // SELECT * FROM player WHERE 1
/// var request = Player.all()
/// request = request.filter { db in true }
public func filter(_ predicate: @escaping (Database) throws -> SQLExpressible) -> QueryInterfaceRequest {
return mapQuery { $0.filter(predicate) }
}
/// Creates a request grouped according to *expressions promise*.
public func group(_ expressions: @escaping (Database) throws -> [SQLExpressible]) -> QueryInterfaceRequest {
return mapQuery { $0.group(expressions) }
}
/// Creates a request with the provided *predicate* added to the
/// eventual set of already applied predicates.
public func having(_ predicate: SQLExpressible) -> QueryInterfaceRequest {
return mapQuery { $0.having(predicate) }
}
/// Creates a request with the provided *orderings promise*.
///
/// // SELECT * FROM player ORDER BY name
/// var request = Player.all()
/// request = request.order { _ in [Column("name")] }
///
/// Any previous ordering is replaced:
///
/// // SELECT * FROM player ORDER BY name
/// request
/// .order{ _ in [Column("email")] }
/// .reversed()
/// .order{ _ in [Column("name")] }
public func order(_ orderings: @escaping (Database) throws -> [SQLOrderingTerm]) -> QueryInterfaceRequest {
return mapQuery { $0.order(orderings) }
}
/// Creates a request that reverses applied orderings.
///
/// // SELECT * FROM player ORDER BY name DESC
/// var request = Player.all().order(Column("name"))
/// request = request.reversed()
///
/// If no ordering was applied, the returned request is identical.
///
/// // SELECT * FROM player
/// var request = Player.all()
/// request = request.reversed()
public func reversed() -> QueryInterfaceRequest {
return mapQuery { $0.reversed() }
}
/// Creates a request which fetches *limit* rows, starting at *offset*.
///
/// // SELECT * FROM player LIMIT 1
/// var request = Player.all()
/// request = request.limit(1)
///
/// Any previous limit is replaced.
public func limit(_ limit: Int, offset: Int? = nil) -> QueryInterfaceRequest {
return mapQuery { $0.limit(limit, offset: offset) }
}
/// Creates a request that allows you to define expressions that target
/// a specific database table.
///
/// In the example below, the "team.avgScore < player.score" condition in
/// the ON clause could be not achieved without table aliases.
///
/// struct Player: TableRecord {
/// static let team = belongsTo(Team.self)
/// }
///
/// // SELECT player.*, team.*
/// // JOIN team ON ... AND team.avgScore < player.score
/// let playerAlias = TableAlias()
/// let request = Player
/// .all()
/// .aliased(playerAlias)
/// .including(required: Player.team.filter(Column("avgScore") < playerAlias[Column("score")])
public func aliased(_ alias: TableAlias) -> QueryInterfaceRequest {
return mapQuery { $0.qualified(with: alias) }
}
/// Creates a request bound to type Target.
///
/// The returned request can fetch if the type Target is fetchable (Row,
/// value, record).
///
/// // Int?
/// let maxScore = try Player
/// .select(max(scoreColumn))
/// .asRequest(of: Int.self) // <--
/// .fetchOne(db)
///
/// - parameter type: The fetched type Target
/// - returns: A typed request bound to type Target.
public func asRequest<RowDecoder>(of type: RowDecoder.Type) -> QueryInterfaceRequest<RowDecoder> {
return QueryInterfaceRequest<RowDecoder>(query: query)
}
/// Returns a request whose query is transformed by the given closure.
func mapQuery(_ transform: (SQLSelectQuery) -> SQLSelectQuery) -> QueryInterfaceRequest {
var request = self
request.query = transform(query)
return request
}
}
extension QueryInterfaceRequest {
/// Turns a request into a SQLRelation.
///
/// This method helps initializing associations:
///
/// struct Book: TableRecord {
/// // invokes Author.all().relation
/// static let author = belongsTo(Author.self)
/// }
var relation: SQLRelation {
let query = self.query
// Prevent information loss
GRDBPrecondition(!query.isDistinct, "Not implemented: join distinct queries")
GRDBPrecondition(query.groupPromise == nil, "Can't join aggregated queries")
GRDBPrecondition(query.havingExpression == nil, "Can't join aggregated queries")
GRDBPrecondition(query.limit == nil, "Can't join limited queries")
return query.relation
}
}
/// Conditional conformance to TableRequest when RowDecoder conforms
/// to TableRecord:
///
/// let request = Player.all()
/// request.filter(key: ...)
/// request.filter(keys: ...)
extension QueryInterfaceRequest: TableRequest where RowDecoder: TableRecord {
/// :nodoc:
public var databaseTableName: String {
return RowDecoder.databaseTableName
}
}
extension QueryInterfaceRequest where T: MutablePersistableRecord {
// MARK: Deleting
/// Deletes matching rows; returns the number of deleted rows.
///
/// - parameter db: A database connection.
/// - returns: The number of deleted rows
/// - throws: A DatabaseError is thrown whenever an SQLite error occurs.
@discardableResult
public func deleteAll(_ db: Database) throws -> Int {
try SQLSelectQueryGenerator(query).makeDeleteStatement(db).execute()
return db.changesCount
}
}
extension TableRecord {
// MARK: Request Derivation
/// Creates a request which fetches all records.
///
/// // SELECT * FROM player
/// let request = Player.all()
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func all() -> QueryInterfaceRequest<Self> {
let relation = SQLRelation(
source: .table(tableName: databaseTableName, alias: nil),
selection: databaseSelection)
let query = SQLSelectQuery(relation: relation)
return QueryInterfaceRequest(query: query)
}
/// Creates a request which fetches no record.
public static func none() -> QueryInterfaceRequest<Self> {
return all().none() // don't laugh
}
/// Creates a request which selects *selection*.
///
/// // SELECT id, email FROM player
/// let request = Player.select(Column("id"), Column("email"))
public static func select(_ selection: SQLSelectable...) -> QueryInterfaceRequest<Self> {
return all().select(selection)
}
/// Creates a request which selects *selection*.
///
/// // SELECT id, email FROM player
/// let request = Player.select([Column("id"), Column("email")])
public static func select(_ selection: [SQLSelectable]) -> QueryInterfaceRequest<Self> {
return all().select(selection)
}
/// Creates a request which selects *sql*.
///
/// // SELECT id, email FROM player
/// let request = Player.select(sql: "id, email")
public static func select(sql: String, arguments: StatementArguments = StatementArguments()) -> QueryInterfaceRequest<Self> {
return select(literal: SQLLiteral(sql: sql, arguments: arguments))
}
/// Creates a request which selects an SQL *literal*.
///
/// // SELECT id, email FROM player
/// let request = Player.select(literal: SQLLiteral(sql: "id, email"))
public static func select(literal sqlLiteral: SQLLiteral) -> QueryInterfaceRequest<Self> {
return all().select(literal: sqlLiteral)
}
/// Creates a request which selects *selection*, and fetches values of
/// type *type*.
///
/// try dbQueue.read { db in
/// // SELECT max(score) FROM player
/// let request = Player.select([max(Column("score"))], as: Int.self)
/// let maxScore: Int? = try request.fetchOne(db)
/// }
public static func select<RowDecoder>(_ selection: [SQLSelectable], as type: RowDecoder.Type) -> QueryInterfaceRequest<RowDecoder> {
return all().select(selection, as: type)
}
/// Creates a request which selects *selection*, and fetches values of
/// type *type*.
///
/// try dbQueue.read { db in
/// // SELECT max(score) FROM player
/// let request = Player.select(max(Column("score")), as: Int.self)
/// let maxScore: Int? = try request.fetchOne(db)
/// }
public static func select<RowDecoder>(_ selection: SQLSelectable..., as type: RowDecoder.Type) -> QueryInterfaceRequest<RowDecoder> {
return all().select(selection, as: type)
}
/// Creates a request which selects *sql*, and fetches values of
/// type *type*.
///
/// try dbQueue.read { db in
/// // SELECT max(score) FROM player
/// let request = Player.select(sql: "max(score)", as: Int.self)
/// let maxScore: Int? = try request.fetchOne(db)
/// }
public static func select<RowDecoder>(sql: String, arguments: StatementArguments = StatementArguments(), as type: RowDecoder.Type) -> QueryInterfaceRequest<RowDecoder> {
return all().select(literal: SQLLiteral(sql: sql, arguments: arguments), as: type)
}
/// Creates a request which selects an SQL *literal*, and fetches values of
/// type *type*.
///
/// try dbQueue.read { db in
/// // SELECT max(score) FROM player
/// let request = Player.select(literal: SQLLiteral(sql: "max(score)"), as: Int.self)
/// let maxScore: Int? = try request.fetchOne(db)
/// }
public static func select<RowDecoder>(literal sqlLiteral: SQLLiteral, as type: RowDecoder.Type) -> QueryInterfaceRequest<RowDecoder> {
return all().select(literal: sqlLiteral, as: type)
}
/// Creates a request with the provided *predicate*.
///
/// // SELECT * FROM player WHERE email = 'arthur@example.com'
/// let request = Player.filter(Column("email") == "arthur@example.com")
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func filter(_ predicate: SQLExpressible) -> QueryInterfaceRequest<Self> {
return all().filter(predicate)
}
/// Creates a request with the provided primary key *predicate*.
///
/// // SELECT * FROM player WHERE id = 1
/// let request = Player.filter(key: 1)
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func filter<PrimaryKeyType: DatabaseValueConvertible>(key: PrimaryKeyType?) -> QueryInterfaceRequest<Self> {
return all().filter(key: key)
}
/// Creates a request with the provided primary key *predicate*.
///
/// // SELECT * FROM player WHERE id IN (1, 2, 3)
/// let request = Player.filter(keys: [1, 2, 3])
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func filter<Sequence: Swift.Sequence>(keys: Sequence) -> QueryInterfaceRequest<Self> where Sequence.Element: DatabaseValueConvertible {
return all().filter(keys: keys)
}
/// Creates a request with the provided primary key *predicate*.
///
/// // SELECT * FROM passport WHERE personId = 1 AND countryCode = 'FR'
/// let request = Passport.filter(key: ["personId": 1, "countryCode": "FR"])
///
/// When executed, this request raises a fatal error if there is no unique
/// index on the key columns.
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func filter(key: [String: DatabaseValueConvertible?]?) -> QueryInterfaceRequest<Self> {
return all().filter(key: key)
}
/// Creates a request with the provided primary key *predicate*.
///
/// // SELECT * FROM passport WHERE (personId = 1 AND countryCode = 'FR') OR ...
/// let request = Passport.filter(keys: [["personId": 1, "countryCode": "FR"], ...])
///
/// When executed, this request raises a fatal error if there is no unique
/// index on the key columns.
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func filter(keys: [[String: DatabaseValueConvertible?]]) -> QueryInterfaceRequest<Self> {
return all().filter(keys: keys)
}
/// Creates a request with the provided *predicate*.
///
/// // SELECT * FROM player WHERE email = 'arthur@example.com'
/// let request = Player.filter(sql: "email = ?", arguments: ["arthur@example.com"])
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func filter(sql: String, arguments: StatementArguments = StatementArguments()) -> QueryInterfaceRequest<Self> {
return filter(literal: SQLLiteral(sql: sql, arguments: arguments))
}
/// Creates a request with the provided *predicate*.
///
/// // SELECT * FROM player WHERE email = 'arthur@example.com'
/// let request = Player.filter(literal: SQLLiteral(sql: "email = ?", arguments: ["arthur@example.com"]))
///
/// With Swift 5, you can safely embed raw values in your SQL queries,
/// without any risk of syntax errors or SQL injection:
///
/// let request = Player.filter(literal: "name = \("O'Brien"))
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func filter(literal sqlLiteral: SQLLiteral) -> QueryInterfaceRequest<Self> {
// NOT TESTED
return all().filter(literal: sqlLiteral)
}
/// Creates a request sorted according to the
/// provided *orderings*.
///
/// // SELECT * FROM player ORDER BY name
/// let request = Player.order(Column("name"))
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func order(_ orderings: SQLOrderingTerm...) -> QueryInterfaceRequest<Self> {
return all().order(orderings)
}
/// Creates a request sorted according to the
/// provided *orderings*.
///
/// // SELECT * FROM player ORDER BY name
/// let request = Player.order([Column("name")])
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func order(_ orderings: [SQLOrderingTerm]) -> QueryInterfaceRequest<Self> {
return all().order(orderings)
}
/// Creates a request sorted by primary key.
///
/// // SELECT * FROM player ORDER BY id
/// let request = Player.orderByPrimaryKey()
///
/// // SELECT * FROM country ORDER BY code
/// let request = Country.orderByPrimaryKey()
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func orderByPrimaryKey() -> QueryInterfaceRequest<Self> {
return all().orderByPrimaryKey()
}
/// Creates a request sorted according to *sql*.
///
/// // SELECT * FROM player ORDER BY name
/// let request = Player.order(sql: "name")
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func order(sql: String, arguments: StatementArguments = StatementArguments()) -> QueryInterfaceRequest<Self> {
return all().order(literal: SQLLiteral(sql: sql, arguments: arguments))
}
/// Creates a request sorted according to an SQL *literal*.
///
/// // SELECT * FROM player ORDER BY name
/// let request = Player.order(literal: SQLLiteral(sql: "name"))
///
/// With Swift 5, you can safely embed raw values in your SQL queries,
/// without any risk of syntax errors or SQL injection:
///
/// // SELECT * FROM player ORDER BY name
/// let request = Player.order(literal: "name"))
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func order(literal sqlLiteral: SQLLiteral) -> QueryInterfaceRequest<Self> {
return all().order(literal: sqlLiteral)
}
/// Creates a request which fetches *limit* rows, starting at
/// *offset*.
///
/// // SELECT * FROM player LIMIT 1
/// let request = Player.limit(1)
///
/// The selection defaults to all columns. This default can be changed for
/// all requests by the `TableRecord.databaseSelection` property, or
/// for individual requests with the `TableRecord.select` method.
public static func limit(_ limit: Int, offset: Int? = nil) -> QueryInterfaceRequest<Self> {
return all().limit(limit, offset: offset)
}
/// Creates a request that allows you to define expressions that target
/// a specific database table.
///
/// In the example below, the "team.avgScore < player.score" condition in
/// the ON clause could be not achieved without table aliases.
///
/// struct Player: TableRecord {
/// static let team = belongsTo(Team.self)
/// }
///
/// // SELECT player.*, team.*
/// // JOIN team ON ... AND team.avgScore < player.score
/// let playerAlias = TableAlias()
/// let request = Player
/// .aliased(playerAlias)
/// .including(required: Player.team.filter(Column("avgScore") < playerAlias[Column("score")])
public static func aliased(_ alias: TableAlias) -> QueryInterfaceRequest<Self> {
return all().aliased(alias)
}
}