DatabaseReader
public protocol DatabaseReader : AnyObject
The protocol for all types that can fetch values from a database.
It is adopted by DatabaseQueue and DatabasePool.
The protocol comes with isolation guarantees that describe the behavior of adopting types in a multithreaded application.
Types that adopt the protocol can provide in practice stronger guarantees. For example, DatabaseQueue provides a stronger isolation level than DatabasePool.
Warning: Isolation guarantees stand as long as there is no external connection to the database. Should you have to cope with external connections, protect yourself with transactions, and be ready to setup a busy handler.
-
The database configuration
Declaration
Swift
var configuration: Configuration { get }
-
Synchronously executes a read-only block that takes a database connection, and returns its result.
Guarantee 1: the block argument is isolated. Eventual concurrent database updates are not visible inside the block:
try reader.read { db in // Those two values are guaranteed to be equal, even if the // `player` table is modified between the two requests: let count1 = try Player.fetchCount(db) let count2 = try Player.fetchCount(db) } try reader.read { db in // Now this value may be different: let count = try Player.fetchCount(db) }
Guarantee 2: Starting iOS 8.2, OSX 10.10, and with custom SQLite builds and SQLCipher, attempts to write in the database throw a DatabaseError whose resultCode is
SQLITE_READONLY
.Throws
The error thrown by the block, or any DatabaseError that would happen while establishing the read access to the database.Declaration
Swift
func read<T>(_ block: (Database) throws -> T) throws -> T
Parameters
block
A block that accesses the database.
-
Asynchronously executes a read-only block that takes a database connection.
Guarantee 1: the block argument is isolated. Eventual concurrent database updates are not visible inside the block:
try reader.asyncRead { result in do ( let db = try result.get() // Those two values are guaranteed to be equal, even if the // `player` table is modified between the two requests: let count1 = try Player.fetchCount(db) let count2 = try Player.fetchCount(db) } catch { // handle error } }
Guarantee 2: Starting iOS 8.2, OSX 10.10, and with custom SQLite builds and SQLCipher, attempts to write in the database throw a DatabaseError whose resultCode is
SQLITE_READONLY
.Declaration
Swift
func asyncRead(_ block: @escaping (Result<Database, Error>) -> Void)
Parameters
block
A block that accesses the database.
-
Synchronously executes a read-only block that takes a database connection, and returns its result.
The two guarantees of the safe
read
method are lifted:The block argument is not isolated: eventual concurrent database updates are visible inside the block:
try reader.unsafeRead { db in // Those two values may be different because some other thread // may have inserted or deleted a player between the two requests: let count1 = try Player.fetchCount(db) let count2 = try Player.fetchCount(db) }
Cursor iterations are isolated, though:
try reader.unsafeRead { db in // No concurrent update can mess with this iteration: let rows = try Row.fetchCursor(db, sql: "SELECT ...") while let row = try rows.next() { ... } }
The block argument is not prevented from writing (DatabaseQueue, in particular, will accept database modifications in
unsafeRead
).Throws
The error thrown by the block, or any DatabaseError that would happen while establishing the read access to the database.Declaration
Swift
func unsafeRead<T>(_ block: (Database) throws -> T) throws -> T
Parameters
block
A block that accesses the database.
-
Synchronously executes a block that takes a database connection, and returns its result.
The two guarantees of the safe
read
method are lifted:The block argument is not isolated: eventual concurrent database updates are visible inside the block:
try reader.unsafeReentrantRead { db in // Those two values may be different because some other thread // may have inserted or deleted a player between the two requests: let count1 = try Player.fetchCount(db) let count2 = try Player.fetchCount(db) }
Cursor iterations are isolated, though:
try reader.unsafeReentrantRead { db in // No concurrent update can mess with this iteration: let rows = try Row.fetchCursor(db, sql: "SELECT ...") while let row = try rows.next() { ... } }
The block argument is not prevented from writing (DatabaseQueue, in particular, will accept database modifications in
unsafeReentrantRead
).Throws
The error thrown by the block, or any DatabaseError that would happen while establishing the read access to the database.This method is reentrant. It should be avoided because it fosters dangerous concurrency practices.
Declaration
Swift
func unsafeReentrantRead<T>(_ block: (Database) throws -> T) throws -> T
Parameters
block
A block that accesses the database.
-
Add or redefine an SQL function.
let fn = DatabaseFunction("succ", argumentCount: 1) { dbValues in guard let int = Int.fromDatabaseValue(dbValues[0]) else { return nil } return int + 1 } reader.add(function: fn) try reader.read { db in try Int.fetchOne(db, sql: "SELECT succ(1)")! // 2 }
Declaration
Swift
func add(function: DatabaseFunction)
-
Remove an SQL function.
Declaration
Swift
func remove(function: DatabaseFunction)
-
Add or redefine a collation.
let collation = DatabaseCollation("localized_standard") { (string1, string2) in return (string1 as NSString).localizedStandardCompare(string2) } reader.add(collation: collation) try reader.execute(sql: "SELECT * FROM file ORDER BY name COLLATE localized_standard")
Declaration
Swift
func add(collation: DatabaseCollation)
-
Remove a collation.
Declaration
Swift
func remove(collation: DatabaseCollation)
-
Starts a value observation.
You should use the
ValueObservation.start(in:onError:onChange:)
method instead.Declaration
Swift
func add<Reducer>(observation: ValueObservation<Reducer>, onError: @escaping (Error) -> Void, onChange: @escaping (Reducer.Value) -> Void) -> TransactionObserver where Reducer : ValueReducer
Parameters
observation
the stared observation
onError
a closure that is provided by eventual errors that happen during observation
onChange
a closure that is provided fresh values
Return Value
a TransactionObserver
-
Remove a transaction observer.
Declaration
Swift
func remove(transactionObserver: TransactionObserver)
-
backup(to:)
Extension methodCopies the database contents into another database.
The
backup
method blocks the current thread until the destination database contains the same contents as the source database.When the source is a DatabasePool, concurrent writes can happen during the backup. Those writes may, or may not, be reflected in the backup, but they won’t trigger any error.
Declaration
Swift
public func backup(to writer: DatabaseWriter) throws