Skip to content

Commit 2c5a864

Browse files
committed
DocC: Transactions and Savepoints
1 parent 119f226 commit 2c5a864

File tree

11 files changed

+471
-339
lines changed

11 files changed

+471
-339
lines changed

GRDB/Core/Configuration.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ public struct Configuration {
6464
/// A boolean value indicating whether an SQLite connection is read-only.
6565
///
6666
/// The default is false.
67+
///
68+
/// ```swift
69+
/// var config = Configuration()
70+
/// config.readonly = true
71+
///
72+
/// let dbQueue = try DatabaseQueue( // or DatabasePool
73+
/// path: "/path/to/database.sqlite",
74+
/// configuration: config)
75+
/// ```
6776
public var readonly = false
6877

6978
/// A label that describes a database connection.

GRDB/Core/DatabasePool.swift

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,66 +4,6 @@ import Foundation
44
import UIKit
55
#endif
66

7-
/// A database connection that allows concurrent accesses to an SQLite database.
8-
///
9-
/// ## Overview
10-
///
11-
/// Unless ``Configuration/readonly``, a `DatabasePool` opens an SQLite database
12-
/// in the [WAL mode](https://sqlite.org/wal.html).
13-
///
14-
/// It creates one writer SQLite connection, and a pool of up to
15-
/// ``Configuration/maximumReaderCount`` read-only SQLite connections. All
16-
/// write accesses are executed in a serial **writer dispatch queue**. All
17-
/// read accesses are executed in **reader dispatch queues** (one per read-only
18-
/// SQLite connection). SQLite connections are closed when the `DatabasePool`
19-
/// is deallocated.
20-
///
21-
/// See <doc:Concurrency> for more information about concurrent
22-
/// database accesses.
23-
///
24-
/// ## Usage
25-
///
26-
/// ```swift
27-
/// let dbPool = try DatabasePool(path: "/path/to/database.sqlite")
28-
///
29-
/// let playerCount = try dbPool.read { db in
30-
/// try Player.fetchCount(db)
31-
/// }
32-
///
33-
/// let newPlayerCount = try dbPool.write { db -> Int in
34-
/// try Player(name: "Arthur").insert(db)
35-
/// return try Player.fetchCount(db)
36-
/// }
37-
/// ```
38-
///
39-
/// `DatabasePool` inherits most of its database access methods from the
40-
/// ``DatabaseReader`` and ``DatabaseWriter`` protocols. It defines a few
41-
/// specific database access methods as well.
42-
///
43-
/// ## Topics
44-
///
45-
/// ### Creating a DatabasePool
46-
///
47-
/// - ``init(path:configuration:)``
48-
///
49-
/// ### Accessing the Database
50-
///
51-
/// See ``DatabaseReader`` and ``DatabaseWriter`` for more database
52-
/// access methods.
53-
///
54-
/// - ``asyncConcurrentRead(_:)``
55-
/// - ``writeInTransaction(_:_:)``
56-
///
57-
/// ### Creating Database Snapshots
58-
///
59-
/// - ``makeSnapshot()``
60-
/// - ``makeSnapshotPool()``
61-
///
62-
/// ### Managing SQLite Connections
63-
///
64-
/// - ``invalidateReadOnlyConnections()``
65-
/// - ``releaseMemory()``
66-
/// - ``releaseMemoryEventually()``
677
public final class DatabasePool {
688
private let writer: SerializedDatabase
699

GRDB/Core/DatabaseQueue.swift

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,6 @@ import Foundation
44
import UIKit
55
#endif
66

7-
/// A database connection that serializes accesses to an SQLite database.
8-
///
9-
/// ## Overview
10-
///
11-
/// A `DatabaseQueue` creates one single SQLite connection. All database
12-
/// accesses are executed in a serial **writer dispatch queue**. The SQLite
13-
/// connection is closed when the `DatabaseQueue` is deallocated.
14-
///
15-
/// ```swift
16-
/// let dbQueue = try DatabaseQueue(path: "/path/to/database.sqlite")
17-
///
18-
/// let playerCount = try dbQueue.read { db in
19-
/// try Player.fetchCount(db)
20-
/// }
21-
///
22-
/// let newPlayerCount = try dbQueue.write { db -> Int in
23-
/// try Player(name: "Arthur").insert(db)
24-
/// return try Player.fetchCount(db)
25-
/// }
26-
/// ```
27-
///
28-
/// ## Usage
29-
///
30-
/// `DatabaseQueue` inherits most of its database access methods from the
31-
/// ``DatabaseReader`` and ``DatabaseWriter`` protocols. It defines a few
32-
/// specific database access methods as well.
33-
///
34-
/// ## Topics
35-
///
36-
/// ### Creating a DatabaseQueue
37-
///
38-
/// - ``init(named:configuration:)``
39-
/// - ``init(path:configuration:)``
40-
///
41-
/// ### Accessing the Database
42-
///
43-
/// See ``DatabaseReader`` and ``DatabaseWriter`` for more database
44-
/// access methods.
45-
///
46-
/// - ``inDatabase(_:)``
47-
/// - ``inTransaction(_:_:)``
48-
///
49-
/// ### Managing the SQLite Connection
50-
///
51-
/// - ``releaseMemory()``
527
public final class DatabaseQueue {
538
private let writer: SerializedDatabase
549

GRDB/Documentation.docc/Concurrency.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ try dbQueue.read { db
4545
}
4646
```
4747

48-
Alternatively, you can open an explicit transaction or savepoint with ``Database/inTransaction(_:_:)`` and ``Database/inSavepoint(_:)``.
48+
Alternatively, you can open an explicit transaction or savepoint: see <doc:Transactions>.
4949

5050
- *Why does this rule exist?* - Because GRDB and SQLite can not guess where to insert the transaction boundaries that protect the invariants of your database. This is your task. Transactions also avoid concurrency problems, as described in the <doc:Concurrency#Safe-and-Unsafe-Database-Accesses> section below.
5151

GRDB/Documentation.docc/DatabaseConnections.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,34 @@ let dbPool = try DatabasePool(path: "/path/to/database.sqlite")
1717
The differences are:
1818

1919
- `DatabasePool` allows concurrent database accesses (this can improve the performance of multithreaded applications).
20-
- `DatabasePool` opens your SQLite database in the [WAL mode](https://www.sqlite.org/wal.html) (unless read-only).
21-
- `DatabaseQueue` supports [in-memory databases](https://www.sqlite.org/inmemorydb.html).
20+
- `DatabasePool` opens your SQLite database in the [WAL mode](https://www.sqlite.org/wal.html) (unless ``Configuration/readonly``).
21+
- `DatabaseQueue` supports <doc:DatabaseQueue#In-Memory-Databases>.
2222

2323
**If you are not sure, choose `DatabaseQueue`.** You will always be able to switch to `DatabasePool` later.
2424

25+
Once connected, you can define the database schema, and access the database with raw SQL or high-level Swift apis:
26+
27+
- <doc:DatabaseSchema>
28+
- <doc:SQLSupport>
29+
- <doc:QueryInterface>
30+
2531
## Topics
2632

27-
### Configuring Database Connections
33+
### Configuring database connections
2834

2935
- ``Configuration``
3036

31-
### Database Connections
37+
### Connections for read and write accesses
3238

3339
- ``DatabaseQueue``
3440
- ``DatabasePool``
41+
42+
### Read-only connections on an unchanging database content
43+
3544
- ``DatabaseSnapshot``
3645
- ``DatabaseSnapshotPool``
3746

38-
### Using Database Connections
47+
### Using database connections
3948

4049
- ``Database``
4150
- ``DatabaseError``

GRDB/Documentation.docc/Documentation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Compared to [SQLite.swift](https://github.com/stephencelis/SQLite.swift) or [FMD
2121
- <doc:DatabaseConnections>
2222
- <doc:SQLSupport>
2323
- <doc:Concurrency>
24+
- <doc:Transactions>
2425

2526
### Migrations and The Database Schema
2627

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# ``GRDB/DatabasePool``
2+
3+
A database connection that allows concurrent accesses to an SQLite database.
4+
5+
## Usage
6+
7+
**Open a `DatabasePool`** with the path to a database file:
8+
9+
```swift
10+
import GRDB
11+
12+
let dbPool = try DatabasePool(path: "/path/to/database.sqlite")
13+
```
14+
15+
SQLite creates the database file if it does not already exist. The connection is closed when the database queue gets deallocated.
16+
17+
**A `DatabasePool` can be used from any thread.** The ``DatabaseWriter/write(_:)-76inz`` and ``DatabaseReader/read(_:)-3806d`` methods are synchronous, and block the current thread until your database statements are executed in a protected dispatch queue:
18+
19+
```swift
20+
// Modify the database:
21+
try dbPool.write { db in
22+
try Player(name: "Arthur").insert(db)
23+
}
24+
25+
// Read values:
26+
try dbPool.read { db in
27+
let players = try Player.fetchAll(db)
28+
let playerCount = try Player.fetchCount(db)
29+
}
30+
```
31+
32+
Database access methods can return values:
33+
34+
```swift
35+
let playerCount = try dbPool.read { db in
36+
try Place.fetchCount(db)
37+
}
38+
39+
let newPlayerCount = try dbPool.write { db -> Int in
40+
try Player(name: "Arthur").insert(db)
41+
return try Player.fetchCount(db)
42+
}
43+
```
44+
45+
The ``DatabaseWriter/write(_:)-76inz`` method wraps your database statements in a transaction that commits if and only if no error occurs. On the first unhandled error, all changes are reverted, the whole transaction is rollbacked, and the error is rethrown.
46+
47+
When you don't need to modify the database, prefer the ``DatabaseReader/read(_:)-3806d`` method, because several threads can perform reads in parallel.
48+
49+
When precise transaction handling is required, see <doc:Transactions>.
50+
51+
Asynchronous database accesses are described in <doc:Concurrency>.
52+
53+
`DatabasePool` can take snapshots of the database: see ``DatabaseSnapshot`` and ``DatabaseSnapshotPool``.
54+
55+
`DatabasePool` can be configured with ``Configuration``.
56+
57+
## Concurrency
58+
59+
A `DatabasePool` creates one writer SQLite connection, and a pool of read-only SQLite connections.
60+
61+
Unless ``Configuration/readonly``, the database is set to the [WAL mode](https://sqlite.org/wal.html). The WAL mode makes it possible for reads and writes to proceed concurrently.
62+
63+
All write accesses are executed in a serial **writer dispatch queue**, which means that there is never more than one thread that writes in the database.
64+
65+
All read accesses are executed in **reader dispatch queues** (one per read-only SQLite connection). Reads are generally non-blocking, unless the maximum number of concurrent reads has been reached. In this case, a read has to wait for another read to complete. That maximum number can be configured with ``Configuration/maximumReaderCount``.
66+
67+
SQLite connections are closed when the `DatabasePool` is deallocated.
68+
69+
`DatabasePool` inherits most of its database access methods from the ``DatabaseReader`` and ``DatabaseWriter`` protocols. It defines a few specific database access methods as well, listed below.
70+
71+
A `DatabasePool` needs your application to follow rules in order to deliver its safety guarantees. See <doc:Concurrency> for more information.
72+
73+
## Topics
74+
75+
### Creating a DatabasePool
76+
77+
- ``init(path:configuration:)``
78+
79+
### Accessing the Database
80+
81+
See ``DatabaseReader`` and ``DatabaseWriter`` for more database
82+
access methods.
83+
84+
- ``asyncConcurrentRead(_:)``
85+
- ``writeInTransaction(_:_:)``
86+
87+
### Creating Database Snapshots
88+
89+
- ``makeSnapshot()``
90+
- ``makeSnapshotPool()``
91+
92+
### Managing SQLite Connections
93+
94+
- ``invalidateReadOnlyConnections()``
95+
- ``releaseMemory()``
96+
- ``releaseMemoryEventually()``
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# ``GRDB/DatabaseQueue``
2+
3+
A database connection that serializes accesses to an SQLite database.
4+
5+
## Usage
6+
7+
**Open a `DatabaseQueue`** with the path to a database file:
8+
9+
```swift
10+
import GRDB
11+
12+
let dbQueue = try DatabaseQueue(path: "/path/to/database.sqlite")
13+
```
14+
15+
SQLite creates the database file if it does not already exist. The connection is closed when the database queue gets deallocated.
16+
17+
**A `DatabaseQueue` can be used from any thread.** The ``DatabaseWriter/write(_:)-76inz`` and ``DatabaseReader/read(_:)-3806d`` methods are synchronous, and block the current thread until your database statements are executed in a protected dispatch queue:
18+
19+
```swift
20+
// Modify the database:
21+
try dbQueue.write { db in
22+
try Player(name: "Arthur").insert(db)
23+
}
24+
25+
// Read values:
26+
try dbQueue.read { db in
27+
let players = try Player.fetchAll(db)
28+
let playerCount = try Player.fetchCount(db)
29+
}
30+
```
31+
32+
Database access methods can return values:
33+
34+
```swift
35+
let playerCount = try dbQueue.read { db in
36+
try Place.fetchCount(db)
37+
}
38+
39+
let newPlayerCount = try dbQueue.write { db -> Int in
40+
try Player(name: "Arthur").insert(db)
41+
return try Player.fetchCount(db)
42+
}
43+
```
44+
45+
The ``DatabaseWriter/write(_:)-76inz`` method wraps your database statements in a transaction that commits if and only if no error occurs. On the first unhandled error, all changes are reverted, the whole transaction is rollbacked, and the error is rethrown.
46+
47+
When you don't need to modify the database, prefer the ``DatabaseReader/read(_:)-3806d`` method: it prevents any modification to the database.
48+
49+
When precise transaction handling is required, see <doc:Transactions>.
50+
51+
Asynchronous database accesses are described in <doc:Concurrency>.
52+
53+
`DatabaseQueue` can be configured with ``Configuration``.
54+
55+
## In-Memory Databases
56+
57+
`DatabaseQueue` can open a connection to an [in-memory SQLite database](https://www.sqlite.org/inmemorydb.html).
58+
59+
Such connections are quite handy for tests and SwiftUI previews, since you do not have to perform any cleanup of the file system.
60+
61+
```swift
62+
let dbQueue = try DatabaseQueue()
63+
```
64+
65+
In order to create several connections to the same in-memory database, give this database a name:
66+
67+
```swift
68+
// A shared in-memory database
69+
let dbQueue1 = try DatabaseQueue(named: "myDatabase")
70+
71+
// Another connection to the same database
72+
let dbQueue2 = try DatabaseQueue(named: "myDatabase")
73+
```
74+
75+
See ``init(named:configuration:)``.
76+
77+
## Concurrency
78+
79+
A `DatabaseQueue` creates one single SQLite connection. All database accesses are executed in a serial **writer dispatch queue**, which means that there is never more than one thread that uses the database. The SQLite connection is closed when the `DatabaseQueue` is deallocated.
80+
81+
`DatabaseQueue` inherits most of its database access methods from the ``DatabaseReader`` and ``DatabaseWriter`` protocols. It defines a few specific database access methods as well, listed below.
82+
83+
A `DatabaseQueue` needs your application to follow rules in order to deliver its safety guarantees. See <doc:Concurrency> for more information.
84+
85+
## Topics
86+
87+
### Creating a DatabaseQueue
88+
89+
- ``init(named:configuration:)``
90+
- ``init(path:configuration:)``
91+
92+
### Accessing the Database
93+
94+
See ``DatabaseReader`` and ``DatabaseWriter`` for more database
95+
access methods.
96+
97+
- ``inDatabase(_:)``
98+
- ``inTransaction(_:_:)``
99+
100+
### Managing the SQLite Connection
101+
102+
- ``releaseMemory()``

0 commit comments

Comments
 (0)