Skip to content

Commit 33cdf6f

Browse files
committed
Extract <doc:DatabaseSchema>
1 parent ca079ca commit 33cdf6f

File tree

5 files changed

+104
-65
lines changed

5 files changed

+104
-65
lines changed

GRDB/Core/Database.swift

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ let SQLITE_TRANSIENT = unsafeBitCast(OpaquePointer(bitPattern: -1), to: sqlite3_
2424
/// }
2525
/// ```
2626
///
27+
/// `Database` methods that modify or query the database schema are listed
28+
/// in <doc:DatabaseSchema>.
29+
///
2730
/// ## Topics
2831
///
2932
/// ### Database Information
@@ -71,44 +74,6 @@ let SQLITE_TRANSIENT = unsafeBitCast(OpaquePointer(bitPattern: -1), to: sqlite3_
7174
/// - ``TransactionObserver``
7275
/// - ``TransactionObservationExtent``
7376
///
74-
/// ### Defining the Database Schema
75-
///
76-
/// - ``alter(table:body:)``
77-
/// - ``clearSchemaCache()``
78-
/// - ``create(index:on:columns:options:condition:)``
79-
/// - ``create(table:options:body:)``
80-
/// - ``create(virtualTable:ifNotExists:using:)``
81-
/// - ``create(virtualTable:ifNotExists:using:_:)``
82-
/// - ``drop(index:)``
83-
/// - ``drop(table:)``
84-
/// - ``dropFTS4SynchronizationTriggers(forTable:)``
85-
/// - ``rename(table:to:)``
86-
/// - ``ColumnType``
87-
/// - ``ConflictResolution``
88-
/// - ``ForeignKeyAction``
89-
/// - ``IndexOptions``
90-
/// - ``TableAlteration``
91-
/// - ``TableDefinition``
92-
/// - ``TableOptions``
93-
/// - ``VirtualTableModule``
94-
///
95-
/// ### Querying the Database Schema
96-
///
97-
/// - ``columns(in:)``
98-
/// - ``foreignKeys(on:)``
99-
/// - ``indexes(on:)``
100-
/// - ``isGRDBInternalTable(_:)``
101-
/// - ``isSQLiteInternalTable(_:)``
102-
/// - ``primaryKey(_:)``
103-
/// - ``table(_:hasUniqueKey:)``
104-
/// - ``tableExists(_:)``
105-
/// - ``triggerExists(_:)``
106-
/// - ``viewExists(_:)``
107-
/// - ``ColumnInfo``
108-
/// - ``ForeignKeyInfo``
109-
/// - ``IndexInfo``
110-
/// - ``PrimaryKeyInfo``
111-
///
11277
/// ### Collations
11378
///
11479
/// - ``add(collation:)``
@@ -124,23 +89,16 @@ let SQLITE_TRANSIENT = unsafeBitCast(OpaquePointer(bitPattern: -1), to: sqlite3_
12489
/// - ``remove(function:)``
12590
/// - ``DatabaseFunction``
12691
///
127-
/// ### Integrity Checks
128-
///
129-
/// - ``checkForeignKeys()``
130-
/// - ``checkForeignKeys(in:)``
131-
/// - ``foreignKeyViolations()``
132-
/// - ``foreignKeyViolations(in:)``
133-
/// - ``ForeignKeyViolation``
134-
///
13592
/// ### Notifications
13693
///
13794
/// - ``resumeNotification``
13895
/// - ``suspendNotification``
13996
///
140-
/// ### Other Operations
97+
/// ### Other Database Operations
14198
///
14299
/// - ``backup(to:pagesPerStep:progress:)``
143100
/// - ``checkpoint(_:on:)``
101+
/// - ``clearSchemaCache()``
144102
/// - ``logError``
145103
/// - ``releaseMemory()``
146104
/// - ``trace(options:_:)``
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# The Database Schema
2+
3+
Learn how to define the database schema.
4+
5+
## Overview
6+
7+
SQLite directly supports a [set of schema alterations](https://www.sqlite.org/lang.html). Many of them are available as `Database` methods such as ``Database/create(table:options:body:)``, ``Database/alter(table:body:)``, listed below.
8+
9+
You can directly create tables when you open a database, as below:
10+
11+
```swift
12+
let dbQueue = try DatabaseQueue(path: "/path/to/database.sqlite")
13+
14+
try dbQueue.write { db in
15+
try db.create(table: "player", options: .ifNotExists) { t in
16+
t.autoIncrementedPrimaryKey("id")
17+
t.column("name", .text).notNull()
18+
t.column("score", .integer).notNull()
19+
}
20+
}
21+
```
22+
23+
But you should prefer wrapping your schema changes in <doc:Migrations> when you plan to upgrade the database schema in future versions of your app.
24+
25+
> Tip: When modifying the database schema, prefer Swift APIs over raw SQL queries. This helps the compiler check if features are available on the SQLite version that ships on the target operating system. For example:
26+
>
27+
> - Dropping a table column requires SQLite 3.35+ (iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0).
28+
> - [Strict tables](https://www.sqlite.org/stricttables.html) require SQLite 3.37+ (iOS 15.4, macOS 12.4, tvOS 15.4, watchOS 8.5).
29+
>
30+
> When you need to perform a schema change that is not directly supported, or not available, you will sometimes need to recreate database tables. See <doc:Migrations> for the detailed procedure.
31+
32+
## Topics
33+
34+
### Database Tables
35+
36+
- ``Database/alter(table:body:)``
37+
- ``Database/create(table:options:body:)``
38+
- ``Database/create(virtualTable:ifNotExists:using:)``
39+
- ``Database/create(virtualTable:ifNotExists:using:_:)``
40+
- ``Database/drop(table:)``
41+
- ``Database/dropFTS4SynchronizationTriggers(forTable:)``
42+
- ``Database/rename(table:to:)``
43+
- ``Database/ColumnType``
44+
- ``Database/ConflictResolution``
45+
- ``Database/ForeignKeyAction``
46+
- ``TableAlteration``
47+
- ``TableDefinition``
48+
- ``TableOptions``
49+
- ``VirtualTableModule``
50+
51+
### Database Indexes
52+
53+
- ``Database/create(index:on:columns:options:condition:)``
54+
- ``Database/drop(index:)``
55+
- ``IndexOptions``
56+
57+
### Querying the Database Schema
58+
59+
- ``Database/columns(in:)``
60+
- ``Database/foreignKeys(on:)``
61+
- ``Database/indexes(on:)``
62+
- ``Database/isGRDBInternalTable(_:)``
63+
- ``Database/isSQLiteInternalTable(_:)``
64+
- ``Database/primaryKey(_:)``
65+
- ``Database/table(_:hasUniqueKey:)``
66+
- ``Database/tableExists(_:)``
67+
- ``Database/triggerExists(_:)``
68+
- ``Database/viewExists(_:)``
69+
- ``ColumnInfo``
70+
- ``ForeignKeyInfo``
71+
- ``IndexInfo``
72+
- ``PrimaryKeyInfo``
73+
74+
### Integrity Checks
75+
76+
- ``Database/checkForeignKeys()``
77+
- ``Database/checkForeignKeys(in:)``
78+
- ``Database/foreignKeyViolations()``
79+
- ``Database/foreignKeyViolations(in:)``
80+
- ``ForeignKeyViolation``

GRDB/Documentation.docc/Documentation.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ GRDB provides raw access to SQL and advanced SQLite features, because one someti
1515
- <doc:SQLSupport>
1616
- <doc:Errors>
1717

18-
### Migrations
18+
### Migrations and The Database Schema
1919

20+
- <doc:DatabaseSchema>
2021
- <doc:Migrations>
2122

2223
### Record Types and the Query Interface

GRDB/Documentation.docc/Migrations.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ try dbQueue.read { db in
8383

8484
**The memory of applied migrations is stored in the database itself** (in a reserved table).
8585

86-
## Defining the Database Schema
86+
## Defining the Database Schema from a Migration
8787

88-
SQLite directly supports a [limited set of schema alterations](https://www.sqlite.org/lang.html). Many of them are available as `Database` methods such as ``Database/create(table:options:body:)``, ``Database/alter(table:body:)``, etc.
88+
See <doc:DatabaseSchema> for the methods that define the database schema.
8989

90-
> Tip: When you use a Swift API instead of a raw SQL query, GRDB can check its availability on the SQLite version that ships on the target operating system.
90+
When you need to perform a schema change that is not directly supported by SQLite, or not available on your target operating system, you will need to recreate database tables.
9191

92-
Other changes to the schema are still possible, by recreating tables. For example:
92+
For example:
9393

9494
```swift
9595
migrator.registerMigration("Add NOT NULL check on author.name") { db in
@@ -104,25 +104,25 @@ migrator.registerMigration("Add NOT NULL check on author.name") { db in
104104
}
105105
```
106106

107-
This technique is described in the [Making Other Kinds Of Table Schema Changes](https://www.sqlite.org/lang_altertable.html#making_other_kinds_of_table_schema_changes) section of the SQLite documentation.
107+
The detailed sequence of operations for recreating a database table from a migration is:
108108

109-
The detailed sequence of operations for recreating a database table is:
110-
111-
1. Remember the format of all indexes, triggers, and views associated with table X. This information will be needed in step 5 below. One way to do this is to run a query like the following: `SELECT type, sql FROM sqlite_schema WHERE tbl_name='X'`.
109+
1. When relevant, remember the format of all indexes, triggers, and views associated with table X. This information will be needed in steps 6 and 7 below. One way to do this is to run a query like the following: `SELECT type, sql FROM sqlite_schema WHERE tbl_name='X'`.
112110

113111
2. Use `CREATE TABLE` to construct a new table "new_X" that is in the desired revised format of table X. Make sure that the name "new_X" does not collide with any existing table name, of course.
114112

115-
2. Transfer content from X into new_X using a statement like: `INSERT INTO new_X SELECT ... FROM X`.
113+
3. Transfer content from X into new_X using a statement like: `INSERT INTO new_X SELECT ... FROM X`.
116114

117-
3. Drop the old table X: `DROP TABLE X`.
115+
4. Drop the old table X: `DROP TABLE X`.
118116

119-
4. Change the name of new_X to X using: `ALTER TABLE new_X RENAME TO X`.
117+
5. Change the name of new_X to X using: `ALTER TABLE new_X RENAME TO X`.
120118

121-
5. Use `CREATE INDEX`, `CREATE TRIGGER`, and `CREATE VIEW` to reconstruct indexes, triggers, and views associated with table X. Perhaps use the old format of the triggers, indexes, and views saved from step 3 above as a guide, making changes as appropriate for the alteration.
119+
6. When relevant, use `CREATE INDEX`, `CREATE TRIGGER`, and `CREATE VIEW` to reconstruct indexes, triggers, and views associated with table X. Perhaps use the old format of the triggers, indexes, and views saved from step 3 above as a guide, making changes as appropriate for the alteration.
122120

123-
6. If any views refer to table X in a way that is affected by the schema change, then drop those views using `DROP VIEW` and recreate them with whatever changes are necessary to accommodate the schema change using `CREATE VIEW`.
121+
7. If any views refer to table X in a way that is affected by the schema change, then drop those views using `DROP VIEW` and recreate them with whatever changes are necessary to accommodate the schema change using `CREATE VIEW`.
124122

125123
> Important: When recreating a table, be sure to follow the above procedure exactly, in the given order, or you might corrupt triggers, views, and foreign key constraints.
124+
>
125+
> When you want to recreate a table _outside of a migration_, check the full procedure detailed in the [Making Other Kinds Of Table Schema Changes](https://www.sqlite.org/lang_altertable.html#making_other_kinds_of_table_schema_changes) section of the SQLite documentation.
126126
127127
## Good Practices for Defining Migrations
128128

@@ -148,7 +148,7 @@ migrator.registerMigration("Create authors") { db in
148148

149149
In other words, migrations should talk to the database, only to the database, and use the database language. This makes sure the Swift code of any given migrations will never have to change in the future.
150150

151-
Migrations and the rest of the application code do not live at the same "moment". Migrations describe the past states of the database, while the rest of the application code targets the latest one only. This difference is the reason why migrations should not depend on application types.
151+
Migrations and the rest of the application code do not live at the same "moment". Migrations describe the past states of the database, while the rest of the application code targets the latest one only. This difference is the reason why **migrations should not depend on application types.**
152152

153153
## The eraseDatabaseOnSchemaChange Option
154154

@@ -187,7 +187,7 @@ When you register a migration with `.immediate` foreign key checks, the migratio
187187
migrator.registerMigration("Faster migration", foreignKeyChecks: .immediate) { db in ... }
188188
```
189189

190-
Such a migration is much faster, and it still guarantees database integrity. But it must only execute schema alterations directly supported by SQLite. Migrations that recreate tables as described in <doc:Migrations#Defining-the-Database-Schema> **must not** run with immediate foreign keys checks. You'll need to use the second mitigation technique:
190+
Such a migration is much faster, and it still guarantees database integrity. But it must only execute schema alterations directly supported by SQLite. Migrations that recreate tables as described in <doc:Migrations#Defining-the-Database-Schema-from-a-Migration> **must not** run with immediate foreign keys checks. You'll need to use the second mitigation technique:
191191

192192
**Your second mitigation technique is to disable deferred foreign key checks.**
193193

@@ -201,7 +201,7 @@ migrator = migrator.disablingDeferredForeignKeyChecks()
201201
202202
In order to prevent foreign key violations from being committed to disk, you can:
203203

204-
- Register migrations with immediate foreign key checks, as long as they do not recreate tables as described in <doc:Migrations#Defining-the-Database-Schema>:
204+
- Register migrations with immediate foreign key checks, as long as they do not recreate tables as described in <doc:Migrations#Defining-the-Database-Schema-from-a-Migration>:
205205

206206
```swift
207207
migrator = migrator.disablingDeferredForeignKeyChecks()

GRDB/Migration/DatabaseMigrator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public struct DatabaseMigrator {
6969
///
7070
/// Immediate foreign key checks are NOT compatible with migrations that
7171
/// recreate tables as described
72-
/// in <doc:Migrations#Defining-the-Database-Schema>.
72+
/// in <doc:Migrations#Defining-the-Database-Schema-from-a-Migration>.
7373
case immediate
7474
}
7575

0 commit comments

Comments
 (0)