Skip to content

Commit 3b6cf4c

Browse files
committed
Merge branch 'development'
2 parents cceb24a + 2329a5c commit 3b6cf4c

File tree

55 files changed

+4262
-1356
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+4262
-1356
lines changed

CHANGELOG.md

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

88
#### 5.x Releases
99

10+
- `5.5.x` Releases - [5.5.0](#550)
1011
- `5.4.x` Releases - [5.4.0](#540)
1112
- `5.3.x` Releases - [5.3.0](#530)
1213
- `5.2.x` Releases - [5.2.0](#520)
@@ -69,6 +70,25 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:
6970

7071
- [0.110.0](#01100), ...
7172

73+
74+
## 5.5.0
75+
76+
Released March 3, 2021 • [diff](https://github.com/groue/GRDB.swift/compare/v5.4.0...v5.5.0)
77+
78+
- **New**: You can now define common table expressions without any generic qualifier (which defaults to `Row`):
79+
80+
```swift
81+
let cte = CommonTableExpression(...)
82+
```
83+
84+
The [Common Table Expressions Guide](Documentation/CommonTableExpressions.md) was updated accordingly.
85+
86+
- **New**: `DatabaseQueue` reading methods are now wrapped in a deferred transaction. This guarantees snapshot isolation in case of concurrent writes performed by external connections, and makes `DatabaseQueue` a type suitable for shared databases.
87+
88+
- **Fixed**: `DatabaseQueue.read` is now declared `throws` instead of `rethrows`.
89+
90+
- **Fixed**: [#930](https://github.com/groue/GRDB.swift/pull/930): Fix SQL generation for `COLLATE`, `IN`, `NOT IN`
91+
7292
## 5.4.0
7393

7494
Released February 15, 2021 • [diff](https://github.com/groue/GRDB.swift/compare/v5.3.0...v5.4.0)

Documentation/CommonTableExpressions.md

Lines changed: 43 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ The CTE request can be provided as a [query interface request]:
2929

3030
```swift
3131
// WITH playerName AS (SELECT name FROM player) ...
32-
let playerNameCTE = CommonTableExpression<Void>(
32+
let playerNameCTE = CommonTableExpression(
3333
named: "playerName",
3434
request: Player.select(Column("name")))
3535
```
@@ -40,18 +40,18 @@ You can feed a CTE with raw SQL as well (second and third examples use [SQL Inte
4040
let name = "O'Brien"
4141

4242
// WITH playerName AS (SELECT 'O''Brien') ...
43-
let playerNameCTE = CommonTableExpression<Void>(
43+
let playerNameCTE = CommonTableExpression(
4444
named: "playerName",
4545
sql: "SELECT ?", arguments: [name])
4646

4747
// WITH playerName AS (SELECT 'O''Brien') ...
48-
let playerNameCTE = CommonTableExpression<Void>(
48+
let playerNameCTE = CommonTableExpression(
4949
named: "playerName",
5050
literal: "SELECT \(name)")
5151

5252
// WITH playerName AS (SELECT 'O''Brien') ...
5353
let request: SQLRequest<String> = "SELECT \(name)"
54-
let playerNameCTE = CommonTableExpression<Void>(
54+
let playerNameCTE = CommonTableExpression(
5555
named: "playerName",
5656
request: requests)
5757
```
@@ -60,7 +60,7 @@ All CTEs can be provided with explicit column names:
6060

6161
```swift
6262
// WITH pair(a, b) AS (SELECT 1, 2) ...
63-
let pairCTE = CommonTableExpression<Void>(
63+
let pairCTE = CommonTableExpression(
6464
named: "pair",
6565
columns: ["a", "b"],
6666
sql: "SELECT 1, 2")
@@ -71,7 +71,7 @@ Recursive CTEs need the `recursive` flag. The example below selects all integers
7171
```swift
7272
// WITH RECURSIVE counter(x) AS
7373
// (VALUES(1) UNION ALL SELECT x+1 FROM counter WHERE x<1000)
74-
let counterCTE = CommonTableExpression<Int>(
74+
let counterCTE = CommonTableExpression(
7575
recursive: true,
7676
named: "counter",
7777
columns: ["x"],
@@ -84,8 +84,6 @@ let counterCTE = CommonTableExpression<Int>(
8484

8585
> :point_up: **Note**: many recursive CTEs use the `UNION ALL` SQL operator. The query interface does not provide any Swift support for it, so you'll generally have to write SQL in your definitions of recursive CTEs.
8686
87-
As you can see in all above examples, `CommonTableExpression` is a generic type: `CommonTableExpression<Void>`, `CommonTableExpression<Int>`. The generic argument (`Void`, `Int`) turns useful when you [join common table expressions](#associations-to-common-table-expressions), or when you [fetch values directly from a common table expression](#fetch-values-from-common-table-expressions). Otherwise, you can just use `Void`.
88-
8987

9088
## Embed Common Table Expressions in Requests
9189

@@ -103,7 +101,7 @@ We first build a `CommonTableExpression`:
103101

104102
```swift
105103
let name = "O'Brien"
106-
let playerNameCTE = CommonTableExpression<Void>(
104+
let playerNameCTE = CommonTableExpression(
107105
named: "playerName",
108106
literal: "SELECT \(name)")
109107
```
@@ -184,48 +182,37 @@ try Player
184182

185183
In the previous chapter, a common table expression was embedded as a subquery, with the `CommonTableExpression.all()` method.
186184

187-
`all()` builds a regular [query interface request] that you can filter, sort, etc, like all query interface requests. You can also fetch from it, but only as long as it is provided with the definition of the CTE.
188-
189-
This will generally give requests of the form `cte.all().with(cte)`. In SQL, this would give: `WITH cte AS (...) SELECT * FROM cte`.
185+
`cte.all()` builds a regular [query interface request] that you can filter, sort, etc, like all query interface requests.
190186

191-
The generic type of `CommonTableExpression<...>` now turns out useful, so that you can fetch the desired outcome (database [rows](../README.md#row-queries), simple [values](../README.md#value-queries), or custom [records](../README.md#records)).
187+
You can also fetch from `cte.all()`, as long as the request is given the definition of the CTE: `cte.all().with(cte)`. In SQL, this would give: `WITH cte AS (...) SELECT * FROM cte`:
192188

193-
For example, let's fetch a range of integer:
189+
This request, of type `QueryInterfaceRequest<Row>`, can fetch raw database [rows](../README.md#row-queries):
194190

195191
```swift
196-
func counterRequest(range: ClosedRange<Int>) -> QueryInterfaceRequest<Int> {
197-
// WITH RECURSIVE counter(x) AS
198-
// (VALUES(...) UNION ALL SELECT x+1 FROM counter WHERE x<...)
199-
// SELECT * FROM counter
200-
let counter = CommonTableExpression<Int>(
201-
recursive: true,
202-
named: "counter",
203-
columns: ["x"],
204-
literal: """
205-
VALUES(\(range.lowerBound)) \
206-
UNION ALL \
207-
SELECT x+1 FROM counter WHERE x < \(range.upperBound)
208-
""")
209-
return counter.all().with(counter)
210-
}
211-
212-
let values = try dbQueue.read { db in
213-
try counterRequest(range: 3...7).fetchAll(db)
214-
}
215-
print(values) // prints "[3, 4, 5, 6, 7]"
192+
let cte = CommonTableExpression(...)
193+
let request = cte.all().with(cte)
194+
let rows = try request.fetchAll(db) // [Row]
216195
```
217196

218-
When you have to fetch from a `CommonTableExpression` which does not have the desired generic type, you can still use the `asRequest(of:)` method:
219-
220-
```swift
221-
let cte: CommonTableExpression<Void> = ...
222-
let rows: [Row] = try dbQueue.read { db in
223-
try cte.all().with(cte)
224-
.asRequest(of: Row.self)
225-
.fetchAll(db)
226-
}
227-
```
197+
In order to fetch something else, such as simple [values](../README.md#value-queries), or custom [records](../README.md#records), you have two possible options:
228198

199+
1. Use the `asRequest(of:)` method:
200+
201+
```swift
202+
let cte = CommonTableExpression(...)
203+
let request = cte.all().with(cte).asRequest(of: Player.self)
204+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
205+
let players = try request.fetchAll(db) // [Player]
206+
```
207+
208+
2. Provide the fetched type to the cte itself:
209+
210+
```swift
211+
let cte = CommonTableExpression<Player>(...)
212+
// ~~~~~~~~
213+
let request = cte.all().with(cte)
214+
let players = try request.fetchAll(db) // [Player]
215+
```
229216

230217
## Associations to Common Table Expressions
231218

@@ -337,7 +324,7 @@ We can now define the CTE for the latest messages:
337324
```swift
338325
// WITH latestMessage AS
339326
// (SELECT *, MAX(date) FROM message GROUP BY chatID)
340-
let latestMessageCTE = CommonTableExpression<Void>(
327+
let latestMessageCTE = CommonTableExpression(
341328
named: "latestMessage",
342329
request: latestMessageRequest)
343330
```
@@ -378,9 +365,9 @@ And we can fetch the data that feeds our application screen:
378365
let chatInfos: [ChatInfos] = try dbQueue.read(request.fetchAll)
379366
```
380367

381-
> :bulb: **Tip**: the joining methods are generally type-safe: they won't allow you to join apples to oranges. This works when associations have a *precise* type. In this context, our go-to `CommonTableExpression<Void>` CTEs can work against type safety. So when you want to define associations between several CTEs, and make sure the compiler will notice wrong uses of those associations, tag your `CommonTableExpression` with a type instead of `Void`.
368+
> :bulb: **Tip**: the joining methods are generally type-safe: they won't allow you to join apples to oranges. This works when associations have a *precise* type. In this context, anonymous `CommonTableExpression` CTEs can work against type safety. When you want to define associations between several CTEs, and make sure the compiler will notice wrong uses of those associations, tag your common table expressions with an explicit type: `CommonTableExpression<SomeType>`.
382369
>
383-
> You can use an existing record type, or an ad-hoc enum. For example:
370+
> To do so, you can use an existing record type, or an ad-hoc enum. For example:
384371
>
385372
> ```swift
386373
> enum CTE1 { }
@@ -389,9 +376,9 @@ let chatInfos: [ChatInfos] = try dbQueue.read(request.fetchAll)
389376
> enum CTE2 { }
390377
> let cte2 = CommonTableExpression<CTE2>(...)
391378
>
392-
> let assoc1 = BaseRecord.association(to: cte1, on: ...)
393-
> let assoc2 = cte1.association(to: cte2, on: ...)
394-
> let assoc3 = cte2.association(to: FarRecord.self, on: ...)
379+
> let assoc1 = BaseRecord.association(to: cte1, on: ...) // from BaseRecord to CTE1
380+
> let assoc2 = cte1.association(to: cte2, on: ...) // from CTE1 to CTE2
381+
> let assoc3 = cte2.association(to: FarRecord.self, on: ...) // from CTE2 to FarRecord
395382
>
396383
> // WITH ...
397384
> // SELECT base.* FROM base
@@ -400,12 +387,14 @@ let chatInfos: [ChatInfos] = try dbQueue.read(request.fetchAll)
400387
> // JOIN far ON ...
401388
> let request = BaseRecord
402389
> .with(cte1).with(cte2)
403-
> .joining(required: assoc1
404-
> .joining(required: assoc2
405-
> .joining(required: assoc3)))
390+
> .joining(required: assoc1. // OK
391+
> .joining(required: assoc2. // OK
392+
> .joining(required: assoc3))) // OK
406393
>
407394
> // Compiler error
408-
> let request = BaseRecord.joining(required: assoc2)
395+
> let request = BaseRecord
396+
> .joining(required: assoc2) // Not OK
397+
> .joining(required: assoc3) // Not OK
409398
> ```
410399
411400
[query interface request]: ../README.md#requests

Documentation/DemoApps/GRDBDemoiOS/GRDBDemoiOS.xcodeproj/xcshareddata/xcschemes/GRDBDemoWatchOS.xcscheme

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,33 +78,46 @@
7878
debugDocumentVersioning = "YES"
7979
debugServiceExtension = "internal"
8080
allowLocationSimulation = "YES">
81-
<BuildableProductRunnable
82-
runnableDebuggingMode = "0">
81+
<RemoteRunnable
82+
runnableDebuggingMode = "2"
83+
BundleIdentifier = "com.apple.Carousel"
84+
RemotePath = "/GRDBDemoiOS">
8385
<BuildableReference
8486
BuildableIdentifier = "primary"
8587
BlueprintIdentifier = "568E5FBE1E926430002582E0"
8688
BuildableName = "GRDBDemoWatchOS.app"
8789
BlueprintName = "GRDBDemoWatchOS"
8890
ReferencedContainer = "container:GRDBDemoiOS.xcodeproj">
8991
</BuildableReference>
90-
</BuildableProductRunnable>
92+
</RemoteRunnable>
9193
</LaunchAction>
9294
<ProfileAction
9395
buildConfiguration = "Release"
9496
shouldUseLaunchSchemeArgsEnv = "YES"
9597
savedToolIdentifier = ""
9698
useCustomWorkingDirectory = "NO"
9799
debugDocumentVersioning = "YES">
98-
<BuildableProductRunnable
99-
runnableDebuggingMode = "0">
100+
<RemoteRunnable
101+
runnableDebuggingMode = "2"
102+
BundleIdentifier = "com.apple.Carousel"
103+
RemotePath = "/GRDBDemoiOS">
100104
<BuildableReference
101105
BuildableIdentifier = "primary"
102106
BlueprintIdentifier = "568E5FBE1E926430002582E0"
103107
BuildableName = "GRDBDemoWatchOS.app"
104108
BlueprintName = "GRDBDemoWatchOS"
105109
ReferencedContainer = "container:GRDBDemoiOS.xcodeproj">
106110
</BuildableReference>
107-
</BuildableProductRunnable>
111+
</RemoteRunnable>
112+
<MacroExpansion>
113+
<BuildableReference
114+
BuildableIdentifier = "primary"
115+
BlueprintIdentifier = "568E5FBE1E926430002582E0"
116+
BuildableName = "GRDBDemoWatchOS.app"
117+
BlueprintName = "GRDBDemoWatchOS"
118+
ReferencedContainer = "container:GRDBDemoiOS.xcodeproj">
119+
</BuildableReference>
120+
</MacroExpansion>
108121
</ProfileAction>
109122
<AnalyzeAction
110123
buildConfiguration = "Debug">

Documentation/FullTextSearch.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ let pattern = FTS3Pattern(matchingAnyTokenIn: "") // nil
280280
let pattern = FTS3Pattern(matchingAnyTokenIn: "*") // nil
281281
```
282282

283-
FTS3Pattern are regular [values](../README.md#values). You can use them as query [arguments](http://groue.github.io/GRDB.swift/docs/5.4/Structs/StatementArguments.html):
283+
FTS3Pattern are regular [values](../README.md#values). You can use them as query [arguments](http://groue.github.io/GRDB.swift/docs/5.5/Structs/StatementArguments.html):
284284

285285
```swift
286286
let documents = try Document.fetchAll(db,
@@ -529,7 +529,7 @@ let pattern = FTS5Pattern(matchingAnyTokenIn: "") // nil
529529
let pattern = FTS5Pattern(matchingAnyTokenIn: "*") // nil
530530
```
531531

532-
FTS5Pattern are regular [values](../README.md#values). You can use them as query [arguments](http://groue.github.io/GRDB.swift/docs/5.4/Structs/StatementArguments.html):
532+
FTS5Pattern are regular [values](../README.md#values). You can use them as query [arguments](http://groue.github.io/GRDB.swift/docs/5.5/Structs/StatementArguments.html):
533533

534534
```swift
535535
let documents = try Document.fetchAll(db,

Documentation/Migrations.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ try dbQueue.read { db in
6868
}
6969
```
7070

71-
See the [DatabaseMigrator reference](http://groue.github.io/GRDB.swift/docs/5.4/Structs/DatabaseMigrator.html) for more migrator methods.
71+
See the [DatabaseMigrator reference](http://groue.github.io/GRDB.swift/docs/5.5/Structs/DatabaseMigrator.html) for more migrator methods.
7272

7373

7474
## The `eraseDatabaseOnSchemaChange` Option

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 = '5.4.0'
3+
s.version = '5.5.0'
44

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

0 commit comments

Comments
 (0)