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

536 lines
13 KiB
Swift

/// The `ABS` SQL function.
///
/// For example:
///
/// ```swift
/// // ABS(amount)
/// abs(Column("amount"))
/// ```
public func abs(_ value: some SQLSpecificExpressible) -> SQLExpression {
.function("ABS", [value.sqlExpression])
}
#if GRDBCUSTOMSQLITE || GRDBCIPHER
/// The `AVG` SQL function.
///
/// For example:
///
/// ```swift
/// // AVG(length)
/// average(Column("length"))
/// ```
public func average(
_ value: some SQLSpecificExpressible,
filter: (any SQLSpecificExpressible)? = nil)
-> SQLExpression {
.aggregateFunction("AVG", [value.sqlExpression], filter: filter?.sqlExpression)
}
#else
/// The `AVG` SQL function.
///
/// For example:
///
/// ```swift
/// // AVG(length) FILTER (WHERE length > 0)
/// average(Column("length"), filter: Column("length") > 0)
/// ```
@available(iOS 14, macOS 10.16, tvOS 14, watchOS 7, *) // SQLite 3.30+
public func average(
_ value: some SQLSpecificExpressible,
filter: some SQLSpecificExpressible)
-> SQLExpression {
.aggregateFunction(
"AVG", [value.sqlExpression],
filter: filter.sqlExpression)
}
/// The `AVG` SQL function.
///
/// For example:
///
/// ```swift
/// // AVG(length)
/// average(Column("length"))
/// ```
public func average(_ value: some SQLSpecificExpressible) -> SQLExpression {
.aggregateFunction("AVG", [value.sqlExpression])
}
#endif
/// The `CAST` SQL function.
///
/// For example:
///
/// ```swift
/// // CAST(value AS REAL)
/// cast(Column("value"), as: .real)
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/lang_expr.html#castexpr>
public func cast(_ expression: some SQLSpecificExpressible, as storageClass: Database.StorageClass) -> SQLExpression {
.cast(expression.sqlExpression, as: storageClass)
}
/// The `COUNT` SQL function.
///
/// For example:
///
/// ```swift
/// // COUNT(email)
/// count(Column("email"))
/// ```
public func count(_ counted: some SQLSpecificExpressible) -> SQLExpression {
.count(counted.sqlExpression)
}
/// The `COUNT(DISTINCT)` SQL function.
///
/// For example:
///
/// ```swift
/// // COUNT(DISTINCT email)
/// count(distinct: Column("email"))
/// ```
public func count(distinct value: some SQLSpecificExpressible) -> SQLExpression {
.countDistinct(value.sqlExpression)
}
extension SQLSpecificExpressible {
/// The `IFNULL` SQL function.
///
/// For example:
///
/// ```swift
/// // IFNULL(name, 'Anonymous')
/// Column("name") ?? "Anonymous"
/// ```
public static func ?? (lhs: Self, rhs: some SQLExpressible) -> SQLExpression {
.function("IFNULL", [lhs.sqlExpression, rhs.sqlExpression])
}
}
/// The `LENGTH` SQL function.
///
/// For example:
///
/// ```swift
/// // LENGTH(name)
/// length(Column("name"))
/// ```
public func length(_ value: some SQLSpecificExpressible) -> SQLExpression {
.function("LENGTH", [value.sqlExpression])
}
#if GRDBCUSTOMSQLITE || GRDBCIPHER
/// The `MAX` SQL function.
///
/// For example:
///
/// ```swift
/// // MAX(score)
/// max(Column("score"))
/// ```
public func max(
_ value: some SQLSpecificExpressible,
filter: (any SQLSpecificExpressible)? = nil)
-> SQLExpression {
.aggregateFunction("MAX", [value.sqlExpression], filter: filter?.sqlExpression)
}
#else
/// The `MAX` SQL function.
///
/// For example:
///
/// ```swift
/// // MAX(score) FILTER (WHERE score < 0)
/// max(Column("score"), filter: Column("score") < 0)
/// ```
@available(iOS 14, macOS 10.16, tvOS 14, watchOS 7, *) // SQLite 3.30+
public func max(
_ value: some SQLSpecificExpressible,
filter: some SQLSpecificExpressible)
-> SQLExpression {
.aggregateFunction("MAX", [value.sqlExpression], filter: filter.sqlExpression)
}
/// The `MAX` SQL function.
///
/// For example:
///
/// ```swift
/// // MAX(score)
/// max(Column("score"))
/// ```
public func max(_ value: some SQLSpecificExpressible) -> SQLExpression {
.aggregateFunction("MAX", [value.sqlExpression])
}
#endif
#if GRDBCUSTOMSQLITE || GRDBCIPHER
/// The `MIN` SQL function.
///
/// For example:
///
/// ```swift
/// // MIN(score)
/// min(Column("score"))
/// ```
public func min(
_ value: some SQLSpecificExpressible,
filter: (any SQLSpecificExpressible)? = nil)
-> SQLExpression {
.aggregateFunction("MIN", [value.sqlExpression], filter: filter?.sqlExpression)
}
#else
/// The `MIN` SQL function.
///
/// For example:
///
/// ```swift
/// // MIN(score) FILTER (WHERE score > 0)
/// min(Column("score"), filter: Column("score") > 0)
/// ```
@available(iOS 14, macOS 10.16, tvOS 14, watchOS 7, *) // SQLite 3.30+
public func min(
_ value: some SQLSpecificExpressible,
filter: some SQLSpecificExpressible)
-> SQLExpression {
.aggregateFunction("MIN", [value.sqlExpression], filter: filter.sqlExpression)
}
/// The `MIN` SQL function.
///
/// For example:
///
/// ```swift
/// // MIN(score)
/// min(Column("score"))
/// ```
public func min(_ value: some SQLSpecificExpressible) -> SQLExpression {
.aggregateFunction("MIN", [value.sqlExpression])
}
#endif
#if GRDBCUSTOMSQLITE || GRDBCIPHER
/// The `SUM` SQL function.
///
/// For example:
///
/// ```swift
/// // SUM(amount)
/// sum(Column("amount"))
/// ```
///
/// See also ``total(_:)``.
///
/// Related SQLite documentation: <https://www.sqlite.org/lang_aggfunc.html#sumunc>.
public func sum(
_ value: some SQLSpecificExpressible,
orderBy ordering: (any SQLOrderingTerm)? = nil,
filter: (any SQLSpecificExpressible)? = nil)
-> SQLExpression
{
.aggregateFunction(
"SUM", [value.sqlExpression],
ordering: ordering?.sqlOrdering,
filter: filter?.sqlExpression)
}
#else
/// The `SUM` SQL function.
///
/// For example:
///
/// ```swift
/// // SUM(amount) FILTER (WHERE amount > 0)
/// sum(Column("amount"), filter: Column("amount") > 0)
/// ```
///
/// See also ``total(_:)``.
///
/// Related SQLite documentation: <https://www.sqlite.org/lang_aggfunc.html#sumunc>.
@available(iOS 14, macOS 10.16, tvOS 14, watchOS 7, *) // SQLite 3.30+
public func sum(
_ value: some SQLSpecificExpressible,
filter: some SQLSpecificExpressible)
-> SQLExpression {
.aggregateFunction(
"SUM", [value.sqlExpression],
filter: filter.sqlExpression)
}
/// The `SUM` SQL function.
///
/// For example:
///
/// ```swift
/// // SUM(amount)
/// sum(Column("amount"))
/// ```
///
/// See also ``total(_:)``.
///
/// Related SQLite documentation: <https://www.sqlite.org/lang_aggfunc.html#sumunc>.
public func sum(_ value: some SQLSpecificExpressible) -> SQLExpression {
.aggregateFunction("SUM", [value.sqlExpression])
}
#endif
#if GRDBCUSTOMSQLITE || GRDBCIPHER
/// The `TOTAL` SQL function.
///
/// For example:
///
/// ```swift
/// // TOTAL(amount)
/// total(Column("amount"))
/// ```
///
/// See also ``sum(_:)``.
///
/// Related SQLite documentation: <https://www.sqlite.org/lang_aggfunc.html#sumunc>.
public func total(
_ value: some SQLSpecificExpressible,
orderBy ordering: (any SQLOrderingTerm)? = nil,
filter: (any SQLSpecificExpressible)? = nil)
-> SQLExpression
{
.aggregateFunction(
"TOTAL", [value.sqlExpression],
ordering: ordering?.sqlOrdering,
filter: filter?.sqlExpression)
}
#else
/// The `TOTAL` SQL function.
///
/// For example:
///
/// ```swift
/// // TOTAL(amount) FILTER (WHERE amount > 0)
/// total(Column("amount"), filter: Column("amount") > 0)
/// ```
///
/// See also ``total(_:)``.
///
/// Related SQLite documentation: <https://www.sqlite.org/lang_aggfunc.html#sumunc>.
@available(iOS 14, macOS 10.16, tvOS 14, watchOS 7, *) // SQLite 3.30+
public func total(
_ value: some SQLSpecificExpressible,
filter: some SQLSpecificExpressible)
-> SQLExpression {
.aggregateFunction(
"TOTAL", [value.sqlExpression],
filter: filter.sqlExpression)
}
/// The `TOTAL` SQL function.
///
/// For example:
///
/// ```swift
/// // TOTAL(amount)
/// total(Column("amount"))
/// ```
///
/// See also ``sum(_:)``.
///
/// Related SQLite documentation: <https://www.sqlite.org/lang_aggfunc.html#sumunc>.
public func total(_ value: some SQLSpecificExpressible) -> SQLExpression {
.aggregateFunction("TOTAL", [value.sqlExpression])
}
#endif
// MARK: - String functions
extension SQLSpecificExpressible {
/// An SQL expression that calls the Foundation
/// `String.capitalized` property.
///
/// For example:
///
/// ```swift
/// Column("name").capitalized
/// ```
public var capitalized: SQLExpression {
DatabaseFunction.capitalize(sqlExpression)
}
/// An SQL expression that calls the Swift
/// `String.lowercased()` method.
///
/// For example:
///
/// ```swift
/// Column("name").lowercased
/// ```
public var lowercased: SQLExpression {
DatabaseFunction.lowercase(sqlExpression)
}
/// An SQL expression that calls the Swift
/// `String.uppercased()` method.
///
/// For example:
///
/// ```swift
/// Column("name").uppercased
/// ```
public var uppercased: SQLExpression {
DatabaseFunction.uppercase(sqlExpression)
}
}
extension SQLSpecificExpressible {
/// An SQL expression that calls the Foundation
/// `String.localizedCapitalized` property.
///
/// For example:
///
/// ```swift
/// Column("name").localizedCapitalized
/// ```
public var localizedCapitalized: SQLExpression {
DatabaseFunction.localizedCapitalize(sqlExpression)
}
/// An SQL expression that calls the Foundation
/// `String.localizedLowercase` property.
///
/// For example:
///
/// ```swift
/// Column("name").localizedLowercased
/// ```
public var localizedLowercased: SQLExpression {
DatabaseFunction.localizedLowercase(sqlExpression)
}
/// An SQL expression that calls the Foundation
/// `String.localizedUppercase` property.
///
/// For example:
///
/// ```swift
/// Column("name").localizedUppercased
/// ```
public var localizedUppercased: SQLExpression {
DatabaseFunction.localizedUppercase(sqlExpression)
}
}
// MARK: - Date functions
/// A date modifier for SQLite date functions.
///
/// Related SQLite documentation: <https://www.sqlite.org/lang_datefunc.html>
public enum SQLDateModifier: SQLSpecificExpressible, Sendable {
/// Adds the specified amount of seconds
case second(Double)
/// Adds the specified amount of minutes
case minute(Int)
/// Adds the specified amount of hours
case hour(Int)
/// Adds the specified amount of days
case day(Int)
/// Adds the specified amount of months
case month(Int)
/// Adds the specified amount of years
case year(Int)
/// Shifts the date backwards to the beginning of the current day
case startOfDay
/// Shifts the date backwards to the beginning of the current month
case startOfMonth
/// Shifts the date backwards to the beginning of the current year
case startOfYear
/// See <https://www.sqlite.org/lang_datefunc.html>
case weekday(Int)
/// See <https://www.sqlite.org/lang_datefunc.html>
case unixEpoch
/// See <https://www.sqlite.org/lang_datefunc.html>
case localTime
/// See <https://www.sqlite.org/lang_datefunc.html>
case utc
public var sqlExpression: SQLExpression {
rawValue.sqlExpression
}
var rawValue: String {
switch self {
case let .day(value):
return "\(value) days"
case let .hour(value):
return "\(value) hours"
case let .minute(value):
return "\(value) minutes"
case let .second(value):
return "\(value) seconds"
case let .month(value):
return "\(value) months"
case let .year(value):
return "\(value) years"
case .startOfMonth:
return "start of month"
case .startOfYear:
return "start of year"
case .startOfDay:
return "start of day"
case let .weekday(value):
return "weekday \(value)"
case .unixEpoch:
return "unixepoch"
case .localTime:
return "localtime"
case .utc:
return "utc"
}
}
}
/// The `JULIANDAY` SQL function.
///
/// For example:
///
/// ```swift
/// // JULIANDAY(date)
/// julianDay(Column("date"))
///
/// // JULIANDAY(date, '1 days')
/// julianDay(Column("date"), .day(1))
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/lang_datefunc.html>
public func julianDay(_ value: some SQLSpecificExpressible, _ modifiers: SQLDateModifier...) -> SQLExpression {
.function("JULIANDAY", [value.sqlExpression] + modifiers.map(\.sqlExpression))
}
// MARK: DATETIME(...)
/// The `DATETIME` SQL function.
///
/// For example:
///
/// ```swift
/// // DATETIME(date)
/// dateTime(Column("date"))
///
/// // DATETIME(date, '1 days')
/// dateTime(Column("date"), .day(1))
/// ```
///
/// Related SQLite documentation:<https://www.sqlite.org/lang_datefunc.html>
public func dateTime(_ value: some SQLSpecificExpressible, _ modifiers: SQLDateModifier...) -> SQLExpression {
.function("DATETIME", [value.sqlExpression] + modifiers.map(\.sqlExpression))
}