Skip to content

Commit bccf312

Browse files
committed
Merge branch 'development'
2 parents dc03b8a + 5b4a86b commit bccf312

18 files changed

+238
-12
lines changed

CHANGELOG.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:
77

88
#### 7.x Releases
99

10-
- `7.0.0` Betas - [7.0.0-beta](#700-beta) - [7.0.0-beta.2](#700-beta2)
10+
- `7.0.0` Betas - [7.0.0-beta](#700-beta) - [7.0.0-beta.2](#700-beta2) - [7.0.0-beta.3](#700-beta3)
1111

1212
#### 6.x Releases
1313

@@ -131,11 +131,19 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:
131131

132132
---
133133

134+
## 7.0.0-beta.3
135+
136+
Released October 6, 2024
137+
138+
- **Fix**: use #if directives to conditionally @preconcurrency import the Dispatch module to enable building the package on linux by [@tayloraswift](https://github.com/tayloraswift) in [#1644](https://github.com/groue/GRDB.swift/pull/1644)
139+
- **New**: Add coalesce free function and Row method by [@philmitchell](https://github.com/philmitchell) in [#1645](https://github.com/groue/GRDB.swift/pull/1645)
140+
- **Documentation Update**: Add `DatabaseValueConvertible` tip for JSON columns by [@bok-](https://github.com/bok-) in [#1649](https://github.com/groue/GRDB.swift/pull/1649)
141+
134142
## 7.0.0-beta.2
135143

136144
Released September 29, 2024
137145

138-
- **Fix** Update .spi.yml by [@finestructure](https://github.com/finestructure) in [#1643](https://github.com/groue/GRDB.swift/pull/1643)
146+
- **Fix**: Update .spi.yml by [@finestructure](https://github.com/finestructure) in [#1643](https://github.com/groue/GRDB.swift/pull/1643)
139147

140148
## 7.0.0-beta
141149

@@ -145,7 +153,7 @@ Released September 29, 2024
145153

146154
[Migrating From GRDB 6 to GRDB 7](Documentation/GRDB7MigrationGuide.md) describes in detail how to bump the GRDB version in your application.
147155

148-
The new [Swift Concurrency and GRDB](https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.2/documentation/grdb/swiftconcurrency) guide explains how to best integrate GRDB and Swift Concurrency.
156+
The new [Swift Concurrency and GRDB](https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.3/documentation/grdb/swiftconcurrency) guide explains how to best integrate GRDB and Swift Concurrency.
149157

150158
The [demo app](Documentation/DemoApps/) was rewritten from scratch in a brand new Xcode 16 project.
151159

Documentation/GRDB7MigrationGuide.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ Do not miss [Swift Concurrency and GRDB], for more recommendations regarding non
228228
- The async sequence returned by [`ValueObservation.values`](https://swiftpackageindex.com/groue/grdb.swiftdocumentation/grdb/valueobservation/values(in:scheduling:bufferingpolicy:)) now iterates on the cooperative thread pool by default. Use .mainActor as the scheduler if you need the previous behavior.
229229

230230
[Migrating to Swift 6]: https://www.swift.org/migration/documentation/migrationguide
231-
[Sharing a Database]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.2/documentation/grdb/databasesharing
232-
[Transaction Kinds]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.2/documentation/grdb/transactions#Transaction-Kinds
233-
[Swift Concurrency and GRDB]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.2/documentation/grdb/swiftconcurrency
234-
[Record]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.2/documentation/grdb/record
231+
[Sharing a Database]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.3/documentation/grdb/databasesharing
232+
[Transaction Kinds]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.3/documentation/grdb/transactions#Transaction-Kinds
233+
[Swift Concurrency and GRDB]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.3/documentation/grdb/swiftconcurrency
234+
[Record]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.3/documentation/grdb/record

GRDB.swift.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'GRDB.swift'
3-
s.version = '7.0.0-beta.2'
3+
s.version = '7.0.0-beta.3'
44

55
s.license = { :type => 'MIT', :file => 'LICENSE' }
66
s.summary = 'A toolkit for SQLite databases, with a focus on application development.'

GRDB/Core/Configuration.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import SQLCipher
77
import SQLite3
88
#endif
99

10+
#if !canImport(Darwin)
11+
@preconcurrency
12+
#endif
1013
import Dispatch
1114
import Foundation
1215

GRDB/Core/Row.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ import Foundation
6060
/// - ``subscript(_:)-3tp8o``
6161
/// - ``subscript(_:)-4k8od``
6262
/// - ``subscript(_:)-9rbo7``
63+
/// - ``coalesce(_:)-359k7``
64+
/// - ``coalesce(_:)-6nbah``
6365
/// - ``withUnsafeData(named:_:)``
6466
/// - ``dataNoCopy(named:)``
6567
///
@@ -671,6 +673,53 @@ extension Row {
671673
public func dataNoCopy(_ column: some ColumnExpression) -> Data? {
672674
dataNoCopy(named: column.name)
673675
}
676+
677+
/// Returns the first non-null value, if any. Identical to SQL `COALESCE` function.
678+
///
679+
/// For example:
680+
///
681+
/// ```swift
682+
/// let name: String? = row.coalesce(["nickname", "name"])
683+
/// ```
684+
///
685+
/// Prefer `coalesce` to nil-coalescing row values, which does not
686+
/// return the expected value:
687+
///
688+
/// ```swift
689+
/// // INCORRECT
690+
/// let name: String? = row["nickname"] ?? row["name"]
691+
/// ```
692+
public func coalesce<T: DatabaseValueConvertible>(
693+
_ columns: some Collection<String>
694+
) -> T? {
695+
for column in columns {
696+
if let value = self[column] as T? {
697+
return value
698+
}
699+
}
700+
return nil
701+
}
702+
703+
/// Returns the first non-null value, if any. Identical to SQL `COALESCE` function.
704+
///
705+
/// For example:
706+
///
707+
/// ```swift
708+
/// let name: String? = row.coalesce([Column("nickname"), Column("name")])
709+
/// ```
710+
///
711+
/// Prefer `coalesce` to nil-coalescing row values, which does not
712+
/// return the expected value:
713+
///
714+
/// ```swift
715+
/// // INCORRECT
716+
/// let name: String? = row[Column("nickname")] ?? row[Column("name")]
717+
/// ```
718+
public func coalesce<T: DatabaseValueConvertible>(
719+
_ columns: some Collection<any ColumnExpression>
720+
) -> T? {
721+
return coalesce(columns.lazy.map { $0.name })
722+
}
674723
}
675724

676725
extension Row {

GRDB/Core/SchedulingWatchdog.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#if !canImport(Darwin)
2+
@preconcurrency
3+
#endif
14
import Dispatch
25

36
/// SchedulingWatchdog makes sure that databases connections are used on correct

GRDB/Documentation.docc/JSON.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,21 @@ extension Team: FetchableRecord, PersistableRecord {
9898
}
9999
```
100100
101+
> Tip: Conform your `Codable` property to `DatabaseValueConvertible` if you want to be able to filter on specific values of it:
102+
>
103+
> ```swift
104+
> struct Address: Codable { ... }
105+
> extension Address: DatabaseValueConvertible {}
106+
>
107+
> // SELECT * FROM player
108+
> // WHERE address = '{"street": "...", "city": "...", "country": "..."}'
109+
> let players = try Player
110+
> .filter(JSONColumn("address") == Address(...))
111+
> .fetchAll(db)
112+
> ```
113+
>
114+
> Take care that SQLite will compare strings, not JSON objects: white-space and key ordering matter. For this comparison to succeed, make sure that the database contains values that are formatted exactly like a serialized `Address`.
115+
101116
## Manipulate JSON values at the database level
102117
103118
[SQLite JSON functions and operators](https://www.sqlite.org/json1.html) are available starting iOS 16+, macOS 10.15+, tvOS 17+, and watchOS 9+.

GRDB/Fixits.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Fixits for changes introduced by GRDB 7.0.0
2+
// swiftlint:disable all
23

34
extension Configuration {
45
@available(*, unavailable, message: "The default transaction kind is now automatically managed.")
@@ -15,3 +16,5 @@ extension DatabasePool {
1516
@available(*, unavailable, message: "concurrentRead has been removed. Use `asyncConcurrentRead` instead.")
1617
public func concurrentRead<T>(_ value: @escaping (Database) throws -> T) -> DatabaseFuture<T> { preconditionFailure() }
1718
}
19+
20+
// swiftlint:enable all

GRDB/QueryInterface/SQL/SQLExpression.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2168,6 +2168,7 @@ extension SQLExpressible where Self == Column {
21682168
/// - ``average(_:filter:)``
21692169
/// - ``capitalized``
21702170
/// - ``cast(_:as:)-1dmu3``
2171+
/// - ``coalesce(_:)``
21712172
/// - ``count(_:)``
21722173
/// - ``count(distinct:)``
21732174
/// - ``dateTime(_:_:)``

GRDB/QueryInterface/SQL/SQLFunctions.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,32 @@ public func cast(_ expression: some SQLSpecificExpressible, as storageClass: Dat
7171
.cast(expression.sqlExpression, as: storageClass)
7272
}
7373

74+
/// The `COALESCE` SQL function.
75+
///
76+
/// For example:
77+
///
78+
/// ```swift
79+
/// // COALESCE(value1, value2, ...)
80+
/// coalesce([Column("value1"), Column("value2"), ...])
81+
/// ```
82+
///
83+
/// Unlike the SQL function, `coalesce` accepts any number of arguments.
84+
/// When `values` is empty, the result is `NULL`. When `values` contains a
85+
/// single value, the result is this value. `COALESCE` is used from
86+
/// two values upwards.
87+
public func coalesce(_ values: some Collection<any SQLSpecificExpressible>) -> SQLExpression {
88+
// SQLite COALESCE wants at least two arguments.
89+
// There is no reason to apply the same limitation.
90+
guard let value = values.first else {
91+
return .null
92+
}
93+
if values.count > 1 {
94+
return .function("COALESCE", values.map { $0.sqlExpression })
95+
} else {
96+
return value.sqlExpression
97+
}
98+
}
99+
74100
/// The `COUNT` SQL function.
75101
///
76102
/// For example:

0 commit comments

Comments
 (0)