Skip to content

Commit 09f730e

Browse files
committed
Update documentation
- Closes #1076 - Clarify BusyHandler usage (Closes #786) - Integrate playground into project
1 parent bbb7cd1 commit 09f730e

File tree

11 files changed

+167
-37
lines changed

11 files changed

+167
-37
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
* Support for database backup ([#919][])
55
* Support for custom SQL aggregates ([#881][])
6+
* Restore previous iteration behavior ([#1075][])
7+
* Fix compilation on Linux ([#1077][])
68

79
0.13.0 (22-08-2021), [diff][diff-0.13.0]
810
========================================
@@ -124,3 +126,5 @@
124126
[#866]: https://github.com/stephencelis/SQLite.swift/pull/866
125127
[#881]: https://github.com/stephencelis/SQLite.swift/pull/881
126128
[#919]: https://github.com/stephencelis/SQLite.swift/pull/919
129+
[#1075]: https://github.com/stephencelis/SQLite.swift/pull/1075
130+
[#1077]: https://github.com/stephencelis/SQLite.swift/issues/1077

Documentation/Index.md

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,6 @@ The [Swift Package Manager][] is a tool for managing the distribution of
7777
Swift code. It’s integrated with the Swift build system to automate the
7878
process of downloading, compiling, and linking dependencies.
7979

80-
It is the recommended approach for using SQLite.swift in OSX CLI
81-
applications.
82-
8380
1. Add the following to your `Package.swift` file:
8481

8582
```swift
@@ -342,16 +339,15 @@ execution and can be safely accessed across threads. Threads that open
342339
transactions and savepoints will block other threads from executing
343340
statements while the transaction is open.
344341

345-
If you maintain multiple connections for a single database, consider setting a timeout (in seconds) and/or a busy handler:
342+
If you maintain multiple connections for a single database, consider setting a timeout
343+
(in seconds) *or* a busy handler. There can only be one active at a time, so setting a busy
344+
handler will effectively override `busyTimeout`.
346345

347346
```swift
348-
db.busyTimeout = 5
347+
db.busyTimeout = 5 // error after 5 seconds (does multiple retries)
349348
350349
db.busyHandler({ tries in
351-
if tries >= 3 {
352-
return false
353-
}
354-
return true
350+
tries < 3 // error after 3 tries
355351
})
356352
```
357353

@@ -656,12 +652,13 @@ do {
656652
}
657653
```
658654

659-
Multiple rows can be inserted at once by similarily calling `insertMany` with an array of per-row [setters](#setters).
655+
Multiple rows can be inserted at once by similarily calling `insertMany` with an array of
656+
per-row [setters](#setters).
660657

661658
```swift
662659
do {
663-
let rowid = try db.run(users.insertMany([mail <- "[email protected]"], [email <- "[email protected]"]))
664-
print("inserted id: \(rowid)")
660+
let lastRowid = try db.run(users.insertMany([mail <- "[email protected]"], [email <- "[email protected]"]))
661+
print("last inserted id: \(lastRowid)")
665662
} catch {
666663
print("insertion failed: \(error)")
667664
}
@@ -799,6 +796,33 @@ for user in try db.prepare(users) {
799796
}
800797
```
801798

799+
Note that the iterator can throw *undeclared* database errors at any point during
800+
iteration:
801+
802+
```swift
803+
let query = try db.prepare(users)
804+
for user in query {
805+
// 💥 can throw an error here
806+
}
807+
````
808+
809+
#### Failable iteration
810+
811+
It is therefore recommended using the `RowIterator` API instead,
812+
which has explicit error handling:
813+
814+
```swift
815+
let rowIterator = try db.prepareRowIterator(users)
816+
for user in try Array(rowIterator) {
817+
print("id: \(user[id]), email: \(user[email])")
818+
}
819+
820+
/// or using `map()`
821+
let mapRowIterator = try db.prepareRowIterator(users)
822+
let userIds = try mapRowIterator.map { $0[id] }
823+
824+
```
825+
802826
### Plucking Rows
803827

804828
We can pluck the first row by passing a query to the `pluck` function on a
@@ -1285,7 +1309,6 @@ try db.run(users.addColumn(suffix))
12851309
// ALTER TABLE "users" ADD COLUMN "suffix" TEXT
12861310
```
12871311

1288-
12891312
#### Added Column Constraints
12901313

12911314
The `addColumn` function shares several of the same [`column` function
@@ -1337,6 +1360,13 @@ tables](#creating-a-table).
13371360
// ALTER TABLE "posts" ADD COLUMN "user_id" INTEGER REFERENCES "users" ("id")
13381361
```
13391362

1363+
### Renaming Columns
1364+
1365+
Added in SQLite 3.25.0, not exposed yet. [#1073](https://github.com/stephencelis/SQLite.swift/issues/1073)
1366+
1367+
### Dropping Columns
1368+
1369+
Added in SQLite 3.35.0, not exposed yet. [#1073](https://github.com/stephencelis/SQLite.swift/issues/1073)
13401370

13411371
### Indexes
13421372

@@ -1762,6 +1792,19 @@ for row in stmt.bind(kUTTypeImage) { /* ... */ }
17621792

17631793
[UTTypeConformsTo]: https://developer.apple.com/documentation/coreservices/1444079-uttypeconformsto
17641794

1795+
## Custom Aggregations
1796+
1797+
We can create custom aggregation functions by calling `createAggregation`:
1798+
1799+
```swift
1800+
let reduce: (String, [Binding?]) -> String = { (last, bindings) in
1801+
last + " " + (bindings.first as? String ?? "")
1802+
}
1803+
1804+
db.createAggregation("customConcat", initialValue: "", reduce: reduce, result: { $0 })
1805+
let result = db.prepare("SELECT customConcat(email) FROM users").scalar() as! String
1806+
```
1807+
17651808
## Custom Collations
17661809

17671810
We can create custom collating sequences by calling `createCollation` on a
@@ -1944,6 +1987,19 @@ using the following functions.
19441987
let count = try stmt.scalar() as! Int64
19451988
```
19461989

1990+
## Online Database Backup
1991+
1992+
To copy a database to another using the
1993+
[SQLite Online Backup API](https://sqlite.org/backup.html):
1994+
1995+
```swift
1996+
// creates an in-memory copy of db.sqlite
1997+
let db = try Connection("db.sqlite")
1998+
let target = try Connection(.inMemory)
1999+
2000+
let backup = try db.backup(usingConnection: target)
2001+
try backup.step()
2002+
```
19472003

19482004
## Logging
19492005

@@ -1955,6 +2011,14 @@ We can log SQL using the database’s `trace` function.
19552011
#endif
19562012
```
19572013

2014+
## Vacuum
2015+
2016+
To run the [vacuum](https://www.sqlite.org/lang_vacuum.html) command:
2017+
2018+
```swift
2019+
try db.vacuum()
2020+
```
2021+
19582022

19592023
[ROWID]: https://sqlite.org/lang_createtable.html#rowid
19602024
[SQLiteMigrationManager.swift]: https://github.com/garriguv/SQLiteMigrationManager.swift

Documentation/Planning.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ additions and Pull Requests, as well as to keep the Issues list clear of
66
enhancement requests so that bugs are more visible.
77

88
> ⚠ This document is currently not actively maintained. See
9-
> the [0.13.0 milestone](https://github.com/stephencelis/SQLite.swift/issues?q=is%3Aopen+is%3Aissue+milestone%3A0.13.0)
9+
> the [0.13.1 milestone](https://github.com/stephencelis/SQLite.swift/issues?q=is%3Aopen+is%3Aissue+milestone%3A0.13.1)
1010
> on Github for additional information about planned features for the next release.
1111
1212
## Roadmap

Documentation/Release.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SQLite.swift Release checklist
22

33
* [ ] Make sure current master branch has a green build
4+
* [ ] Make sure `SQLite.playground` runs without errors
45
* [ ] Make sure `CHANGELOG.md` is up-to-date
56
* [ ] Update the version number in `SQLite.swift.podspec`
67
* [ ] Run `pod lib lint` locally

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ Swift code.
146146
$ swift build
147147
```
148148

149+
See the [Tests/SPM](https://github.com/stephencelis/SQLite.swift/tree/master/Tests/SPM) folder for a small demo project which uses SPM.
150+
149151
[Swift Package Manager]: https://swift.org/package-manager
150152

151153
### Carthage
@@ -175,8 +177,7 @@ install SQLite.swift with Carthage:
175177
[CocoaPods][] is a dependency manager for Cocoa projects. To install
176178
SQLite.swift with CocoaPods:
177179

178-
1. Make sure CocoaPods is [installed][CocoaPods Installation]. (SQLite.swift
179-
requires version 1.6.1 or greater.)
180+
1. Make sure CocoaPods is [installed][CocoaPods Installation].
180181

181182
```sh
182183
# Using the default Ruby install will require you to use sudo when
@@ -266,7 +267,8 @@ These projects enhance or use SQLite.swift:
266267

267268
- [SQLiteMigrationManager.swift][] (inspired by
268269
[FMDBMigrationManager][])
269-
- [Delta: Math helper](https://apps.apple.com/app/delta-math-helper/id1436506800) (see [Delta/Utils/Database.swift](https://github.com/GroupeMINASTE/Delta-iOS/blob/master/Delta/Utils/Database.swift) for production implementation example)
270+
- [Delta: Math helper](https://apps.apple.com/app/delta-math-helper/id1436506800)
271+
(see [Delta/Utils/Database.swift](https://github.com/GroupeMINASTE/Delta-iOS/blob/master/Delta/Utils/Database.swift) for production implementation example)
270272

271273

272274
## Alternatives
@@ -278,7 +280,6 @@ Looking for something else? Try another Swift wrapper (or [FMDB][]):
278280
- [SQLiteDB](https://github.com/FahimF/SQLiteDB)
279281
- [Squeal](https://github.com/nerdyc/Squeal)
280282
- [SwiftData](https://github.com/ryanfowler/SwiftData)
281-
- [SwiftSQLite](https://github.com/chrismsimpson/SwiftSQLite)
282283

283284
[Swift]: https://swift.org/
284285
[SQLite3]: https://www.sqlite.org

SQLite.playground/Contents.swift

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,93 @@
11
import SQLite
22

3-
let db = try! Connection()
3+
/// Create an in-memory database
4+
let db = try Connection(.inMemory)
45

6+
/// enable statement logging
57
db.trace { print($0) }
68

9+
/// define a "users" table with some fields
710
let users = Table("users")
811

912
let id = Expression<Int64>("id")
10-
let email = Expression<String>("email")
11-
let name = Expression<String?>("name")
13+
let email = Expression<String>("email") // non-null
14+
let name = Expression<String?>("name") // nullable
1215

13-
try! db.run(users.create { t in
16+
/// prepare the query
17+
let statement = users.create { t in
1418
t.column(id, primaryKey: true)
1519
t.column(email, unique: true, check: email.like("%@%"))
1620
t.column(name)
17-
})
21+
}
22+
23+
/// …and run it
24+
try db.run(statement)
25+
26+
/// insert "alice"
27+
let rowid = try db.run(users.insert(email <- "[email protected]"))
28+
29+
/// insert multiple rows using `insertMany`
30+
let lastRowid = try db.run(users.insertMany([
31+
[email <- "[email protected]"],
32+
[email <- "[email protected]"]
33+
]))
34+
35+
36+
let query = try db.prepare(users)
37+
for user in query {
38+
print("id: \(user[id]), email: \(user[email])")
39+
}
1840

19-
let rowid = try! db.run(users.insert(email <- "[email protected]"))
20-
let alice = users.filter(id == rowid)
41+
// re-requery just rowid of Alice
42+
let alice = try db.prepare(users.filter(id == rowid))
43+
for user in alice {
44+
print("id: \(user[id]), email: \(user[email])")
45+
}
2146

22-
for user in try! db.prepare(users) {
47+
/// using the `RowIterator` API
48+
let rowIterator = try db.prepareRowIterator(users)
49+
for user in try Array(rowIterator) {
2350
print("id: \(user[id]), email: \(user[email])")
2451
}
2552

53+
/// also with `map()`
54+
let mapRowIterator = try db.prepareRowIterator(users)
55+
let userIds = try mapRowIterator.map { $0[id] }
56+
57+
/// define a virtual tabe for the FTS index
2658
let emails = VirtualTable("emails")
2759

28-
let subject = Expression<String?>("subject")
60+
let subject = Expression<String>("subject")
2961
let body = Expression<String?>("body")
3062

31-
try! db.run(emails.create(.FTS4(subject, body)))
63+
/// create the index
64+
try db.run(emails.create(.FTS5(
65+
FTS5Config()
66+
.column(subject)
67+
.column(body)
68+
)))
3269

33-
try! db.run(emails.insert(
70+
/// populate with data
71+
try db.run(emails.insert(
3472
subject <- "Hello, world!",
3573
body <- "This is a hello world message."
3674
))
3775

38-
let row = try! db.pluck(emails.match("hello"))
76+
/// run a query
77+
let ftsQuery = try db.prepare(emails.match("hello"))
3978

40-
let query = try! db.prepare(emails.match("hello"))
41-
for row in query {
79+
for row in ftsQuery {
4280
print(row[subject])
4381
}
82+
83+
/// custom aggregations
84+
let reduce: (String, [Binding?]) -> String = { (last, bindings) in
85+
last + " " + (bindings.first as? String ?? "")
86+
}
87+
88+
db.createAggregation("customConcat",
89+
initialValue: "users:",
90+
reduce: reduce,
91+
result: { $0 })
92+
let result = db.prepare("SELECT customConcat(email) FROM users").scalar() as! String
93+
print(result)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<playground version='5.0' target-platform='osx' display-mode='raw'>
2+
<playground version='5.0' target-platform='macos' display-mode='rendered' buildActiveScheme='true' importAppTypes='true'>
33
<timeline fileName='timeline.xctimeline'/>
44
</playground>

SQLite.playground/playground.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

SQLite.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@
256256
19A17E723300E5ED3771DCB5 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
257257
19A17EA3A313F129011B3FA0 /* Release.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Release.md; sourceTree = "<group>"; };
258258
3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
259+
3D3C3CCB26E5568800759140 /* SQLite.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = SQLite.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
259260
3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
260261
3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../SQLiteObjc/include/SQLiteObjc.h; sourceTree = "<group>"; };
261262
49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
@@ -386,6 +387,7 @@
386387
children = (
387388
EE247AD51C3F04ED00AE3E12 /* SQLite */,
388389
EE247AE11C3F04ED00AE3E12 /* SQLiteTests */,
390+
3D3C3CCB26E5568800759140 /* SQLite.playground */,
389391
EE247B8A1C3F81D000AE3E12 /* Metadata */,
390392
EE247AD41C3F04ED00AE3E12 /* Products */,
391393
3D67B3E41DB2469200A4F4C6 /* Frameworks */,

Sources/SQLite/Core/Connection+Aggregation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ extension Connection {
9090
register(functionName, argc: argc, value: box)
9191
}
9292

93-
func createAggregation<T: AnyObject>(
93+
public func createAggregation<T: AnyObject>(
9494
_ aggregate: String,
9595
argumentCount: UInt? = nil,
9696
deterministic: Bool = false,
@@ -122,7 +122,7 @@ extension Connection {
122122
createAggregation(aggregate, step: step, final: final, state: state)
123123
}
124124

125-
func createAggregation<T>(
125+
public func createAggregation<T>(
126126
_ aggregate: String,
127127
argumentCount: UInt? = nil,
128128
deterministic: Bool = false,

0 commit comments

Comments
 (0)