127 lines
3.2 KiB
Swift
127 lines
3.2 KiB
Swift
/// A type that represents a column in a database table.
|
|
///
|
|
/// ## Topics
|
|
///
|
|
/// ### Standard Columns
|
|
///
|
|
/// - ``rowID``
|
|
///
|
|
/// ### Deriving SQL Expressions
|
|
///
|
|
/// - ``detached``
|
|
/// - ``match(_:)-727nk``
|
|
/// - ``match(_:)-1vvo8``
|
|
///
|
|
/// ### Creating Column Assignments
|
|
///
|
|
/// - ``noOverwrite``
|
|
/// - ``set(to:)``
|
|
public protocol ColumnExpression: SQLSpecificExpressible {
|
|
/// The column name.
|
|
///
|
|
/// The column name is never qualified with a table name.
|
|
///
|
|
/// For example, the name of a column can be `"score"`, but
|
|
/// not `"player.score"`.
|
|
var name: String { get }
|
|
}
|
|
|
|
extension ColumnExpression {
|
|
/// Returns an SQL column.
|
|
public var sqlExpression: SQLExpression {
|
|
.column(name)
|
|
}
|
|
|
|
/// An SQL expression that refers to an aliased column
|
|
/// (`expression AS alias`).
|
|
///
|
|
/// Once detached, a column is never qualified with any table name in the
|
|
/// SQL generated by the query interface.
|
|
///
|
|
/// For example, see how `Column("total").detached` makes it possible to
|
|
/// sort this query, when a raw `Column("total")` could not:
|
|
///
|
|
/// ```swift
|
|
/// // SELECT player.*,
|
|
/// // (player.score + player.bonus) AS total,
|
|
/// // team.*
|
|
/// // FROM player
|
|
/// // JOIN team ON team.id = player.teamID
|
|
/// // ORDER BY total, player.name
|
|
/// // ~~~~~
|
|
/// let request = Player
|
|
/// .annotated(with: (Column("score") + Column("bonus")).forKey("total"))
|
|
/// .including(required: Player.team)
|
|
/// .order(Column("total").detached, Column("name"))
|
|
/// ```
|
|
public var detached: SQLExpression {
|
|
SQL(sql: name.quotedDatabaseIdentifier).sqlExpression
|
|
}
|
|
}
|
|
|
|
extension ColumnExpression where Self == Column {
|
|
/// The hidden rowID column.
|
|
public static var rowID: Self { Column.rowID }
|
|
}
|
|
|
|
/// A column in a database table.
|
|
///
|
|
/// For example:
|
|
///
|
|
/// ```swift
|
|
/// struct Player: TableRecord {
|
|
/// var score: Int
|
|
/// }
|
|
///
|
|
/// let maximumScore = try dbQueue.read { db in
|
|
/// // SELECT MAX(score) FROM player
|
|
/// try Player
|
|
/// .select(max(Column("score")), as: Int.self)
|
|
/// .fetchOne(db)
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// ## Topics
|
|
///
|
|
/// ### Standard Columns
|
|
///
|
|
/// - ``rowID-3bn70``
|
|
/// - ``rank``
|
|
///
|
|
/// ### Creating A Column
|
|
///
|
|
/// - ``init(_:)-5grmu``
|
|
/// - ``init(_:)-7xc4z``
|
|
public struct Column: Sendable {
|
|
/// The hidden rowID column.
|
|
public static let rowID = Column("rowid")
|
|
|
|
public var name: String
|
|
|
|
/// Creates a `Column` given its name.
|
|
///
|
|
/// The name should be unqualified, such as `"score"`. Qualified name such
|
|
/// as `"player.score"` are unsupported.
|
|
public init(_ name: String) {
|
|
self.name = name
|
|
}
|
|
|
|
/// Creates a `Column` given a `CodingKey`.
|
|
public init(_ codingKey: some CodingKey) {
|
|
self.name = codingKey.stringValue
|
|
}
|
|
}
|
|
|
|
extension Column: ColumnExpression { }
|
|
|
|
/// Support for column enums:
|
|
///
|
|
/// struct Player {
|
|
/// enum Columns: String, ColumnExpression {
|
|
/// case id, name, score
|
|
/// }
|
|
/// }
|
|
extension ColumnExpression where Self: RawRepresentable, Self.RawValue == String {
|
|
public var name: String { rawValue }
|
|
}
|