Files
UUVPN/iOS-SwiftUI-Code/GRDB.swift-6.29.3/GRDB/Core/StatementColumnConvertible.swift
T
2025-01-22 14:09:10 +08:00

848 lines
28 KiB
Swift

/// A type that can decode itself from the low-level C interface to
/// SQLite results.
///
/// `StatementColumnConvertible` is adopted by `Bool`, `Int`, `String`,
/// `Date`, and most common values.
///
/// When a type conforms to both ``DatabaseValueConvertible`` and
/// `StatementColumnConvertible`, GRDB can apply some optimization whenever
/// direct access to SQLite is possible. For example:
///
/// ```swift
/// // Optimized
/// let scores = Int.fetchAll(db, sql: "SELECT score FROM player")
///
/// let rows = try Row.fetchCursor(db, sql: "SELECT * FROM player")
/// while let row = try rows.next() {
/// // Optimized
/// let int: Int = row[0]
/// let name: String = row[1]
/// }
///
/// struct Player: FetchableRecord {
/// var name: String
/// var score: Int
///
/// init(row: Row) {
/// // Optimized
/// name = row["name"]
/// score = row["score"]
/// }
/// }
/// ```
///
/// To conform to `StatementColumnConvertible`, provide a custom implementation
/// of ``init(sqliteStatement:index:)-354je``. This implementation is ready-made
/// for `RawRepresentable` types whose `RawValue`
/// is `StatementColumnConvertible`.
///
/// Related SQLite documentation: <https://www.sqlite.org/c3ref/column_blob.html>
///
/// ## Topics
///
/// ### Creating a Value
///
/// - ``init(sqliteStatement:index:)-354je``
/// - ``fromStatement(_:atUncheckedIndex:)-2i8y6``
///
/// ### Fetching Values from Raw SQL
///
/// - ``DatabaseValueConvertible/fetchCursor(_:sql:arguments:adapter:)-4xfxh``
/// - ``DatabaseValueConvertible/fetchAll(_:sql:arguments:adapter:)-7bn2i``
/// - ``DatabaseValueConvertible/fetchSet(_:sql:arguments:adapter:)-1ythd``
/// - ``DatabaseValueConvertible/fetchOne(_:sql:arguments:adapter:)-563lc``
///
/// ### Fetching Values from a Prepared Statement
///
/// - ``DatabaseValueConvertible/fetchCursor(_:arguments:adapter:)-81f9d``
/// - ``DatabaseValueConvertible/fetchAll(_:arguments:adapter:)-64gua``
/// - ``DatabaseValueConvertible/fetchSet(_:arguments:adapter:)-9fh2b``
/// - ``DatabaseValueConvertible/fetchOne(_:arguments:adapter:)-8cbzp``
///
/// ### Fetching Values from a Request
///
/// - ``DatabaseValueConvertible/fetchCursor(_:_:)-77a34``
/// - ``DatabaseValueConvertible/fetchAll(_:_:)-7tnun``
/// - ``DatabaseValueConvertible/fetchSet(_:_:)-4bc1m``
/// - ``DatabaseValueConvertible/fetchOne(_:_:)-94q4e``
///
/// ### Supporting Types
///
/// - ``FastDatabaseValueCursor``
public protocol StatementColumnConvertible {
/// Creates an instance from a raw SQLite statement pointer, if possible.
///
/// This method can be called with a NULL database value.
///
/// - warning: Do not customize the default implementation.
///
/// - parameters:
/// - sqliteStatement: A pointer to an SQLite statement.
/// - index: The column index.
/// - returns: A decoded value, or, if decoding is impossible, nil.
static func fromStatement(
_ sqliteStatement: SQLiteStatement,
atUncheckedIndex index: CInt)
-> Self?
/// Creates an instance from a raw SQLite statement pointer, if possible.
///
/// Do not check for `NULL` in your implementation of this method. Null
/// database values are handled
/// in ``StatementColumnConvertible/fromStatement(_:atUncheckedIndex:)-2i8y6``.
///
/// For example, here is the how Int64 adopts StatementColumnConvertible:
///
/// ```swift
/// extension Int64: StatementColumnConvertible {
/// public init(sqliteStatement: SQLiteStatement, index: CInt) {
/// self = sqlite3_column_int64(sqliteStatement, index)
/// }
/// }
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/c3ref/column_blob.html>
///
/// - precondition: This initializer is not called with a NULL
/// database value.
/// - parameters:
/// - sqliteStatement: A pointer to an SQLite statement.
/// - index: The column index.
/// - returns: A decoded value, or, if decoding is impossible, nil.
init?(sqliteStatement: SQLiteStatement, index: CInt)
}
extension StatementColumnConvertible {
// `Optional` overrides this default behavior.
/// Default implementation fails on decoding NULL.
@inline(__always)
@inlinable
public static func fromStatement(_ sqliteStatement: SQLiteStatement, atUncheckedIndex index: CInt) -> Self? {
if sqlite3_column_type(sqliteStatement, index) == SQLITE_NULL {
return nil
}
return self.init(sqliteStatement: sqliteStatement, index: index)
}
}
// MARK: - Conversions
extension DatabaseValueConvertible where Self: StatementColumnConvertible {
@usableFromInline
/* private */ static func _valueMismatch(
fromStatement sqliteStatement: SQLiteStatement,
atUncheckedIndex index: CInt,
context: @autoclosure () -> RowDecodingContext)
throws -> Never
{
throw RowDecodingError.valueMismatch(
Self.self,
sqliteStatement: sqliteStatement,
index: index,
context: context())
}
@inline(__always)
@inlinable
static func fastDecode(
fromRow row: Row,
atUncheckedIndex index: Int)
throws -> Self
{
if let sqliteStatement = row.sqliteStatement {
return try fastDecode(
fromStatement: sqliteStatement,
atUncheckedIndex: CInt(index),
context: RowDecodingContext(row: row, key: .columnIndex(index)))
}
// Support for fast decoding from adapted rows
return try row.fastDecode(Self.self, atUncheckedIndex: index)
}
@inline(__always)
@inlinable
static func fastDecode(
fromStatement sqliteStatement: SQLiteStatement,
atUncheckedIndex index: CInt,
context: @autoclosure () -> RowDecodingContext)
throws -> Self
{
if let value = fromStatement(sqliteStatement, atUncheckedIndex: index) {
return value
} else {
try _valueMismatch(fromStatement: sqliteStatement, atUncheckedIndex: index, context: context())
}
}
// Support for Decodable
@inline(__always)
@inlinable
static func fastDecodeIfPresent(
fromRow row: Row,
atUncheckedIndex index: Int)
throws -> Self?
{
try Optional<Self>.fastDecode(fromRow: row, atUncheckedIndex: index)
}
}
// MARK: - Cursors
/// A cursor of database values.
///
/// A `FastDatabaseValueCursor` iterates all rows from a database request. Its
/// elements are the database values decoded from the leftmost column.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let names: FastDatabaseValueCursor<String> = try String.fetchCursor(db, sql: """
/// SELECT name FROM player
/// """)
/// while let name = names.next() { // String
/// print(name)
/// }
/// }
/// ```
public final class FastDatabaseValueCursor<Value>: DatabaseCursor
where Value: DatabaseValueConvertible & StatementColumnConvertible
{
public typealias Element = Value
public let _statement: Statement
public var _isDone = false
@usableFromInline let columnIndex: CInt
init(statement: Statement, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) throws {
self._statement = statement
if let adapter {
// adapter may redefine the index of the leftmost column
columnIndex = try CInt(adapter.baseColumnIndex(atIndex: 0, layout: statement))
} else {
columnIndex = 0
}
// Assume cursor is created for immediate iteration: reset and set arguments
try statement.prepareExecution(withArguments: arguments)
}
deinit {
// Statement reset fails when sqlite3_step has previously failed.
// Just ignore reset error.
try? _statement.reset()
}
@inlinable
public func _element(sqliteStatement: SQLiteStatement) throws -> Value {
try Value.fastDecode(
fromStatement: sqliteStatement,
atUncheckedIndex: columnIndex,
context: RowDecodingContext(statement: _statement, index: Int(columnIndex)))
}
}
// Explicit non-conformance to Sendable: database cursors must be used from
// a serialized database access dispatch queue.
@available(*, unavailable)
extension FastDatabaseValueCursor: Sendable { }
/// Types that adopt both DatabaseValueConvertible and
/// StatementColumnConvertible can be efficiently initialized from
/// database values.
///
/// See DatabaseValueConvertible for more information.
extension DatabaseValueConvertible where Self: StatementColumnConvertible {
// MARK: Fetching From Prepared Statement
/// Returns a cursor over values fetched from a prepared statement.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
/// let sql = "SELECT score FROM player WHERE lastName = ?"
/// let statement = try db.makeStatement(sql: sql)
/// let scores = try Int.fetchCursor(statement, arguments: [lastName])
/// while let score = try scores.next() {
/// print(score)
/// }
/// }
/// ```
///
/// Values are decoded from the leftmost column if the `adapter` argument
/// is nil.
///
/// The returned cursor is valid only during the remaining execution of the
/// database access. Do not store or return the cursor for later use.
///
/// If the database is modified during the cursor iteration, the remaining
/// elements are undefined.
///
/// - parameters:
/// - statement: The statement to run.
/// - arguments: Optional statement arguments.
/// - adapter: Optional RowAdapter
/// - returns: A ``FastDatabaseValueCursor`` over fetched values.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public static func fetchCursor(
_ statement: Statement,
arguments: StatementArguments? = nil,
adapter: (any RowAdapter)? = nil)
throws -> FastDatabaseValueCursor<Self>
{
try FastDatabaseValueCursor(statement: statement, arguments: arguments, adapter: adapter)
}
/// Returns an array of values fetched from a prepared statement.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
/// let sql = "SELECT score FROM player WHERE lastName = ?"
/// let statement = try db.makeStatement(sql: sql)
/// let scores = try Int.fetchAll(statement, arguments: [lastName])
/// }
/// ```
///
/// Values are decoded from the leftmost column if the `adapter` argument
/// is nil.
///
/// - parameters:
/// - statement: The statement to run.
/// - arguments: Optional statement arguments.
/// - adapter: Optional RowAdapter
/// - returns: An array of values.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public static func fetchAll(
_ statement: Statement,
arguments: StatementArguments? = nil,
adapter: (any RowAdapter)? = nil)
throws -> [Self]
{
try Array(fetchCursor(statement, arguments: arguments, adapter: adapter))
}
/// Returns a single value fetched from a prepared statement.
///
/// The value is decoded from the leftmost column if the `adapter` argument
/// is nil.
///
/// The result is nil if the request returns no row, or one row with a
/// `NULL` value.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
/// let sql = "SELECT score FROM player WHERE lastName = ? LIMIT 1"
/// let statement = try db.makeStatement(sql: sql)
/// let score = try Int.fetchOne(statement, arguments: [lastName])
/// }
/// ```
///
/// - parameters:
/// - statement: The statement to run.
/// - arguments: Optional statement arguments.
/// - adapter: Optional RowAdapter
/// - returns: An optional value.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public static func fetchOne(
_ statement: Statement,
arguments: StatementArguments? = nil,
adapter: (any RowAdapter)? = nil)
throws -> Self?
{
// fetchOne handles both a missing row, and one row with a NULL value.
let cursor = try FastDatabaseValueCursor<Self?>(
statement: statement,
arguments: arguments,
adapter: adapter)
return try cursor.next() ?? nil
}
}
extension DatabaseValueConvertible where Self: StatementColumnConvertible & Hashable {
/// Returns a set of values fetched from a prepared statement.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
/// let sql = "SELECT score FROM player WHERE lastName = ?"
/// let statement = try db.makeStatement(sql: sql)
/// let scores = try Int.fetchSet(statement, arguments: [lastName])
/// }
/// ```
///
/// Values are decoded from the leftmost column if the `adapter` argument
/// is nil.
///
/// - parameters:
/// - statement: The statement to run.
/// - arguments: Optional statement arguments.
/// - adapter: Optional RowAdapter
/// - returns: A set of values.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public static func fetchSet(
_ statement: Statement,
arguments: StatementArguments? = nil,
adapter: (any RowAdapter)? = nil)
throws -> Set<Self>
{
try Set(fetchCursor(statement, arguments: arguments, adapter: adapter))
}
}
extension DatabaseValueConvertible where Self: StatementColumnConvertible {
// MARK: Fetching From SQL
/// Returns a cursor over values fetched from an SQL query.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
/// let sql = "SELECT score FROM player WHERE lastName = ?"
/// let scores = try Int.fetchCursor(db, sql: sql, arguments: [lastName])
/// while let score = try scores.next() {
/// print(score)
/// }
/// }
/// ```
///
/// Values are decoded from the leftmost column if the `adapter` argument
/// is nil.
///
/// The returned cursor is valid only during the remaining execution of the
/// database access. Do not store or return the cursor for later use.
///
/// If the database is modified during the cursor iteration, the remaining
/// elements are undefined.
///
/// - parameters:
/// - db: A database connection.
/// - sql: An SQL string.
/// - arguments: Statement arguments.
/// - adapter: Optional RowAdapter
/// - returns: A ``FastDatabaseValueCursor`` over fetched values.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public static func fetchCursor(
_ db: Database,
sql: String,
arguments: StatementArguments = StatementArguments(),
adapter: (any RowAdapter)? = nil)
throws -> FastDatabaseValueCursor<Self>
{
try fetchCursor(db, SQLRequest(sql: sql, arguments: arguments, adapter: adapter))
}
/// Returns an array of values fetched from an SQL query.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
/// let sql = "SELECT score FROM player WHERE lastName = ?"
/// let scores = try Int.fetchAll(db, sql: sql, arguments: [lastName])
/// }
/// ```
///
/// Values are decoded from the leftmost column if the `adapter` argument
/// is nil.
///
/// - parameters:
/// - db: A database connection.
/// - sql: An SQL string.
/// - arguments: Statement arguments.
/// - adapter: Optional RowAdapter
/// - returns: An array of values.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public static func fetchAll(
_ db: Database,
sql: String,
arguments: StatementArguments = StatementArguments(),
adapter: (any RowAdapter)? = nil)
throws -> [Self]
{
try fetchAll(db, SQLRequest(sql: sql, arguments: arguments, adapter: adapter))
}
/// Returns a single value fetched from an SQL query.
///
/// The value is decoded from the leftmost column if the `adapter` argument
/// is nil.
///
/// The result is nil if the request returns no row, or one row with a
/// `NULL` value.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
/// let sql = "SELECT score FROM player WHERE lastName = ?"
/// let score = try Int.fetchOne(db, sql: sql, arguments: [lastName])
/// }
/// ```
///
/// - parameters:
/// - db: A database connection.
/// - sql: An SQL string.
/// - arguments: Statement arguments.
/// - adapter: Optional RowAdapter
/// - returns: An optional value.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public static func fetchOne(
_ db: Database,
sql: String,
arguments: StatementArguments = StatementArguments(),
adapter: (any RowAdapter)? = nil)
throws -> Self?
{
try fetchOne(db, SQLRequest(sql: sql, arguments: arguments, adapter: adapter))
}
}
extension DatabaseValueConvertible where Self: StatementColumnConvertible & Hashable {
/// Returns a set of values fetched from an SQL query.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
/// let sql = "SELECT score FROM player WHERE lastName = ?"
/// let scores = try Int.fetchSet(db, sql: sql, arguments: [lastName])
/// }
/// ```
///
/// Values are decoded from the leftmost column if the `adapter` argument
/// is nil.
///
/// - parameters:
/// - db: A database connection.
/// - sql: An SQL string.
/// - arguments: Statement arguments.
/// - adapter: Optional RowAdapter
/// - returns: A set of values.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public static func fetchSet(
_ db: Database,
sql: String,
arguments: StatementArguments = StatementArguments(),
adapter: (any RowAdapter)? = nil)
throws -> Set<Self>
{
try fetchSet(db, SQLRequest(sql: sql, arguments: arguments, adapter: adapter))
}
}
extension DatabaseValueConvertible where Self: StatementColumnConvertible {
// MARK: Fetching From FetchRequest
/// Returns a cursor over values fetched from a fetch request.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
///
/// // Query interface request
/// let request = Player
/// .select(Column("score"))
/// .filter(Column("lastName") == lastName)
///
/// // SQL request
/// let request: SQLRequest<Int> = """
/// SELECT score FROM player WHERE lastName = \(lastName)
/// """
///
/// let scores = try Int.fetchCursor(db, request)
/// while let score = try scores.next() {
/// print(score)
/// }
/// }
/// ```
///
/// Values are decoded from the leftmost column.
///
/// The returned cursor is valid only during the remaining execution of the
/// database access. Do not store or return the cursor for later use.
///
/// If the database is modified during the cursor iteration, the remaining
/// elements are undefined.
///
/// - parameters:
/// - db: A database connection.
/// - request: A FetchRequest.
/// - returns: A ``FastDatabaseValueCursor`` over fetched values.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public static func fetchCursor(_ db: Database, _ request: some FetchRequest)
throws -> FastDatabaseValueCursor<Self>
{
let request = try request.makePreparedRequest(db, forSingleResult: false)
return try fetchCursor(request.statement, adapter: request.adapter)
}
/// Returns an array of values fetched from a fetch request.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
///
/// // Query interface request
/// let request = Player
/// .select(Column("score"))
/// .filter(Column("lastName") == lastName)
///
/// // SQL request
/// let request: SQLRequest<Int> = """
/// SELECT score FROM player WHERE lastName = \(lastName)
/// """
///
/// let scores = try Int.fetchAll(db, request)
/// }
/// ```
///
/// Values are decoded from the leftmost column.
///
/// - parameters:
/// - db: A database connection.
/// - request: A FetchRequest.
/// - returns: An array of values.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public static func fetchAll(_ db: Database, _ request: some FetchRequest) throws -> [Self] {
let request = try request.makePreparedRequest(db, forSingleResult: false)
return try fetchAll(request.statement, adapter: request.adapter)
}
/// Returns a single value fetched from a fetch request.
///
/// The value is decoded from the leftmost column.
///
/// The result is nil if the request returns no row, or one row with a
/// `NULL` value.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
///
/// // Query interface request
/// let request = Player
/// .select(Column("score"))
/// .filter(Column("lastName") == lastName)
///
/// // SQL request
/// let request: SQLRequest<Int> = """
/// SELECT score FROM player WHERE lastName = \(lastName) LIMIT 1
/// """
///
/// let scores = try Int.fetchOne(db, request)
/// }
/// ```
///
/// - parameters:
/// - db: A database connection.
/// - request: A FetchRequest.
/// - returns: An optional value.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public static func fetchOne(_ db: Database, _ request: some FetchRequest) throws -> Self? {
let request = try request.makePreparedRequest(db, forSingleResult: true)
return try fetchOne(request.statement, adapter: request.adapter)
}
}
extension DatabaseValueConvertible where Self: StatementColumnConvertible & Hashable {
/// Returns a set of values fetched from a fetch request.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
///
/// // Query interface request
/// let request = Player
/// .select(Column("score"))
/// .filter(Column("lastName") == lastName)
///
/// // SQL request
/// let request: SQLRequest<Int> = """
/// SELECT score FROM player WHERE lastName = \(lastName)
/// """
///
/// let scores = try Int.fetchAll(db, request)
/// }
/// ```
///
/// Values are decoded from the leftmost column.
///
/// - parameters:
/// - db: A database connection.
/// - request: A FetchRequest.
/// - returns: A set of values.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public static func fetchSet(_ db: Database, _ request: some FetchRequest) throws -> Set<Self> {
let request = try request.makePreparedRequest(db, forSingleResult: false)
return try fetchSet(request.statement, adapter: request.adapter)
}
}
extension FetchRequest where RowDecoder: DatabaseValueConvertible & StatementColumnConvertible {
// MARK: Fetching Values
/// Returns a cursor over fetched values.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
///
/// // Query interface request
/// let request = Player
/// .filter(Column("lastName") == lastName)
/// .select(Column("score"), as: Int.self)
///
/// // SQL request
/// let request: SQLRequest<Int> = """
/// SELECT score FROM player WHERE lastName = \(lastName)
/// """
///
/// let scores = try request.fetchCursor(db)
/// while let score = try scores.next() {
/// print(score)
/// }
/// }
/// ```
///
/// Values are decoded from the leftmost column.
///
/// The returned cursor is valid only during the remaining execution of the
/// database access. Do not store or return the cursor for later use.
///
/// If the database is modified during the cursor iteration, the remaining
/// elements are undefined.
///
/// - parameter db: A database connection.
/// - returns: A ``FastDatabaseValueCursor`` over fetched values.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public func fetchCursor(_ db: Database) throws -> FastDatabaseValueCursor<RowDecoder> {
try RowDecoder.fetchCursor(db, self)
}
/// Returns an array of fetched values.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
///
/// // Query interface request
/// let request = Player
/// .filter(Column("lastName") == lastName)
/// .select(Column("score"), as: Int.self)
///
/// // SQL request
/// let request: SQLRequest<Int> = """
/// SELECT score FROM player WHERE lastName = \(lastName)
/// """
///
/// let scores = try request.fetchAll(db)
/// }
/// ```
///
/// Values are decoded from the leftmost column.
///
/// - parameter db: A database connection.
/// - returns: An array of values.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public func fetchAll(_ db: Database) throws -> [RowDecoder] {
try RowDecoder.fetchAll(db, self)
}
/// Returns a single fetched value.
///
/// The value is decoded from the leftmost column.
///
/// The result is nil if the request returns no row, or one row with a
/// `NULL` value.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
///
/// // Query interface request
/// let request = Player
/// .filter(Column("lastName") == lastName)
/// .select(Column("score"), as: Int.self)
///
/// // SQL request
/// let request: SQLRequest<Int> = """
/// SELECT score FROM player WHERE lastName = \(lastName) LIMIT 1
/// """
///
/// let score = try request.fetchOne(db)
/// }
/// ```
///
/// - parameter db: A database connection.
/// - returns: An optional value.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public func fetchOne(_ db: Database) throws -> RowDecoder? {
try RowDecoder.fetchOne(db, self)
}
}
extension FetchRequest where RowDecoder: DatabaseValueConvertible & StatementColumnConvertible & Hashable {
/// Returns a set of fetched values.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let lastName = "O'Reilly"
///
/// // Query interface request
/// let request = Player
/// .filter(Column("lastName") == lastName)
/// .select(Column("score"), as: Int.self)
///
/// // SQL request
/// let request: SQLRequest<Int> = """
/// SELECT score FROM player WHERE lastName = \(lastName)
/// """
///
/// let scores = try request.fetchSet(db)
/// }
/// ```
///
/// Values are decoded from the leftmost column.
///
/// - parameter db: A database connection.
/// - returns: A set of values.
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
public func fetchSet(_ db: Database) throws -> Set<RowDecoder> {
try RowDecoder.fetchSet(db, self)
}
}