add swiftUI code

This commit is contained in:
zeus
2025-01-22 14:09:10 +08:00
parent 68e7b7347c
commit 8a99853829
2531 changed files with 486215 additions and 0 deletions
@@ -0,0 +1,91 @@
/// A JSON column in a database table.
///
/// ## Overview
///
/// `JSONColumn` has benefits over ``Column`` for database columns that
/// contain JSON strings.
///
/// It behaves like a regular `Column`, with all extra conveniences and
/// behaviors of ``SQLJSONExpressible``.
///
/// For example, the sample code below directly accesses the "countryCode"
/// key of the "address" JSON column:
///
/// ```swift
/// struct Player: Codable {
/// var id: Int64
/// var name: String
/// var address: Address
/// }
///
/// struct Address: Codable {
/// var street: String
/// var city: String
/// var countryCode: String
/// }
///
/// extension Player: FetchableRecord, PersistableRecord {
/// enum Columns {
/// static let id = Column(CodingKeys.id)
/// static let name = Column(CodingKeys.name)
/// static let address = JSONColumn(CodingKeys.address) // JSONColumn!
/// }
/// }
///
/// try dbQueue.write { db in
/// // In a real app, table creation should happen in a migration.
/// try db.create(table: "player") { t in
/// t.autoIncrementedPrimaryKey("id")
/// t.column("name", .text).notNull()
/// t.column("address", .jsonText).notNull()
/// }
///
/// // Fetch all country codes
/// // SELECT DISTINCT address ->> 'countryCode' FROM player
/// let countryCodes: [String] = try Player
/// .select(Player.Columns.address["countryCode"], as: String.self)
/// .distinct()
/// .fetchAll(db)
/// }
/// ```
///
/// > Tip: When you can not create a `JSONColumn`, you'll get the same
/// > convenient access to JSON subcomponents
/// > with ``SQLSpecificExpressible/asJSON``.
/// >
/// > For example, the above sample can be adapted as below:
/// >
/// > ```swift
/// > extension Player: FetchableRecord, PersistableRecord {
/// > // That's another valid way to define columns.
/// > // But we don't have any JSONColumn this time.
/// > enum Columns: String, ColumnExpression {
/// > case id, name, address
/// > }
/// > }
/// >
/// > try dbQueue.write { db in
/// > // Fetch all country codes
/// > // SELECT DISTINCT address ->> 'countryCode' FROM player
/// > let countryCodes: [String] = try Player
/// > .select(Player.Columns.address.asJSON["countryCode"], as: String.self)
/// > .distinct()
/// > .fetchAll(db)
/// > }
/// > ```
public struct JSONColumn: ColumnExpression, SQLJSONExpressible, Sendable {
public var name: String
/// Creates a `JSONColumn` 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 `JSONColumn` given a `CodingKey`.
public init(_ codingKey: some CodingKey) {
self.name = codingKey.stringValue
}
}
@@ -0,0 +1,440 @@
/// A type of SQL expression that is interpreted as a JSON value.
///
/// ## Overview
///
/// JSON values that conform to `SQLJSONExpressible` have two purposes:
///
/// - They provide Swift APIs for accessing their JSON subcomponents at
/// the SQL level.
///
/// - When used in a JSON-building function such as
/// ``Database/jsonArray(_:)-8xxe3`` or ``Database/jsonObject(_:)``,
/// they are parsed and interpreted as JSON, not as plain strings.
///
/// To build a JSON value, create a ``JSONColumn``, or call the
/// ``SQLSpecificExpressible/asJSON`` property of any
/// other expression.
///
/// For example, here are some JSON values:
///
/// ```swift
/// // JSON columns:
/// JSONColumn("info")
/// Column("info").asJSON
///
/// // The JSON array [1, 2, 3]:
/// "[1, 2, 3]".databaseValue.asJSON
///
/// // A JSON value that will trigger a
/// // "malformed JSON" SQLite error when
/// // parsed by SQLite:
/// "{foo".databaseValue.asJSON
/// ```
///
/// The expressions below are not JSON values:
///
/// ```swift
/// // A plain column:
/// Column("info")
///
/// // Plain strings:
/// "[1, 2, 3]"
/// "{foo"
/// ```
///
/// ## Access JSON subcomponents
///
/// JSON values provide access to the [`->` and `->>` SQL operators](https://www.sqlite.org/json1.html)
/// and other SQLite JSON functions:
///
/// ```swift
/// let info = JSONColumn("info")
///
/// // SELECT info ->> 'firstName' FROM player
/// // 'Arthur'
/// let firstName = try Player
/// .select(info["firstName"], as: String.self)
/// .fetchOne(db)
///
/// // SELECT info ->> 'address' FROM player
/// // '{"street":"Rue de Belleville","city":"Paris"}'
/// let address = try Player
/// .select(info["address"], as: String.self)
/// .fetchOne(db)
/// ```
///
/// ## Build JSON objects and arrays from JSON values
///
/// When used in a JSON-building function such as
/// ``Database/jsonArray(_:)-8xxe3`` or ``Database/jsonObject(_:)-5iswr``,
/// JSON values are parsed and interpreted as JSON, not as plain strings.
///
/// In the example below, we can see how the `JSONColumn` is interpreted as
/// JSON, while the `Column` with the same name is interpreted as a
/// plain string:
///
/// ```swift
/// let elements: [any SQLExpressible] = [
/// JSONColumn("address"),
/// Column("address"),
/// ]
///
/// let array = Database.jsonArray(elements)
///
/// // SELECT JSON_ARRAY(JSON(address), address) FROM player
/// // '[{"country":"FR"},"{\"country\":\"FR\"}"]'
/// // <--- object ---> <------ string ------>
/// let json = try Player
/// .select(array, as: String.self)
/// .fetchOne(db)
/// ```
///
/// ## Topics
///
/// ### Accessing JSON subcomponents
///
/// - ``subscript(_:)``
/// - ``jsonExtract(atPath:)``
/// - ``jsonExtract(atPaths:)``
/// - ``jsonRepresentation(atPath:)``
///
/// ### Supporting Types
///
/// - ``AnySQLJSONExpressible``
public protocol SQLJSONExpressible: SQLSpecificExpressible { }
extension ColumnExpression where Self: SQLJSONExpressible {
/// Returns an SQL column that is interpreted as a JSON value.
public var sqlExpression: SQLExpression {
.column(name).withPreferredJSONInterpretation(.jsonValue)
}
}
// This type only grants access to `SQLJSONExpressible` apis. The fact that
// it is a JSON value is embedded in its
// `sqlExpression.preferredJSONInterpretation`.
/// A type-erased ``SQLJSONExpressible``.
public struct AnySQLJSONExpressible: SQLJSONExpressible {
/// An SQL expression that is interpreted as a JSON value.
public let sqlExpression: SQLExpression
public init(_ base: some SQLJSONExpressible) {
self.init(sqlExpression: base.sqlExpression)
}
/// - Precondition: `sqlExpression` is a JSON value
init(sqlExpression: SQLExpression) {
assert(sqlExpression.preferredJSONInterpretation == .jsonValue)
self.sqlExpression = sqlExpression
}
}
extension SQLSpecificExpressible {
/// Returns an expression that is interpreted as a JSON value.
///
/// For example:
///
/// ```swift
/// let info = Column("info").asJSON
///
/// // SELECT info ->> 'firstName' FROM player
/// // 'Arthur'
/// let firstName = try Player
/// .select(info["firstName"], as: String.self)
/// .fetchOne(db)
/// ```
///
/// For more information, see ``SQLJSONExpressible``.
public var asJSON: AnySQLJSONExpressible {
AnySQLJSONExpressible(sqlExpression: sqlExpression.withPreferredJSONInterpretation(.jsonValue))
}
}
#if GRDBCUSTOMSQLITE || GRDBCIPHER
extension SQLJSONExpressible {
/// The `->>` SQL operator.
///
/// For example:
///
/// ```swift
/// let info = JSONColumn("info")
///
/// // SELECT info ->> 'firstName' FROM player
/// // 'Arthur'
/// let firstName = try Player
/// .select(info["firstName"], as: String.self)
/// .fetchOne(db)
///
/// // SELECT info ->> 'address' FROM player
/// // '{"street":"Rue de Belleville","city":"Paris"}'
/// let address = try Player
/// .select(info["address"], as: String.self)
/// .fetchOne(db)
/// ```
///
/// Related SQL documentation: <https://www.sqlite.org/json1.html#jptr>
///
/// - parameter path: A [JSON path](https://www.sqlite.org/json1.html#path_arguments),
/// or an JSON object field label, or an array index.
public subscript(_ path: some SQLExpressible) -> SQLExpression {
.binary(.jsonExtractSQL, sqlExpression, path.sqlExpression)
}
/// The `JSON_EXTRACT` SQL function.
///
/// For example:
///
/// ```swift
/// let info = JSONColumn("info")
///
/// // SELECT JSON_EXTRACT(info, '$.firstName') FROM player
/// // 'Arthur'
/// let firstName = try Player
/// .select(info.jsonExtract(atPath: "$.firstName"), as: String.self)
/// .fetchOne(db)
///
/// // SELECT JSON_EXTRACT(info, '$.address') FROM player
/// // '{"street":"Rue de Belleville","city":"Paris"}'
/// let address = try Player
/// .select(info.jsonExtract(atPath: "$.address"), as: String.self)
/// .fetchOne(db)
/// ```
///
/// Related SQL documentation: <https://www.sqlite.org/json1.html#jex>
///
/// - parameter path: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
public func jsonExtract(atPath path: some SQLExpressible) -> SQLExpression {
Database.jsonExtract(self, atPath: path)
}
/// The `JSON_EXTRACT` SQL function.
///
/// For example:
///
/// ```swift
/// let info = JSONColumn("info")
///
/// // SELECT JSON_EXTRACT(info, '$.firstName', '$.lastName') FROM player
/// // '["Arthur","Miller"]'
/// let nameComponents = try Player
/// .select(info.jsonExtract(atPaths: ["$.firstName", "$.lastName"]), as: String.self)
/// .fetchOne(db)
/// ```
///
/// Related SQL documentation: <https://www.sqlite.org/json1.html#jex>
///
/// - parameter paths: A collection of [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
public func jsonExtract<C>(atPaths paths: C) -> SQLExpression
where C: Collection, C.Element: SQLExpressible
{
Database.jsonExtract(self, atPaths: paths)
}
/// Returns a valid JSON string with the `->` SQL operator.
///
/// For example:
///
/// ```swift
/// let info = JSONColumn("info")
///
/// // SELECT info -> 'firstName' FROM player
/// // '"Arthur"'
/// let name = try Player
/// .select(info.jsonRepresentation(atPath: "firstName"), as: String.self)
/// .fetchOne(db)
///
/// // SELECT info -> 'address' FROM player
/// // '{"street":"Rue de Belleville","city":"Paris"}'
/// let name = try Player
/// .select(info.jsonRepresentation(atPath: "address"), as: String.self)
/// .fetchOne(db)
/// ```
///
/// Related SQL documentation: <https://www.sqlite.org/json1.html#jptr>
///
/// - parameter path: A [JSON path](https://www.sqlite.org/json1.html#path_arguments),
/// or an JSON object field label, or an array index.
public func jsonRepresentation(atPath path: some SQLExpressible) -> SQLExpression {
.binary(.jsonExtractJSON, sqlExpression, path.sqlExpression)
}
}
#else
extension SQLJSONExpressible {
/// The `->>` SQL operator.
///
/// For example:
///
/// ```swift
/// let info = JSONColumn("info")
///
/// // SELECT info ->> 'firstName' FROM player
/// // 'Arthur'
/// let firstName = try Player
/// .select(info["firstName"], as: String.self)
/// .fetchOne(db)
///
/// // SELECT info ->> 'address' FROM player
/// // '{"street":"Rue de Belleville","city":"Paris"}'
/// let address = try Player
/// .select(info["address"], as: String.self)
/// .fetchOne(db)
/// ```
///
/// Related SQL documentation: <https://www.sqlite.org/json1.html#jptr>
///
/// - parameter path: A [JSON path](https://www.sqlite.org/json1.html#path_arguments),
/// or an JSON object field label, or an array index.
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
public subscript(_ path: some SQLExpressible) -> SQLExpression {
.binary(.jsonExtractSQL, sqlExpression, path.sqlExpression)
}
/// The `JSON_EXTRACT` SQL function.
///
/// For example:
///
/// ```swift
/// let info = JSONColumn("info")
///
/// // SELECT JSON_EXTRACT(info, '$.firstName') FROM player
/// // 'Arthur'
/// let firstName = try Player
/// .select(info.jsonExtract(atPath: "$.firstName"), as: String.self)
/// .fetchOne(db)
///
/// // SELECT JSON_EXTRACT(info, '$.address') FROM player
/// // '{"street":"Rue de Belleville","city":"Paris"}'
/// let address = try Player
/// .select(info.jsonExtract(atPath: "$.address"), as: String.self)
/// .fetchOne(db)
/// ```
///
/// Related SQL documentation: <https://www.sqlite.org/json1.html#jex>
///
/// - parameter path: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public func jsonExtract(atPath path: some SQLExpressible) -> SQLExpression {
Database.jsonExtract(self, atPath: path)
}
/// The `JSON_EXTRACT` SQL function.
///
/// For example:
///
/// ```swift
/// let info = JSONColumn("info")
///
/// // SELECT JSON_EXTRACT(info, '$.firstName', '$.lastName') FROM player
/// // '["Arthur","Miller"]'
/// let nameComponents = try Player
/// .select(info.jsonExtract(atPaths: ["$.firstName", "$.lastName"]), as: String.self)
/// .fetchOne(db)
/// ```
///
/// Related SQL documentation: <https://www.sqlite.org/json1.html#jex>
///
/// - parameter paths: A collection of [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public func jsonExtract<C>(atPaths paths: C) -> SQLExpression
where C: Collection, C.Element: SQLExpressible
{
Database.jsonExtract(self, atPaths: paths)
}
/// Returns a valid JSON string with the `->` SQL operator.
///
/// For example:
///
/// ```swift
/// let info = JSONColumn("info")
///
/// // SELECT info -> 'firstName' FROM player
/// // '"Arthur"'
/// let name = try Player
/// .select(info.jsonRepresentation(atPath: "firstName"), as: String.self)
/// .fetchOne(db)
///
/// // SELECT info -> 'address' FROM player
/// // '{"street":"Rue de Belleville","city":"Paris"}'
/// let name = try Player
/// .select(info.jsonRepresentation(atPath: "address"), as: String.self)
/// .fetchOne(db)
/// ```
///
/// Related SQL documentation: <https://www.sqlite.org/json1.html#jptr>
///
/// - parameter path: A [JSON path](https://www.sqlite.org/json1.html#path_arguments),
/// or an JSON object field label, or an array index.
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
public func jsonRepresentation(atPath path: some SQLExpressible) -> SQLExpression {
.binary(.jsonExtractJSON, sqlExpression, path.sqlExpression)
}
}
// TODO: Enable when those apis are ready.
// extension ColumnExpression where Self: SQLJSONExpressible {
// /// Updates a columns with the `JSON_PATCH` SQL function.
// ///
// /// For example:
// ///
// /// ```swift
// /// // UPDATE player SET address = JSON_PATCH(address, '{"country": "FR"}')
// /// try Player.updateAll(db, [
// /// JSONColumn("address").jsonPatch(#"{"country": "FR"}"#)
// /// ])
// /// ```
// ///
// /// Related SQLite documentation: <https://www.sqlite.org/json1.html#jpatch>
// @available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
// public func jsonPatch(
// with patch: some SQLExpressible)
// -> ColumnAssignment
// {
// .init(columnName: name, value: Database.jsonPatch(self, with: patch))
// }
//
// /// Updates a columns with the `JSON_REMOVE` SQL function.
// ///
// /// For example:
// ///
// /// ```swift
// /// // UPDATE player SET address = JSON_REMOVE(address, '$.country')
// /// try Player.updateAll(db, [
// /// JSONColumn("address").jsonRemove(atPath: "$.country")
// /// ])
// /// ```
// ///
// /// Related SQLite documentation: <https://www.sqlite.org/json1.html#jrm>
// ///
// /// - Parameters:
// /// - paths: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
// @available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
// public func jsonRemove(atPath path: some SQLExpressible) -> ColumnAssignment {
// .init(columnName: name, value: Database.jsonRemove(self, atPath: path))
// }
//
// /// Updates a columns with the `JSON_REMOVE` SQL function.
// ///
// /// For example:
// ///
// /// ```swift
// /// // UPDATE player SET address = JSON_REMOVE(address, '$.country', '$.city')
// /// try Player.updateAll(db, [
// /// JSONColumn("address").jsonRemove(atPatsh: ["$.country", "$.city"])
// /// ])
// /// ```
// ///
// /// Related SQLite documentation: <https://www.sqlite.org/json1.html#jrm>
// ///
// /// - Parameters:
// /// - paths: A collection of [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
// @available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
// public func jsonRemove<C>(atPaths paths: C)
// -> ColumnAssignment
// where C: Collection, C.Element: SQLExpressible
// {
// .init(columnName: name, value: Database.jsonRemove(self, atPaths: paths))
// }
//
// }
#endif
@@ -0,0 +1,866 @@
#if GRDBCUSTOMSQLITE || GRDBCIPHER
extension Database {
/// Validates and minifies a JSON string, with the `JSON` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON(' { "a": [ "test" ] } ') '{"a":["test"]}'
/// Database.json(#" { "a": [ "test" ] } "#)
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jmini>
public static func json(_ value: some SQLExpressible) -> SQLExpression {
.function("JSON", [value.sqlExpression])
}
/// Creates a JSON array with the `JSON_ARRAY` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_ARRAY(1, 2, 3, 4) '[1,2,3,4]'
/// Database.jsonArray(1...4)
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jarray>
public static func jsonArray<C>(_ values: C) -> SQLExpression
where C: Collection, C.Element: SQLExpressible
{
.function("JSON_ARRAY", values.map(\.sqlExpression.jsonBuilderExpression))
}
/// Creates a JSON array with the `JSON_ARRAY` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_ARRAY(1, 2, '3', 4) '[1,2,"3",4]'
/// Database.jsonArray([1, 2, "3", 4])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jarray>
public static func jsonArray<C>(_ values: C) -> SQLExpression
where C: Collection, C.Element == any SQLExpressible
{
.function("JSON_ARRAY", values.map(\.sqlExpression.jsonBuilderExpression))
}
/// The number of elements in a JSON array, as returned by the
/// `JSON_ARRAY_LENGTH` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_ARRAY_LENGTH('[1,2,3,4]') 4
/// Database.jsonArrayLength("[1,2,3,4]")
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jarraylen>
public static func jsonArrayLength(_ value: some SQLExpressible) -> SQLExpression {
.function("JSON_ARRAY_LENGTH", [value.sqlExpression])
}
/// The number of elements in a JSON array, as returned by the
/// `JSON_ARRAY_LENGTH` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_ARRAY_LENGTH('{"one":[1,2,3]}', '$.one') 3
/// Database.jsonArrayLength(#"{"one":[1,2,3]}"#, atPath: "$.one")
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jarraylen>
///
/// - Parameters:
/// - value: A JSON array.
/// - path: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
public static func jsonArrayLength(
_ value: some SQLExpressible,
atPath path: some SQLExpressible)
-> SQLExpression
{
.function("JSON_ARRAY_LENGTH", [value.sqlExpression, path.sqlExpression])
}
/// The `JSON_ERROR_POSITION` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_ERROR_POSITION(info)
/// Database.jsonErrorPosition(Column("info"))
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jerr>
public static func jsonErrorPosition(_ value: some SQLExpressible) -> SQLExpression {
.function("JSON_ERROR_POSITION", [value.sqlExpression])
}
/// The `JSON_EXTRACT` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_EXTRACT('{"a":123}', '$.a') 123
/// Database.jsonExtract(#"{"a":123}"#, atPath: "$.a")
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jex>
///
/// - Parameters:
/// - value: A JSON value.
/// - path: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
public static func jsonExtract(_ value: some SQLExpressible, atPath path: some SQLExpressible) -> SQLExpression {
.function("JSON_EXTRACT", [value.sqlExpression, path.sqlExpression])
}
/// The `JSON_EXTRACT` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_EXTRACT('{"a":2,"c":[4,5]}','$.c','$.a') '[[4,5],2]'
/// Database.jsonExtract(#"{"a":2,"c":[4,5]}"#, atPaths: ["$.c", "$.a"])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jex>
///
/// - Parameters:
/// - value: A JSON value.
/// - paths: A collection of [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
public static func jsonExtract<C>(_ value: some SQLExpressible, atPaths paths: C)
-> SQLExpression
where C: Collection, C.Element: SQLExpressible
{
.function("JSON_EXTRACT", [value.sqlExpression] + paths.map(\.sqlExpression))
}
/// The `JSON_INSERT` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_INSERT('[1,2,3,4]','$[#]',99) '[1,2,3,4,99]'
/// Database.jsonInsert("[1,2,3,4]", ["$[#]": value: 99])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jins>
///
/// - Parameters:
/// - value: A JSON value.
/// - assignments: A collection of key/value pairs, where keys are
/// [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
public static func jsonInsert<C>(
_ value: some SQLExpressible,
_ assignments: C)
-> SQLExpression
where C: Collection,
C.Element == (key: String, value: any SQLExpressible)
{
.function("JSON_INSERT", [value.sqlExpression] + assignments.flatMap {
[$0.key.sqlExpression, $0.value.sqlExpression.jsonBuilderExpression]
})
}
/// The `JSON_REPLACE` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_REPLACE('{"a":2,"c":4}', '$.a', 99) '{"a":99,"c":4}'
/// Database.jsonReplace(#"{"a":2,"c":4}"#, ["$.a": 99])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jins>
///
/// - Parameters:
/// - value: A JSON value.
/// - assignments: A collection of key/value pairs, where keys are
/// [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
public static func jsonReplace<C>(
_ value: some SQLExpressible,
_ assignments: C)
-> SQLExpression
where C: Collection,
C.Element == (key: String, value: any SQLExpressible)
{
.function("JSON_REPLACE", [value.sqlExpression] + assignments.flatMap {
[$0.key.sqlExpression, $0.value.sqlExpression.jsonBuilderExpression]
})
}
/// The `JSON_SET` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_SET('{"a":2,"c":4}', '$.a', 99) '{"a":99,"c":4}'
/// Database.jsonSet(#"{"a":2,"c":4}"#, ["$.a": 99]])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jins>
///
/// - Parameters:
/// - value: A JSON value.
/// - assignments: A collection of key/value pairs, where keys are
/// [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
public static func jsonSet<C>(
_ value: some SQLExpressible,
_ assignments: C)
-> SQLExpression
where C: Collection,
C.Element == (key: String, value: any SQLExpressible)
{
.function("JSON_SET", [value.sqlExpression] + assignments.flatMap {
[$0.key.sqlExpression, $0.value.sqlExpression.jsonBuilderExpression]
})
}
/// Creates a JSON object with the `JSON_OBJECT` SQL function. Pass
/// key/value pairs with a Swift collection such as a `Dictionary`.
///
/// For example:
///
/// ```swift
/// // JSON_OBJECT('c', '{"e":5}') '{"c":"{\"e\":5}"}'
/// Database.jsonObject([
/// "c": #"{"e":5}"#,
/// ])
///
/// // JSON_OBJECT('c', JSON_OBJECT('e', 5)) '{"c":{"e":5}}'
/// Database.jsonObject([
/// "c": Database.jsonObject(["e": 5])),
/// ])
///
/// // JSON_OBJECT('c', JSON('{"e":5}')) '{"c":{"e":5}}'
/// Database.jsonObject([
/// "c": Database.json(#"{"e":5}"#),
/// ])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jobj>
public static func jsonObject<C>(_ elements: C)
-> SQLExpression
where C: Collection,
C.Element == (key: String, value: any SQLExpressible)
{
.function("JSON_OBJECT", elements.flatMap {
[$0.key.sqlExpression, $0.value.sqlExpression.jsonBuilderExpression]
})
}
/// The `JSON_PATCH` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_PATCH('{"a":1,"b":2}','{"c":3,"d":4}') '{"a":1,"b":2,"c":3,"d":4}'
/// Database.jsonPatch(#"{"a":1,"b":2}"#, #"{"c":3,"d":4}"#)
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jpatch>
public static func jsonPatch(
_ value: some SQLExpressible,
with patch: some SQLExpressible)
-> SQLExpression
{
.function("JSON_PATCH", [value.sqlExpression, patch.sqlExpression])
}
/// The `JSON_REMOVE` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_REMOVE('[0,1,2,3,4]', '$[2]') '[0,1,3,4]'
/// Database.jsonRemove("[0,1,2,3,4]", atPath: "$[2]")
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jrm>
///
/// - Parameters:
/// - value: A JSON value.
/// - paths: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
public static func jsonRemove(_ value: some SQLExpressible, atPath path: some SQLExpressible) -> SQLExpression {
.function("JSON_REMOVE", [value.sqlExpression, path.sqlExpression])
}
/// The `JSON_REMOVE` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_REMOVE('[0,1,2,3,4]', '$[2]','$[0]') '[1,3,4]'
/// Database.jsonRemove("[0,1,2,3,4]", atPaths: ["$[2]", "$[0]"])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jrm>
///
/// - Parameters:
/// - value: A JSON value.
/// - paths: A collection of [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
public static func jsonRemove<C>(_ value: some SQLExpressible, atPaths paths: C)
-> SQLExpression
where C: Collection, C.Element: SQLExpressible
{
.function("JSON_REMOVE", [value.sqlExpression] + paths.map(\.sqlExpression))
}
/// The `JSON_TYPE` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_TYPE('{"a":[2,3.5,true,false,null,"x"]}') 'object'
/// Database.jsonType(#"{"a":[2,3.5,true,false,null,"x"]}"#)
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jtype>
public static func jsonType(_ value: some SQLExpressible) -> SQLExpression {
.function("JSON_TYPE", [value.sqlExpression])
}
/// The `JSON_TYPE` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_TYPE('{"a":[2,3.5,true,false,null,"x"]}', '$.a') 'object'
/// Database.jsonType(#"{"a":[2,3.5,true,false,null,"x"]}"#, atPath: "$.a")
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jtype>
///
/// - Parameters:
/// - value: A JSON value.
/// - paths: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
public static func jsonType(_ value: some SQLExpressible, atPath path: some SQLExpressible) -> SQLExpression {
.function("JSON_TYPE", [value.sqlExpression, path.sqlExpression])
}
/// The `JSON_VALID` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_VALID('{"x":35') 0
/// Database.jsonIsValid(#"{"x":35"#)
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jvalid>
public static func jsonIsValid(_ value: some SQLExpressible) -> SQLExpression {
.function("JSON_VALID", [value.sqlExpression])
}
/// The `JSON_QUOTE` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_QUOTE('[1]') '"[1]"'
/// Database.jsonQuote("[1]")
///
/// // JSON_QUOTE(JSON('[1]')) '[1]'
/// Database.jsonQuote(Database.json("[1]"))
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jquote>
public static func jsonQuote(_ value: some SQLExpressible) -> SQLExpression {
.function("JSON_QUOTE", [value.sqlExpression.jsonBuilderExpression])
}
/// The `JSON_GROUP_ARRAY` SQL function.
///
/// For example:
///
/// ```swift
/// // SELECT JSON_GROUP_ARRAY(name) FROM player
/// Player.select(Database.jsonGroupArray(Column("name")))
///
/// // SELECT JSON_GROUP_ARRAY(name) FILTER (WHERE score > 0) FROM player
/// Player.select(Database.jsonGroupArray(Column("name"), filter: Column("score") > 0))
///
/// // SELECT JSON_GROUP_ARRAY(name ORDER BY name) FROM player
/// Player.select(Database.jsonGroupArray(Column("name"), orderBy: Column("name")))
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jgrouparray>
public static func jsonGroupArray(
_ value: some SQLExpressible,
orderBy ordering: (any SQLOrderingTerm)? = nil,
filter: (any SQLSpecificExpressible)? = nil)
-> SQLExpression {
.aggregateFunction(
"JSON_GROUP_ARRAY",
[value.sqlExpression.jsonBuilderExpression],
ordering: ordering?.sqlOrdering,
filter: filter?.sqlExpression,
isJSONValue: true)
}
/// The `JSON_GROUP_OBJECT` SQL function.
///
/// For example:
///
/// ```swift
/// // SELECT JSON_GROUP_OBJECT(name, score) FROM player
/// Player.select(Database.jsonGroupObject(
/// key: Column("name"),
/// value: Column("score")))
///
/// // SELECT JSON_GROUP_OBJECT(name, score) FILTER (WHERE score > 0) FROM player
/// Player.select(Database.jsonGroupObject(
/// key: Column("name"),
/// value: Column("score"),
/// filter: Column("score") > 0))
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jgrouparray>
public static func jsonGroupObject(
key: some SQLExpressible,
value: some SQLExpressible,
filter: (any SQLSpecificExpressible)? = nil
) -> SQLExpression {
.aggregateFunction(
"JSON_GROUP_OBJECT",
[key.sqlExpression, value.sqlExpression.jsonBuilderExpression],
filter: filter?.sqlExpression,
isJSONValue: true)
}
}
#else
extension Database {
/// Validates and minifies a JSON string, with the `JSON` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON(' { "a": [ "test" ] } ') '{"a":["test"]}'
/// Database.json(#" { "a": [ "test" ] } "#)
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jmini>
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func json(_ value: some SQLExpressible) -> SQLExpression {
.function("JSON", [value.sqlExpression])
}
/// Creates a JSON array with the `JSON_ARRAY` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_ARRAY(1, 2, 3, 4) '[1,2,3,4]'
/// Database.jsonArray(1...4)
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jarray>
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonArray<C>(_ values: C) -> SQLExpression
where C: Collection, C.Element: SQLExpressible
{
.function("JSON_ARRAY", values.map(\.sqlExpression.jsonBuilderExpression))
}
/// Creates a JSON array with the `JSON_ARRAY` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_ARRAY(1, 2, '3', 4) '[1,2,"3",4]'
/// Database.jsonArray([1, 2, "3", 4])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jarray>
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonArray<C>(_ values: C) -> SQLExpression
where C: Collection, C.Element == any SQLExpressible
{
.function("JSON_ARRAY", values.map(\.sqlExpression.jsonBuilderExpression))
}
/// The number of elements in a JSON array, as returned by the
/// `JSON_ARRAY_LENGTH` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_ARRAY_LENGTH('[1,2,3,4]') 4
/// Database.jsonArrayLength("[1,2,3,4]")
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jarraylen>
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonArrayLength(_ value: some SQLExpressible) -> SQLExpression {
.function("JSON_ARRAY_LENGTH", [value.sqlExpression])
}
/// The number of elements in a JSON array, as returned by the
/// `JSON_ARRAY_LENGTH` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_ARRAY_LENGTH('{"one":[1,2,3]}', '$.one') 3
/// Database.jsonArrayLength(#"{"one":[1,2,3]}"#, atPath: "$.one")
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jarraylen>
///
/// - Parameters:
/// - value: A JSON array.
/// - path: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonArrayLength(
_ value: some SQLExpressible,
atPath path: some SQLExpressible)
-> SQLExpression
{
.function("JSON_ARRAY_LENGTH", [value.sqlExpression, path.sqlExpression])
}
/// The `JSON_EXTRACT` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_EXTRACT('{"a":123}', '$.a') 123
/// Database.jsonExtract(#"{"a":123}"#, atPath: "$.a")
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jex>
///
/// - Parameters:
/// - value: A JSON value.
/// - path: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonExtract(_ value: some SQLExpressible, atPath path: some SQLExpressible) -> SQLExpression {
.function("JSON_EXTRACT", [value.sqlExpression, path.sqlExpression])
}
/// The `JSON_EXTRACT` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_EXTRACT('{"a":2,"c":[4,5]}','$.c','$.a') '[[4,5],2]'
/// Database.jsonExtract(#"{"a":2,"c":[4,5]}"#, atPaths: ["$.c", "$.a"])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jex>
///
/// - Parameters:
/// - value: A JSON value.
/// - paths: A collection of [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonExtract<C>(_ value: some SQLExpressible, atPaths paths: C)
-> SQLExpression
where C: Collection, C.Element: SQLExpressible
{
.function("JSON_EXTRACT", [value.sqlExpression] + paths.map(\.sqlExpression))
}
/// The `JSON_INSERT` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_INSERT('[1,2,3,4]','$[#]',99) '[1,2,3,4,99]'
/// Database.jsonInsert("[1,2,3,4]", ["$[#]": value: 99])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jins>
///
/// - Parameters:
/// - value: A JSON value.
/// - assignments: A collection of key/value pairs, where keys are
/// [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonInsert<C>(
_ value: some SQLExpressible,
_ assignments: C)
-> SQLExpression
where C: Collection,
C.Element == (key: String, value: any SQLExpressible)
{
.function("JSON_INSERT", [value.sqlExpression] + assignments.flatMap {
[$0.key.sqlExpression, $0.value.sqlExpression.jsonBuilderExpression]
})
}
/// The `JSON_REPLACE` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_REPLACE('{"a":2,"c":4}', '$.a', 99) '{"a":99,"c":4}'
/// Database.jsonReplace(#"{"a":2,"c":4}"#, ["$.a": 99])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jins>
///
/// - Parameters:
/// - value: A JSON value.
/// - assignments: A collection of key/value pairs, where keys are
/// [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonReplace<C>(
_ value: some SQLExpressible,
_ assignments: C)
-> SQLExpression
where C: Collection,
C.Element == (key: String, value: any SQLExpressible)
{
.function("JSON_REPLACE", [value.sqlExpression] + assignments.flatMap {
[$0.key.sqlExpression, $0.value.sqlExpression.jsonBuilderExpression]
})
}
/// The `JSON_SET` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_SET('{"a":2,"c":4}', '$.a', 99) '{"a":99,"c":4}'
/// Database.jsonSet(#"{"a":2,"c":4}"#, ["$.a": 99]])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jins>
///
/// - Parameters:
/// - value: A JSON value.
/// - assignments: A collection of key/value pairs, where keys are
/// [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonSet<C>(
_ value: some SQLExpressible,
_ assignments: C)
-> SQLExpression
where C: Collection,
C.Element == (key: String, value: any SQLExpressible)
{
.function("JSON_SET", [value.sqlExpression] + assignments.flatMap {
[$0.key.sqlExpression, $0.value.sqlExpression.jsonBuilderExpression]
})
}
/// Creates a JSON object with the `JSON_OBJECT` SQL function. Pass
/// key/value pairs with a Swift collection such as a `Dictionary`.
///
/// For example:
///
/// ```swift
/// // JSON_OBJECT('c', '{"e":5}') '{"c":"{\"e\":5}"}'
/// Database.jsonObject([
/// "c": #"{"e":5}"#,
/// ])
///
/// // JSON_OBJECT('c', JSON_OBJECT('e', 5)) '{"c":{"e":5}}'
/// Database.jsonObject([
/// "c": Database.jsonObject(["e": 5])),
/// ])
///
/// // JSON_OBJECT('c', JSON('{"e":5}')) '{"c":{"e":5}}'
/// Database.jsonObject([
/// "c": Database.json(#"{"e":5}"#),
/// ])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jobj>
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonObject<C>(_ elements: C)
-> SQLExpression
where C: Collection,
C.Element == (key: String, value: any SQLExpressible)
{
.function("JSON_OBJECT", elements.flatMap {
[$0.key.sqlExpression, $0.value.sqlExpression.jsonBuilderExpression]
})
}
/// The `JSON_PATCH` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_PATCH('{"a":1,"b":2}','{"c":3,"d":4}') '{"a":1,"b":2,"c":3,"d":4}'
/// Database.jsonPatch(#"{"a":1,"b":2}"#, #"{"c":3,"d":4}"#)
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jpatch>
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonPatch(
_ value: some SQLExpressible,
with patch: some SQLExpressible)
-> SQLExpression
{
.function("JSON_PATCH", [value.sqlExpression, patch.sqlExpression])
}
/// The `JSON_REMOVE` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_REMOVE('[0,1,2,3,4]', '$[2]') '[0,1,3,4]'
/// Database.jsonRemove("[0,1,2,3,4]", atPath: "$[2]")
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jrm>
///
/// - Parameters:
/// - value: A JSON value.
/// - paths: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonRemove(_ value: some SQLExpressible, atPath path: some SQLExpressible) -> SQLExpression {
.function("JSON_REMOVE", [value.sqlExpression, path.sqlExpression])
}
/// The `JSON_REMOVE` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_REMOVE('[0,1,2,3,4]', '$[2]','$[0]') '[1,3,4]'
/// Database.jsonRemove("[0,1,2,3,4]", atPaths: ["$[2]", "$[0]"])
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jrm>
///
/// - Parameters:
/// - value: A JSON value.
/// - paths: A collection of [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonRemove<C>(_ value: some SQLExpressible, atPaths paths: C)
-> SQLExpression
where C: Collection, C.Element: SQLExpressible
{
.function("JSON_REMOVE", [value.sqlExpression] + paths.map(\.sqlExpression))
}
/// The `JSON_TYPE` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_TYPE('{"a":[2,3.5,true,false,null,"x"]}') 'object'
/// Database.jsonType(#"{"a":[2,3.5,true,false,null,"x"]}"#)
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jtype>
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonType(_ value: some SQLExpressible) -> SQLExpression {
.function("JSON_TYPE", [value.sqlExpression])
}
/// The `JSON_TYPE` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_TYPE('{"a":[2,3.5,true,false,null,"x"]}', '$.a') 'object'
/// Database.jsonType(#"{"a":[2,3.5,true,false,null,"x"]}"#, atPath: "$.a")
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jtype>
///
/// - Parameters:
/// - value: A JSON value.
/// - paths: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonType(_ value: some SQLExpressible, atPath path: some SQLExpressible) -> SQLExpression {
.function("JSON_TYPE", [value.sqlExpression, path.sqlExpression])
}
/// The `JSON_VALID` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_VALID('{"x":35') 0
/// Database.jsonIsValid(#"{"x":35"#)
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jvalid>
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonIsValid(_ value: some SQLExpressible) -> SQLExpression {
.function("JSON_VALID", [value.sqlExpression])
}
/// Returns a valid JSON string with the `JSON_QUOTE` SQL function.
///
/// For example:
///
/// ```swift
/// // JSON_QUOTE('[1]') '"[1]"'
/// Database.jsonQuote("[1]")
///
/// // JSON_QUOTE(JSON('[1]')) '[1]'
/// Database.jsonQuote(Database.json("[1]"))
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jquote>
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonQuote(_ value: some SQLExpressible) -> SQLExpression {
.function("JSON_QUOTE", [value.sqlExpression.jsonBuilderExpression])
}
/// The `JSON_GROUP_ARRAY` SQL function.
///
/// For example:
///
/// ```swift
/// // SELECT JSON_GROUP_ARRAY(name) FROM player
/// Player.select(Database.jsonGroupArray(Column("name")))
///
/// // SELECT JSON_GROUP_ARRAY(name) FILTER (WHERE score > 0) FROM player
/// Player.select(Database.jsonGroupArray(Column("name"), filter: Column("score") > 0))
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jgrouparray>
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonGroupArray(
_ value: some SQLExpressible,
filter: (any SQLSpecificExpressible)? = nil)
-> SQLExpression {
.aggregateFunction(
"JSON_GROUP_ARRAY",
[value.sqlExpression.jsonBuilderExpression],
filter: filter?.sqlExpression,
isJSONValue: true)
}
/// The `JSON_GROUP_OBJECT` SQL function.
///
/// For example:
///
/// ```swift
/// // SELECT JSON_GROUP_OBJECT(name, score) FROM player
/// Player.select(Database.jsonGroupObject(
/// key: Column("name"),
/// value: Column("score")))
///
/// // SELECT JSON_GROUP_OBJECT(name, score) FILTER (WHERE score > 0) FROM player
/// Player.select(Database.jsonGroupObject(
/// key: Column("name"),
/// value: Column("score"),
/// filter: Column("score") > 0))
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jgrouparray>
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonGroupObject(
key: some SQLExpressible,
value: some SQLExpressible,
filter: (any SQLSpecificExpressible)? = nil
) -> SQLExpression {
.aggregateFunction(
"JSON_GROUP_OBJECT",
[key.sqlExpression, value.sqlExpression.jsonBuilderExpression],
filter: filter?.sqlExpression,
isJSONValue: true)
}
}
#endif