Files
2025-01-28 12:28:03 +08:00

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 }
}