Skip to content

Commit 1b98a96

Browse files
committed
Documentation
1 parent 1095d21 commit 1b98a96

File tree

1 file changed

+78
-15
lines changed

1 file changed

+78
-15
lines changed

GRDB/Core/DatabaseSchemaSource.swift

Lines changed: 78 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,25 @@
2727
/// let dbQueue = try DatabaseQueue(path: "/path/to/db.sqlite", configuration: config)
2828
/// ```
2929
///
30-
/// For temporary use, call ``Database/withSchemaSource(_:execute:)``:
30+
/// ## Schema Sources and Migrations
31+
///
32+
/// By default, schema sources are disabled during <doc:Migrations>. In
33+
/// migrations that need a schema source, you can use
34+
/// ``Database/withSchemaSource(_:execute:)``:
3135
///
3236
/// ```swift
33-
/// try dbQueue.read { db in
37+
/// migrator.registerMigration("My migration") { db in
38+
/// // No schema source is enabled at this point.
3439
/// try db.withSchemaSource(MySchemaSource()) {
35-
/// ...
40+
/// // Here the provided schema source is in effect.
3641
/// }
3742
/// }
3843
/// ```
3944
///
45+
/// Take care that **a good migration is a migration that is never
46+
/// modified once it has shipped**: check
47+
/// <doc:Migrations#Good-Practices-for-Defining-Migrations>.
48+
///
4049
/// ## Topics
4150
///
4251
/// ### Customizing the Database Schema
@@ -54,16 +63,22 @@ public protocol DatabaseSchemaSource: Sendable {
5463
/// to specify that the view has no primary key. The default
5564
/// implementation returns nil.
5665
///
57-
/// In your implementation, make sure that:
66+
/// In your implementation, make sure that the returned columns define
67+
/// a genuine **primary key**:
68+
///
69+
/// - All columns exist in the provided view.
70+
/// - The set of columns reliably identifies and distinguishes between
71+
/// each individual row in the view.
72+
/// - No column contains NULL values.
5873
///
59-
/// - The returned columns exist in the database schema.
60-
/// - The returned columns identify unique rows.
61-
/// - The returned columns do not contain NULL values.
74+
/// It is a programmer error with undefined consequences to miss
75+
/// those requirements.
6276
///
6377
/// For example:
6478
///
6579
/// ```swift
66-
/// // A schema source that specifies that views have an "id" primary key.
80+
/// // A schema source that specifies that all views are identified by
81+
/// // their "id" column:
6782
/// struct MySchemaSource: DatabaseSchemaSource {
6883
/// func columnsForPrimaryKey(_ db: Database, inView view: DatabaseObjectID) {
6984
/// ["id"]
@@ -88,18 +103,32 @@ public protocol DatabaseSchemaSource: Sendable {
88103
/// }
89104
/// ```
90105
///
91-
/// When you are developing a library that accesses database files owned
92-
/// by the users of your library, then you you should allow the host
93-
/// application to deal with their own views. To do so, return nil for
94-
/// views that your library does not manage. When necessary, use the
95-
/// `db` argument in order to query the database schema.
106+
/// ### Schema Sources in Libraries
107+
///
108+
/// When you are developing a schema source for a library, some extra
109+
/// care is necessary whenever the database file is owned by the users
110+
/// of your library. Your users may define views for their own purposes,
111+
/// and your library knows nothing about the eventual primary key of
112+
/// those views.
113+
///
114+
/// In this case, make your schema source `public`, and have this method
115+
/// return `nil` for those unknow views. If needed, use the `db`
116+
/// argument and query the database schema with
117+
/// <doc:DatabaseSchemaIntrospection> methods.
118+
///
119+
/// For example:
96120
///
97121
/// ```swift
98-
/// struct MyLbrarySchemaSource: DatabaseSchemaSource {
99-
/// func columnsForPrimaryKey(_ db: Database, inView view: DatabaseObjectID) {
122+
/// // A well-behaved schema source defined by a library is public
123+
/// // and returns nil for unknown views.
124+
/// public struct MyLibrarySchemaSource: DatabaseSchemaSource {
125+
/// public func columnsForPrimaryKey(_ db: Database, inView view: DatabaseObjectID) {
100126
/// if view.name == "playerView" {
101127
/// // This is a view managed by my library:
102128
/// return ["id"]
129+
/// } else if try db.tableExists(view.name + "MyLibrary") {
130+
/// // This is a view managed by my library:
131+
/// return ["uuid"]
103132
/// } else {
104133
/// // Not a view managed by my library:
105134
/// // don't mess with user's schema
@@ -108,6 +137,28 @@ public protocol DatabaseSchemaSource: Sendable {
108137
/// }
109138
/// }
110139
/// ```
140+
///
141+
/// With such a setup, your user will be able to deal with their
142+
/// own views, by chaining your schema source with other ones
143+
/// (see ``DatabaseSchemaSource/then(_:)``):
144+
///
145+
/// ```swift
146+
/// // Application code
147+
/// import MyLibrary
148+
/// import GRDB
149+
///
150+
/// let myLibrarySchemaSource = MyLibrarySchemaSource()
151+
/// let customSchemaSource = TheirCustomSchemaSource()
152+
/// let schemaSource = myLibrarySchemaSource.then(customSchemaSource)
153+
///
154+
/// var config = Configuration()
155+
/// config.schemaSource = schemaSource
156+
/// let dbQueue = try DatabaseQueue(path: "...", configuration: config)
157+
/// ```
158+
///
159+
/// - Parameters:
160+
/// - db: A database connection.
161+
/// - view: The identifier of a database view.
111162
func columnsForPrimaryKey(
112163
_ db: Database,
113164
inView view: DatabaseObjectID
@@ -128,6 +179,18 @@ extension DatabaseSchemaSource {
128179
extension DatabaseSchemaSource {
129180
/// Returns a schema source that queries `other` when this source does
130181
/// not perform customization.
182+
///
183+
/// For example:
184+
///
185+
/// ```swift
186+
/// let schemaSource1 = SomeSchemaSource()
187+
/// let schemaSource2 = AnotherSchemaSource()
188+
/// let schemaSource = schemaSource1.then(schemaSource2)
189+
///
190+
/// var config = Configuration()
191+
/// config.schemaSource = schemaSource
192+
/// let dbQueue = try DatabaseQueue(path: "...", configuration: config)
193+
/// ```
131194
public func then(_ other: some DatabaseSchemaSource) -> some DatabaseSchemaSource {
132195
Chained2SchemaSource(first: self, second: other)
133196
}

0 commit comments

Comments
 (0)