Skip to content

Commit

Permalink
All JSON functions are available on macOS 10.15, but -> and ->>
Browse files Browse the repository at this point in the history
  • Loading branch information
groue committed Oct 15, 2023
1 parent 85792b8 commit a7e9060
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 74 deletions.
10 changes: 5 additions & 5 deletions GRDB/JSON/SQLJSONExpressible.swift
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ extension SQLJSONExpressible {
/// 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 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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)
}
Expand All @@ -334,7 +334,7 @@ extension SQLJSONExpressible {
/// 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 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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
{
Expand Down Expand Up @@ -385,7 +385,7 @@ extension SQLJSONExpressible {
// /// ```
// ///
// /// Related SQLite documentation: <https://www.sqlite.org/json1.html#jpatch>
// @available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
// @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
Expand All @@ -408,7 +408,7 @@ extension SQLJSONExpressible {
// ///
// /// - Parameters:
// /// - paths: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
// @available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
// @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))
// }
Expand All @@ -428,7 +428,7 @@ extension SQLJSONExpressible {
// ///
// /// - Parameters:
// /// - paths: A collection of [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
// @available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
// @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
Expand Down
40 changes: 20 additions & 20 deletions GRDB/JSON/SQLJSONFunctions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ extension Database {
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jmini>
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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])
}
Expand All @@ -412,7 +412,7 @@ extension Database {
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jarray>
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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
{
Expand All @@ -429,7 +429,7 @@ extension Database {
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jarray>
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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
{
Expand All @@ -447,7 +447,7 @@ extension Database {
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jarraylen>
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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])
}
Expand All @@ -467,7 +467,7 @@ extension Database {
/// - Parameters:
/// - value: A JSON array.
/// - path: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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)
Expand Down Expand Up @@ -505,7 +505,7 @@ extension Database {
/// - Parameters:
/// - value: A JSON value.
/// - path: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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])
}
Expand All @@ -524,7 +524,7 @@ extension Database {
/// - Parameters:
/// - value: A JSON value.
/// - paths: A collection of [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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
Expand All @@ -547,7 +547,7 @@ extension Database {
/// - 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 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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)
Expand Down Expand Up @@ -575,7 +575,7 @@ extension Database {
/// - 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 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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)
Expand Down Expand Up @@ -603,7 +603,7 @@ extension Database {
/// - 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 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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)
Expand Down Expand Up @@ -639,7 +639,7 @@ extension Database {
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jobj>
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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,
Expand All @@ -660,7 +660,7 @@ extension Database {
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jpatch>
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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)
Expand All @@ -683,7 +683,7 @@ extension Database {
/// - Parameters:
/// - value: A JSON value.
/// - paths: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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])
}
Expand All @@ -702,7 +702,7 @@ extension Database {
/// - Parameters:
/// - value: A JSON value.
/// - paths: A collection of [JSON paths](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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
Expand All @@ -720,7 +720,7 @@ extension Database {
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jtype>
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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])
}
Expand All @@ -739,7 +739,7 @@ extension Database {
/// - Parameters:
/// - value: A JSON value.
/// - paths: A [JSON path](https://www.sqlite.org/json1.html#path_arguments).
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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])
}
Expand All @@ -754,7 +754,7 @@ extension Database {
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jvalid>
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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])
}
Expand All @@ -772,23 +772,23 @@ extension Database {
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jquote>
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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.
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jgrouparray>
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
public static func jsonGroupArray(_ value: some SQLExpressible) -> SQLExpression {
.function("JSON_GROUP_ARRAY", [value.sqlExpression.jsonBuilderExpression])
}

/// The `JSON_GROUP_OBJECT` SQL function.
///
/// Related SQLite documentation: <https://www.sqlite.org/json1.html#jgrouparray>
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@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) -> SQLExpression {
.function("JSON_GROUP_OBJECT", [key.sqlExpression, value.sqlExpression.jsonBuilderExpression])
}
Expand Down
14 changes: 11 additions & 3 deletions GRDB/QueryInterface/SQL/SQLExpression.swift
Original file line number Diff line number Diff line change
Expand Up @@ -492,13 +492,21 @@ public struct SQLExpression {
/// The `>>` bitwise right shift operator
static let rightShift = BinaryOperator(">>")

// Not guarded by availability checks, but only available for SQLite 3.38+
#if GRDBCUSTOMSQLITE || GRDBCIPHER
/// The `->` SQL operator
static let jsonExtractJSON = BinaryOperator("->", isJSONValue: true)

/// The `->>` SQL operator
static let jsonExtractSQL = BinaryOperator("->>")
#else
/// The `->` SQL operator
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
static let jsonExtractJSON = BinaryOperator("->", isJSONValue: true)

// Not guarded by availability checks, but only available for SQLite 3.38+
/// The `->>` SQL operator
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
static let jsonExtractSQL = BinaryOperator("->>")
#endif
}

/// `EscapableBinaryOperator` is an SQLite binary operator that accepts an
Expand Down Expand Up @@ -1977,7 +1985,7 @@ extension SQLExpression {
}
}
#else
@available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) // SQLite 3.38+
@available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) // SQLite 3.38+ with exceptions for macOS
/// Returns an expression suitable in JSON building contexts.
var jsonBuilderExpression: SQLExpression {
switch preferredJSONInterpretation {
Expand Down
48 changes: 36 additions & 12 deletions Tests/GRDBTests/JSONColumnTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ final class JSONColumnTests: GRDBTestCase {
throw XCTSkip("JSON support is not available")
}
#else
guard #available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) else {
guard #available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) else {
throw XCTSkip("JSON support is not available")
}
#endif
Expand Down Expand Up @@ -44,15 +44,47 @@ final class JSONColumnTests: GRDBTestCase {
}
}

func test_extraction() throws {
func test_JSON_EXTRACT() throws {
#if GRDBCUSTOMSQLITE || GRDBCIPHER
// Prevent SQLCipher failures
guard sqlite3_libversion_number() >= 3038000 else {
throw XCTSkip("JSON support is not available")
throw XCTSkip("JSON_EXTRACT is not available")
}
#else
guard #available(iOS 16, macOS 10.15, tvOS 17, watchOS 9, *) else {
throw XCTSkip("JSON_EXTRACT is not available")
}
#endif

let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
try db.create(table: "player") { t in
t.autoIncrementedPrimaryKey("id")
t.column("info", .jsonText)
}

let player = Table("player")
let info = JSONColumn("info")

try assertEqualSQL(db, player.select(info.jsonExtract(atPath: "$.score")), """
SELECT JSON_EXTRACT("info", '$.score') FROM "player"
""")

try assertEqualSQL(db, player.select(info.jsonExtract(atPaths: ["$.score", "$.bonus"])), """
SELECT JSON_EXTRACT("info", '$.score', '$.bonus') FROM "player"
""")
}
}

func test_extraction_operators() throws {
#if GRDBCUSTOMSQLITE || GRDBCIPHER
// Prevent SQLCipher failures
guard sqlite3_libversion_number() >= 3038000 else {
throw XCTSkip("JSON operators are not available")
}
#else
guard #available(iOS 16, macOS 13.2, tvOS 17, watchOS 9, *) else {
throw XCTSkip("JSON support is not available")
throw XCTSkip("JSON operators are not available")
}
#endif

Expand All @@ -74,14 +106,6 @@ final class JSONColumnTests: GRDBTestCase {
SELECT "info" ->> '$.score' FROM "player"
""")

try assertEqualSQL(db, player.select(info.jsonExtract(atPath: "$.score")), """
SELECT JSON_EXTRACT("info", '$.score') FROM "player"
""")

try assertEqualSQL(db, player.select(info.jsonExtract(atPaths: ["$.score", "$.bonus"])), """
SELECT JSON_EXTRACT("info", '$.score', '$.bonus') FROM "player"
""")

try assertEqualSQL(db, player.select(info.jsonRepresentation(atPath: "score")), """
SELECT "info" -> 'score' FROM "player"
""")
Expand Down
Loading

0 comments on commit a7e9060

Please sign in to comment.