You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+20Lines changed: 20 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,7 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:
7
7
8
8
#### 5.x Releases
9
9
10
+
- `5.5.x` Releases - [5.5.0](#550)
10
11
- `5.4.x` Releases - [5.4.0](#540)
11
12
- `5.3.x` Releases - [5.3.0](#530)
12
13
- `5.2.x` Releases - [5.2.0](#520)
@@ -69,6 +70,25 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:
69
70
70
71
- [0.110.0](#01100), ...
71
72
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`.
Copy file name to clipboardExpand all lines: Documentation/CommonTableExpressions.md
+43-54Lines changed: 43 additions & 54 deletions
Original file line number
Diff line number
Diff line change
@@ -29,7 +29,7 @@ The CTE request can be provided as a [query interface request]:
29
29
30
30
```swift
31
31
// WITH playerName AS (SELECT name FROM player) ...
32
-
let playerNameCTE = CommonTableExpression<Void>(
32
+
let playerNameCTE =CommonTableExpression(
33
33
named: "playerName",
34
34
request: Player.select(Column("name")))
35
35
```
@@ -40,18 +40,18 @@ You can feed a CTE with raw SQL as well (second and third examples use [SQL Inte
40
40
let name ="O'Brien"
41
41
42
42
// WITH playerName AS (SELECT 'O''Brien') ...
43
-
let playerNameCTE = CommonTableExpression<Void>(
43
+
let playerNameCTE =CommonTableExpression(
44
44
named: "playerName",
45
45
sql: "SELECT ?", arguments: [name])
46
46
47
47
// WITH playerName AS (SELECT 'O''Brien') ...
48
-
let playerNameCTE = CommonTableExpression<Void>(
48
+
let playerNameCTE =CommonTableExpression(
49
49
named: "playerName",
50
50
literal: "SELECT \(name)")
51
51
52
52
// WITH playerName AS (SELECT 'O''Brien') ...
53
53
let request: SQLRequest<String> ="SELECT \(name)"
54
-
let playerNameCTE = CommonTableExpression<Void>(
54
+
let playerNameCTE =CommonTableExpression(
55
55
named: "playerName",
56
56
request: requests)
57
57
```
@@ -60,7 +60,7 @@ All CTEs can be provided with explicit column names:
60
60
61
61
```swift
62
62
// WITH pair(a, b) AS (SELECT 1, 2) ...
63
-
let pairCTE = CommonTableExpression<Void>(
63
+
let pairCTE =CommonTableExpression(
64
64
named: "pair",
65
65
columns: ["a", "b"],
66
66
sql: "SELECT 1, 2")
@@ -71,7 +71,7 @@ Recursive CTEs need the `recursive` flag. The example below selects all integers
71
71
```swift
72
72
// WITH RECURSIVE counter(x) AS
73
73
// (VALUES(1) UNION ALL SELECT x+1 FROM counter WHERE x<1000)
74
-
let counterCTE = CommonTableExpression<Int>(
74
+
let counterCTE =CommonTableExpression(
75
75
recursive: true,
76
76
named: "counter",
77
77
columns: ["x"],
@@ -84,8 +84,6 @@ let counterCTE = CommonTableExpression<Int>(
84
84
85
85
> :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.
86
86
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
-
89
87
90
88
## Embed Common Table Expressions in Requests
91
89
@@ -103,7 +101,7 @@ We first build a `CommonTableExpression`:
103
101
104
102
```swift
105
103
let name ="O'Brien"
106
-
let playerNameCTE = CommonTableExpression<Void>(
104
+
let playerNameCTE =CommonTableExpression(
107
105
named: "playerName",
108
106
literal: "SELECT \(name)")
109
107
```
@@ -184,48 +182,37 @@ try Player
184
182
185
183
In the previous chapter, a common table expression was embedded as a subquery, with the `CommonTableExpression.all()` method.
186
184
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.
190
186
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`:
192
188
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):
// (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
-
trycounterRequest(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]
216
195
```
217
196
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:
228
198
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
+
```
229
216
230
217
## Associations to Common Table Expressions
231
218
@@ -337,7 +324,7 @@ We can now define the CTE for the latest messages:
337
324
```swift
338
325
// WITH latestMessage AS
339
326
// (SELECT *, MAX(date) FROM message GROUP BY chatID)
340
-
let latestMessageCTE = CommonTableExpression<Void>(
327
+
let latestMessageCTE =CommonTableExpression(
341
328
named: "latestMessage",
342
329
request: latestMessageRequest)
343
330
```
@@ -378,9 +365,9 @@ And we can fetch the data that feeds our application screen:
378
365
let chatInfos: [ChatInfos] =try dbQueue.read(request.fetchAll)
379
366
```
380
367
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>`.
382
369
>
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:
384
371
>
385
372
> ```swift
386
373
>enumCTE1 { }
@@ -389,9 +376,9 @@ let chatInfos: [ChatInfos] = try dbQueue.read(request.fetchAll)
389
376
>enumCTE2 { }
390
377
>let cte2 = CommonTableExpression<CTE2>(...)
391
378
>
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
395
382
>
396
383
>// WITH ...
397
384
>// SELECT base.* FROM base
@@ -400,12 +387,14 @@ let chatInfos: [ChatInfos] = try dbQueue.read(request.fetchAll)
let pattern =FTS3Pattern(matchingAnyTokenIn: "*") // nil
281
281
```
282
282
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):
let pattern =FTS5Pattern(matchingAnyTokenIn: "*") // nil
530
530
```
531
531
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):
0 commit comments