Skip to content

Commit 0f6c3e3

Browse files
committed
Add documentation
1 parent a6a151b commit 0f6c3e3

File tree

6 files changed

+134
-24
lines changed

6 files changed

+134
-24
lines changed

Documentation/Index.md

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,37 @@ try db.transaction {
13711371

13721372
> _Note:_ Transactions run in a serial queue.
13731373

1374+
## Querying the Schema
1375+
1376+
We can obtain generic information about objects in the current schema with a `SchemaReader`:
1377+
1378+
```swift
1379+
let schema = db.schema
1380+
```
1381+
1382+
To query the data:
1383+
1384+
```swift
1385+
let indexes = try schema.objectDefinitions(type: .index)
1386+
let tables = try schema.objectDefinitions(type: .table)
1387+
let triggers = try schema.objectDefinitions(type: .trigger)
1388+
```
1389+
1390+
### Indexes and Columns
1391+
1392+
Specialized methods are available to get more detailed information:
1393+
1394+
```swift
1395+
let indexes = try schema.indexDefinitions("users")
1396+
let columns = try schema.columnDefinitions("users")
1397+
1398+
for index in indexes {
1399+
print("\(index.name) columns:\(index.columns))")
1400+
}
1401+
for column in columns {
1402+
print("\(column.name) pk:\(column.primaryKey) nullable: \(column.nullable)")
1403+
}
1404+
```
13741405

13751406
## Altering the Schema
13761407

@@ -1454,11 +1485,56 @@ tables](#creating-a-table).
14541485

14551486
### Renaming Columns
14561487

1457-
Added in SQLite 3.25.0, not exposed yet. [#1073](https://github.com/stephencelis/SQLite.swift/issues/1073)
1488+
We can rename columns with the help of the `SchemaChanger` class:
1489+
1490+
```swift
1491+
let schemaChanger = SchemaChanger(connection: db)
1492+
try schemaChanger.alter(table: "users") { table in
1493+
table.rename("old_name", to: "new_name")
1494+
}
1495+
```
14581496

14591497
### Dropping Columns
14601498

1461-
Added in SQLite 3.35.0, not exposed yet. [#1073](https://github.com/stephencelis/SQLite.swift/issues/1073)
1499+
```swift
1500+
let schemaChanger = SchemaChanger(connection: db)
1501+
try schemaChanger.alter(table: "users") { table in
1502+
table.drop("column")
1503+
}
1504+
```
1505+
1506+
These operations will work with all versions of SQLite and use modern SQL
1507+
operations such as `DROP COLUMN` when available.
1508+
1509+
### Adding Columns (SchemaChanger)
1510+
1511+
The `SchemaChanger` provides an alternative API to add new columns:
1512+
1513+
```swift
1514+
let newColumn = ColumnDefinition(
1515+
name: "new_text_column",
1516+
type: .TEXT,
1517+
nullable: true,
1518+
defaultValue: .stringLiteral("foo")
1519+
)
1520+
1521+
let schemaChanger = SchemaChanger(connection: db)
1522+
1523+
try schemaChanger.alter(table: "users") { table in
1524+
table.add(newColumn)
1525+
}
1526+
```
1527+
1528+
### Renaming/dropping Tables (SchemaChanger)
1529+
1530+
The `SchemaChanger` provides an alternative API to rename and drop tables:
1531+
1532+
```swift
1533+
let schemaChanger = SchemaChanger(connection: db)
1534+
1535+
try schemaChanger.rename(table: "users", to: "users_new")
1536+
try schemaChanger.drop(table: "emails")
1537+
```
14621538

14631539
### Indexes
14641540

@@ -1515,7 +1591,6 @@ try db.run(users.dropIndex(email, ifExists: true))
15151591
// DROP INDEX IF EXISTS "index_users_on_email"
15161592
```
15171593

1518-
15191594
### Dropping Tables
15201595

15211596
We can build
@@ -1535,7 +1610,6 @@ try db.run(users.drop(ifExists: true))
15351610
// DROP TABLE IF EXISTS "users"
15361611
```
15371612

1538-
15391613
### Migrations and Schema Versioning
15401614

15411615
You can use the convenience property on `Connection` to query and set the

Sources/SQLite/Schema/SchemaChanger.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public class SchemaChanger: CustomStringConvertible {
9999
operations.append(.addColumn(column))
100100
}
101101

102-
public func remove(_ column: String) {
102+
public func drop(_ column: String) {
103103
operations.append(.dropColumn(column))
104104
}
105105

Sources/SQLite/Schema/SchemaDefinitions.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ public struct ObjectDefinition: Equatable {
3131

3232
// SQL text that describes the object (NULL for the internal indexes)
3333
public let sql: String?
34+
35+
public var isInternal: Bool {
36+
name.starts(with: "sqlite_") || sql == nil
37+
}
3438
}
3539

3640
// https://sqlite.org/syntax/column-def.html

Sources/SQLite/Schema/SchemaReader.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public class SchemaReader {
5252
return ObjectDefinition(
5353
type: type,
5454
name: row[SchemaTable.nameColumn],
55-
tableName: row[SchemaTable.nameColumn],
55+
tableName: row[SchemaTable.tableNameColumn],
5656
rootpage: row[SchemaTable.rootPageColumn] ?? 0,
5757
sql: row[SchemaTable.sqlColumn]
5858
)

Tests/SQLiteTests/Schema/SchemaChangerTests.swift

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,41 @@ import XCTest
33

44
class SchemaChangerTests: SQLiteTestCase {
55
var schemaChanger: SchemaChanger!
6-
var schemaReader: SchemaReader!
6+
var schema: SchemaReader!
77

88
override func setUpWithError() throws {
99
try super.setUpWithError()
1010
try createUsersTable()
1111

1212
try insertUsers("bob")
1313

14-
schemaReader = SchemaReader(connection: db)
14+
schema = SchemaReader(connection: db)
1515
schemaChanger = SchemaChanger(connection: db)
1616
}
1717

1818
func test_empty_migration_does_not_change_column_definitions() throws {
19-
let previous = try schemaReader.columnDefinitions(table: "users")
19+
let previous = try schema.columnDefinitions(table: "users")
2020
try schemaChanger.alter(table: "users") { _ in
2121
}
22-
let current = try schemaReader.columnDefinitions(table: "users")
22+
let current = try schema.columnDefinitions(table: "users")
2323

2424
XCTAssertEqual(previous, current)
2525
}
2626

2727
func test_empty_migration_does_not_change_index_definitions() throws {
28-
let previous = try schemaReader.indexDefinitions(table: "users")
28+
let previous = try schema.indexDefinitions(table: "users")
2929
try schemaChanger.alter(table: "users") { _ in
3030
}
31-
let current = try schemaReader.indexDefinitions(table: "users")
31+
let current = try schema.indexDefinitions(table: "users")
3232

3333
XCTAssertEqual(previous, current)
3434
}
3535

3636
func test_empty_migration_does_not_change_foreign_key_definitions() throws {
37-
let previous = try schemaReader.foreignKeys(table: "users")
37+
let previous = try schema.foreignKeys(table: "users")
3838
try schemaChanger.alter(table: "users") { _ in
3939
}
40-
let current = try schemaReader.foreignKeys(table: "users")
40+
let current = try schema.foreignKeys(table: "users")
4141

4242
XCTAssertEqual(previous, current)
4343
}
@@ -51,21 +51,21 @@ class SchemaChangerTests: SQLiteTestCase {
5151
XCTAssertEqual(previous, current)
5252
}
5353

54-
func test_remove_column() throws {
54+
func test_drop_column() throws {
5555
try schemaChanger.alter(table: "users") { table in
56-
table.remove("age")
56+
table.drop("age")
5757
}
58-
let columns = try schemaReader.columnDefinitions(table: "users").map(\.name)
58+
let columns = try schema.columnDefinitions(table: "users").map(\.name)
5959
XCTAssertFalse(columns.contains("age"))
6060
}
6161

62-
func test_remove_column_legacy() throws {
62+
func test_drop_column_legacy() throws {
6363
schemaChanger = .init(connection: db, version: .init(major: 3, minor: 24)) // DROP COLUMN introduced in 3.35.0
6464

6565
try schemaChanger.alter(table: "users") { table in
66-
table.remove("age")
66+
table.drop("age")
6767
}
68-
let columns = try schemaReader.columnDefinitions(table: "users").map(\.name)
68+
let columns = try schema.columnDefinitions(table: "users").map(\.name)
6969
XCTAssertFalse(columns.contains("age"))
7070
}
7171

@@ -74,7 +74,7 @@ class SchemaChangerTests: SQLiteTestCase {
7474
table.rename("age", to: "age2")
7575
}
7676

77-
let columns = try schemaReader.columnDefinitions(table: "users").map(\.name)
77+
let columns = try schema.columnDefinitions(table: "users").map(\.name)
7878
XCTAssertFalse(columns.contains("age"))
7979
XCTAssertTrue(columns.contains("age2"))
8080
}
@@ -86,7 +86,7 @@ class SchemaChangerTests: SQLiteTestCase {
8686
table.rename("age", to: "age2")
8787
}
8888

89-
let columns = try schemaReader.columnDefinitions(table: "users").map(\.name)
89+
let columns = try schema.columnDefinitions(table: "users").map(\.name)
9090
XCTAssertFalse(columns.contains("age"))
9191
XCTAssertTrue(columns.contains("age2"))
9292
}
@@ -102,7 +102,7 @@ class SchemaChangerTests: SQLiteTestCase {
102102
table.add(newColumn)
103103
}
104104

105-
let columns = try schemaReader.columnDefinitions(table: "users")
105+
let columns = try schema.columnDefinitions(table: "users")
106106
XCTAssertTrue(columns.contains(newColumn))
107107

108108
XCTAssertEqual(try db.pluck(users.select(column))?[column], "foo")

Tests/SQLiteTests/Schema/SchemaReaderTests.swift

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ class SchemaReaderTests: SQLiteTestCase {
146146

147147
XCTAssertEqual(tables.map { table in [table.name, table.tableName, table.type.rawValue]}, [
148148
["users", "users", "table"],
149-
["sqlite_autoindex_users_1", "sqlite_autoindex_users_1", "index"]
149+
["sqlite_autoindex_users_1", "users", "index"]
150150
])
151151
}
152152

@@ -162,6 +162,38 @@ class SchemaReaderTests: SQLiteTestCase {
162162
])
163163
}
164164

165+
func test_objectDefinitions_indexes() throws {
166+
let emailIndex = users.createIndex(Expression<String>("email"), unique: false, ifNotExists: true)
167+
try db.run(emailIndex)
168+
169+
let indexes = try schemaReader.objectDefinitions(type: .index)
170+
.filter { !$0.isInternal }
171+
172+
XCTAssertEqual(indexes.map { index in [index.name, index.tableName, index.type.rawValue, index.sql]}, [
173+
["index_users_on_email",
174+
"users",
175+
"index",
176+
"CREATE INDEX \"index_users_on_email\" ON \"users\" (\"email\")"]
177+
])
178+
}
179+
180+
func test_objectDefinitions_triggers() throws {
181+
let trigger = """
182+
CREATE TRIGGER test_trigger
183+
AFTER INSERT ON users BEGIN
184+
UPDATE USERS SET name = "update" WHERE id = NEW.rowid;
185+
END;
186+
"""
187+
188+
try db.run(trigger)
189+
190+
let triggers = try schemaReader.objectDefinitions(type: .trigger)
191+
192+
XCTAssertEqual(triggers.map { trigger in [trigger.name, trigger.tableName, trigger.type.rawValue]}, [
193+
["test_trigger", "users", "trigger"]
194+
])
195+
}
196+
165197
func test_objectDefinitionsFilterByType() throws {
166198
let tables = try schemaReader.objectDefinitions(type: .table)
167199

0 commit comments

Comments
 (0)