Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e14afe3
SharedDatabaseSnapshot experiment
groue Sep 19, 2019
a242f86
SharedDatabaseSnapshot WIP
groue Sep 26, 2019
5ac8a27
First passing tests for SharedDatabaseSnapshot
groue Sep 26, 2019
8a9963c
SharedDatabaseSnapshot: same creation API as DatabaseSnapshot
groue Sep 26, 2019
138c132
Need more SharedDatabaseSnapshot tests
groue Sep 26, 2019
c97ff9e
Snapshot checkpointing experiments
groue Sep 26, 2019
7935517
Require SQLITE_ENABLE_SNAPSHOT for all things snapshots
groue Sep 26, 2019
a9fa42c
WAL Snapshot and checkpoint tests
groue Sep 27, 2019
c730a67
Database.currentSnapshot
groue Sep 27, 2019
1c67dec
SharedDatabaseSnapshot schema cache cleanup
groue Sep 27, 2019
da24c74
DatabasePool.unsafeReentrantRead gotcha
groue Sep 27, 2019
6c2b02b
Database.Snapshot
groue Sep 27, 2019
9a452e0
Better snapshot vs. checkpoint tests
groue Sep 27, 2019
674282c
Prevent checkpointing if there are snapshots
groue Sep 28, 2019
a64e810
unsafeReentrantRead: prevent snapshot misuses
groue Sep 28, 2019
5d26af0
Documentation
groue Sep 28, 2019
b841f38
Add a safety savepoint when creating a WAL db
groue Sep 28, 2019
226be5e
Prevent automatic checkpoints from invalidating snapshots
groue Sep 28, 2019
0a42249
Expose SQLITE_ENABLE_SNAPSHOT C functiions
groue Sep 28, 2019
f7497e7
Remove SQLITE_ENABLE_SNAPSHOT from GRDBB targets
groue Sep 28, 2019
9d0bebb
Snapshot cleanup and TODO
groue Sep 28, 2019
6ab5f48
Fix for Xcode 10.3
groue Sep 29, 2019
ef3d682
Fix for Xcode 10.1
groue Sep 30, 2019
764e47b
LockedBox locks a value without changing dispatch queues
groue Sep 30, 2019
9dc95df
Failing test: testCreateSharedSnapshotFromTransactionObserver
groue Sep 30, 2019
05a4114
Fix testCreateSharedSnapshotFromTransactionObserver
groue Sep 30, 2019
56daa79
Cleanup DatabaseReaderTests
groue Sep 30, 2019
0f76405
More DatabaseReaderTests
groue Sep 30, 2019
f6fe895
Test that SharedDatabaseSnapshot can perform concurrent read
groue Sep 30, 2019
8732f42
Fix SQLCipher backup test
groue Oct 1, 2019
3520597
Configuration.fragileSnaredSnapshots
groue Oct 1, 2019
17a75a9
More snapshot tests
groue Oct 1, 2019
497dc2d
More snapshot tests
groue Oct 1, 2019
3108805
Rename SharedDatabaseSnapshot to DatabaseHistoricalSnapshot
groue Oct 1, 2019
21968ab
Test enabling SQLITE_ENABLE_SNAPSHOT and SQLITE_ENABLE_PREUPDATE_HOOK…
groue Oct 1, 2019
c3b4c70
Makefile: tests SQLITE_ENABLE_FTS5, SQLITE_ENABLE_SNAPSHOT, SQLITE_EN…
groue Oct 1, 2019
818d707
Add sanity checks
groue Oct 2, 2019
20416f6
DatabaseHistoricalSnapshot documentation WIP
groue Oct 2, 2019
d0d74de
Merge branch 'development' into dev/SharedDatabaseSnapshot
groue Oct 12, 2019
6992c29
Rename Configuration.fragileHistoricalSnapshots to historicalSnapshot…
groue Oct 12, 2019
2f90c13
historicalSnapshotsPreventAutomatickCheckpointing does not prevent ex…
groue Oct 12, 2019
8bb7086
Fix wrong conflict handling
groue Oct 13, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions GRDB.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,10 @@
56BB6EA91D3009B100A1CA52 /* SchedulingWatchdog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BB6EA81D3009B100A1CA52 /* SchedulingWatchdog.swift */; };
56BB6EAC1D3009B100A1CA52 /* SchedulingWatchdog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BB6EA81D3009B100A1CA52 /* SchedulingWatchdog.swift */; };
56C3F7561CF9F12400F6A361 /* DatabaseSavepointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56C3F7521CF9F12400F6A361 /* DatabaseSavepointTests.swift */; };
56CAD2362333906F0057559A /* DatabaseHistoricalSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56CAD2352333906F0057559A /* DatabaseHistoricalSnapshot.swift */; };
56CAD2372333906F0057559A /* DatabaseHistoricalSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56CAD2352333906F0057559A /* DatabaseHistoricalSnapshot.swift */; };
56CAD2382333906F0057559A /* DatabaseHistoricalSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56CAD2352333906F0057559A /* DatabaseHistoricalSnapshot.swift */; };
56CAD2392333906F0057559A /* DatabaseHistoricalSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56CAD2352333906F0057559A /* DatabaseHistoricalSnapshot.swift */; };
56CC922C201DFFB900CB597E /* DropWhileCursorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56CC922B201DFFB900CB597E /* DropWhileCursorTests.swift */; };
56CC922D201DFFB900CB597E /* DropWhileCursorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56CC922B201DFFB900CB597E /* DropWhileCursorTests.swift */; };
56CC9243201E034D00CB597E /* PrefixWhileCursorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56CC9242201E034D00CB597E /* PrefixWhileCursorTests.swift */; };
Expand Down Expand Up @@ -803,6 +807,9 @@
56DF001C228DDBA300D611F3 /* AssociationPrefetchingRowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56DF0019228DDBA200D611F3 /* AssociationPrefetchingRowTests.swift */; };
56DF001D228DDBA300D611F3 /* AssociationPrefetchingCodableRecordTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56DF001A228DDBA300D611F3 /* AssociationPrefetchingCodableRecordTests.swift */; };
56DF001E228DDBA300D611F3 /* AssociationPrefetchingCodableRecordTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56DF001A228DDBA300D611F3 /* AssociationPrefetchingCodableRecordTests.swift */; };
56E58283233CE4D900F908F9 /* DatabaseHistoricalSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56E5827C233CE4D900F908F9 /* DatabaseHistoricalSnapshotTests.swift */; };
56E58284233CE4D900F908F9 /* DatabaseHistoricalSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56E5827C233CE4D900F908F9 /* DatabaseHistoricalSnapshotTests.swift */; };
56E58285233CE4D900F908F9 /* DatabaseHistoricalSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56E5827C233CE4D900F908F9 /* DatabaseHistoricalSnapshotTests.swift */; };
56E5D7D41B4D3FEE00430942 /* GRDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 56E5D7CA1B4D3FED00430942 /* GRDB.framework */; };
56E5D7FE1B4D422E00430942 /* GRDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC3773F319C8CBB3004FCF85 /* GRDB.framework */; };
56E5D8041B4D424400430942 /* GRDBTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623E0901B4AFACC00B20B7F /* GRDBTestCase.swift */; };
Expand Down Expand Up @@ -1609,6 +1616,7 @@
56C494401ED7255500CC72AF /* GRDBDeploymentTarget.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = GRDBDeploymentTarget.xcconfig; sourceTree = "<group>"; };
56CA21FE1BB414FE009A04C5 /* SQLite.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SQLite.xcodeproj; path = SQLite.swift/SQLite.xcodeproj; sourceTree = "<group>"; };
56CA22211BB41565009A04C5 /* PerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceTests.swift; sourceTree = "<group>"; };
56CAD2352333906F0057559A /* DatabaseHistoricalSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseHistoricalSnapshot.swift; sourceTree = "<group>"; };
56CC922B201DFFB900CB597E /* DropWhileCursorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropWhileCursorTests.swift; sourceTree = "<group>"; };
56CC9242201E034D00CB597E /* PrefixWhileCursorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefixWhileCursorTests.swift; sourceTree = "<group>"; };
56CC9245201E058000CB597E /* DropFirstCursorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropFirstCursorTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1643,6 +1651,7 @@
56DE7B361C42BBBB00861EB8 /* PerformanceCoreDataTests.sqlite */ = {isa = PBXFileReference; lastKnownFileType = file; path = PerformanceCoreDataTests.sqlite; sourceTree = "<group>"; };
56DF0019228DDBA200D611F3 /* AssociationPrefetchingRowTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationPrefetchingRowTests.swift; sourceTree = "<group>"; };
56DF001A228DDBA300D611F3 /* AssociationPrefetchingCodableRecordTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationPrefetchingCodableRecordTests.swift; sourceTree = "<group>"; };
56E5827C233CE4D900F908F9 /* DatabaseHistoricalSnapshotTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseHistoricalSnapshotTests.swift; sourceTree = "<group>"; };
56E5D7CA1B4D3FED00430942 /* GRDB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GRDB.framework; sourceTree = BUILT_PRODUCTS_DIR; };
56E5D7D31B4D3FEE00430942 /* GRDBiOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GRDBiOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
56E5D7F91B4D422D00430942 /* GRDBOSXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GRDBOSXTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -2209,6 +2218,7 @@
563DE4EC231A91E2005081B7 /* DatabaseConfigurationTests.swift */,
56A238161B9C74A90082EB20 /* DatabaseErrorTests.swift */,
560C97C61BFD0B8400BF8471 /* DatabaseFunctionTests.swift */,
56E5827C233CE4D900F908F9 /* DatabaseHistoricalSnapshotTests.swift */,
567DAF341EAB789800FC0928 /* DatabaseLogErrorTests.swift */,
560A37A91C90084600949E71 /* DatabasePool */,
563363BB1C93FD32000BE133 /* DatabaseQueue */,
Expand Down Expand Up @@ -2309,6 +2319,7 @@
566B91121FA4C3F50012D5B0 /* DatabaseCollation.swift */,
56A238731B9C75030082EB20 /* DatabaseError.swift */,
564F9C2C1F075DD200877A00 /* DatabaseFunction.swift */,
56CAD2352333906F0057559A /* DatabaseHistoricalSnapshot.swift */,
560A37A31C8F625000949E71 /* DatabasePool.swift */,
56A238741B9C75030082EB20 /* DatabaseQueue.swift */,
563363BF1C942C04000BE133 /* DatabaseReader.swift */,
Expand Down Expand Up @@ -3183,6 +3194,7 @@
5653EB0E20944C7C00F46237 /* HasManyAssociation.swift in Sources */,
565490DA1D5AE252005622CB /* QueryInterfaceRequest.swift in Sources */,
565490CA1D5AE252005622CB /* DatabaseDateComponents.swift in Sources */,
56CAD2382333906F0057559A /* DatabaseHistoricalSnapshot.swift in Sources */,
565490D01D5AE252005622CB /* NSString.swift in Sources */,
565490DF1D5AE252005622CB /* TableDefinition.swift in Sources */,
565490B71D5AE236005622CB /* Database.swift in Sources */,
Expand Down Expand Up @@ -3340,6 +3352,7 @@
569531201C907A8C00CF1A2B /* DatabaseSchemaCache.swift in Sources */,
5605F15E1C672E4000235C62 /* DatabaseDateComponents.swift in Sources */,
5695962A222C462D002CB7C9 /* HasManyThroughAssociation.swift in Sources */,
56CAD2372333906F0057559A /* DatabaseHistoricalSnapshot.swift in Sources */,
563363C11C942C04000BE133 /* DatabaseReader.swift in Sources */,
5659F4A31EA8D997004A4992 /* DatabaseResult.swift in Sources */,
56B964BC1DA51D0A0002DA19 /* FTS5Pattern.swift in Sources */,
Expand Down Expand Up @@ -3519,6 +3532,7 @@
562393521DEDFEFB00A6B01F /* EnumeratedCursorTests.swift in Sources */,
5653EAE520944B4F00F46237 /* AssociationParallelRowScopesTests.swift in Sources */,
56A4CDB41D4234B200B1A9B9 /* SQLExpressionLiteralTests.swift in Sources */,
56E58284233CE4D900F908F9 /* DatabaseHistoricalSnapshotTests.swift in Sources */,
56CC9247201E058100CB597E /* DropFirstCursorTests.swift in Sources */,
56915783231BF28B00E1D237 /* PoolTests.swift in Sources */,
56E8CE0E1BB4FA5600828BEC /* DatabaseValueConvertibleFetchTests.swift in Sources */,
Expand Down Expand Up @@ -3721,6 +3735,7 @@
56D496B21D8133CE008276D7 /* DatabaseQueueInMemoryTests.swift in Sources */,
562393601DEE06D300A6B01F /* CursorTests.swift in Sources */,
5653EAE420944B4F00F46237 /* AssociationParallelRowScopesTests.swift in Sources */,
56E58283233CE4D900F908F9 /* DatabaseHistoricalSnapshotTests.swift in Sources */,
56D4968F1D81316E008276D7 /* RowFromDictionaryTests.swift in Sources */,
56915782231BF28B00E1D237 /* PoolTests.swift in Sources */,
56D4968E1D81316E008276D7 /* RowCopiedFromStatementTests.swift in Sources */,
Expand Down Expand Up @@ -3877,6 +3892,7 @@
AAA4DCC3230F1E0600C74B15 /* DatabaseSchemaCache.swift in Sources */,
AAA4DCC4230F1E0600C74B15 /* DatabaseDateComponents.swift in Sources */,
AAA4DCC5230F1E0600C74B15 /* HasManyThroughAssociation.swift in Sources */,
56CAD2392333906F0057559A /* DatabaseHistoricalSnapshot.swift in Sources */,
AAA4DCC6230F1E0600C74B15 /* DatabaseReader.swift in Sources */,
AAA4DCC7230F1E0600C74B15 /* DatabaseResult.swift in Sources */,
AAA4DCC8230F1E0600C74B15 /* FTS5Pattern.swift in Sources */,
Expand Down Expand Up @@ -4056,6 +4072,7 @@
AAA4DD84230F262000C74B15 /* EnumeratedCursorTests.swift in Sources */,
AAA4DD85230F262000C74B15 /* AssociationParallelRowScopesTests.swift in Sources */,
AAA4DD86230F262000C74B15 /* SQLExpressionLiteralTests.swift in Sources */,
56E58285233CE4D900F908F9 /* DatabaseHistoricalSnapshotTests.swift in Sources */,
AAA4DD87230F262000C74B15 /* DropFirstCursorTests.swift in Sources */,
56915784231BF28B00E1D237 /* PoolTests.swift in Sources */,
AAA4DD88230F262000C74B15 /* DatabaseValueConvertibleFetchTests.swift in Sources */,
Expand Down Expand Up @@ -4212,6 +4229,7 @@
5674A6EB1F307F0E0095F066 /* DatabaseValueConvertible+Encodable.swift in Sources */,
56D91AA92205F2F100770D8D /* DatabasePromise.swift in Sources */,
56959629222C462D002CB7C9 /* HasManyThroughAssociation.swift in Sources */,
56CAD2362333906F0057559A /* DatabaseHistoricalSnapshot.swift in Sources */,
560A37A41C8F625000949E71 /* DatabasePool.swift in Sources */,
56B7F43A1BEB42D500E39BBF /* Migration.swift in Sources */,
5613ED3521A95A5C00DC7A68 /* Map.swift in Sources */,
Expand Down
22 changes: 22 additions & 0 deletions GRDB/Core/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,28 @@ public struct Configuration {
/// Default: 5
public var maximumReaderCount: Int = 5

#if SQLITE_ENABLE_SNAPSHOT
/// [WAL checkpoints](https://www.sqlite.org/wal.html#checkpointing)
/// invalidate historical snapshots created by
/// `DatabasePool.makeHistoricalSnapshot()`. Any attempt to read from an
/// invalidated snapshot throws an error.
///
/// When this flag is false, automatic SQLite WAL checkpoints are run
/// independently of existing historical snapshots.
///
/// When this flag is true, automatic SQLite WAL checkpoints are not run if
/// there exist historical snapshots. Checkpoints can still be performed
/// explicitely, or by external database connections. You must make sure
/// that checkpoings are performed regularly, or the WAL file will grow
/// without bounds. If you want historical snapshots to remain valid for
/// there all life time, you must make sure that absolutely no checkpoint is
/// performed, explicitely, or automatically by an external connection,
/// regardless of this setting.
///
/// Default: false.
public var historicalSnapshotsPreventAutomatickCheckpointing = false
#endif

/// The quality of service class for the work performed by the database.
///
/// The quality of service is ignored if you supply a target queue.
Expand Down
69 changes: 68 additions & 1 deletion GRDB/Core/Database.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public typealias SQLiteConnection = OpaquePointer
/// A raw SQLite function argument.
typealias SQLiteValue = OpaquePointer

#if SQLITE_ENABLE_SNAPSHOT
/// A raw SQLite snapshot
typealias SQLiteSnapshot = UnsafeMutablePointer<sqlite3_snapshot>
#endif

let SQLITE_TRANSIENT = unsafeBitCast(OpaquePointer(bitPattern: -1), to: sqlite3_destructor_type.self)

/// A Database connection.
Expand Down Expand Up @@ -155,7 +160,10 @@ public final class Database {
private var collations = Set<DatabaseCollation>()
private var _readOnlyDepth = 0 // Modify with beginReadOnly/endReadOnly
private var isClosed: Bool = false

#if SQLITE_ENABLE_SNAPSHOT
var currentSnapshot: Snapshot?
#endif

// MARK: - Initializer

init(path: String, configuration: Configuration, schemaCache: DatabaseSchemaCache) throws {
Expand Down Expand Up @@ -637,6 +645,24 @@ extension Database {
}
}

#if SQLITE_ENABLE_SNAPSHOT
extension Database {

// MARK: - Snapshots

func openSnapshot(_ snapshot: Snapshot) throws {
// TODO: carefully handle errors (https://www.sqlite.org/c3ref/snapshot_open.html)
let code = sqlite3_snapshot_open(sqliteConnection, "main", snapshot.sqliteSnapshot)
guard code == SQLITE_OK else {
throw DatabaseError(resultCode: code, message: lastErrorMessage)
}

// Reset in didEndTransaction
currentSnapshot = snapshot
}
}
#endif

extension Database {
// MARK: - Transactions & Savepoint

Expand Down Expand Up @@ -900,6 +926,12 @@ extension Database {
try execute(sql: "COMMIT TRANSACTION")
assert(!isInsideTransaction)
}

func didEndTransaction() {
#if SQLITE_ENABLE_SNAPSHOT
currentSnapshot = nil
#endif
}
}

extension Database {
Expand Down Expand Up @@ -1181,6 +1213,41 @@ extension Database {
/// log function that takes an error message.
public typealias LogErrorFunction = (_ resultCode: ResultCode, _ message: String) -> Void

#if SQLITE_ENABLE_SNAPSHOT
/// A managed SQLite snapshot
class Snapshot: Comparable {
let sqliteSnapshot: SQLiteSnapshot

init(from db: Database) throws {
var sqliteSnapshot: SQLiteSnapshot?
let code = withUnsafeMutablePointer(to: &sqliteSnapshot) {
sqlite3_snapshot_get(db.sqliteConnection, "main", $0)
}
guard code == SQLITE_OK else {
throw DatabaseError(resultCode: code, message: db.lastErrorMessage)
}
if let sqliteSnapshot = sqliteSnapshot {
self.sqliteSnapshot = sqliteSnapshot
} else {
throw DatabaseError(resultCode: .SQLITE_INTERNAL) // WTF SQLite?
}
}

deinit {
sqlite3_snapshot_free(sqliteSnapshot)
}

static func == (lhs: Snapshot, rhs: Snapshot) -> Bool {
return sqlite3_snapshot_cmp(lhs.sqliteSnapshot, rhs.sqliteSnapshot) == 0
}

/// True iff lhs is older than rhs
static func < (lhs: Database.Snapshot, rhs: Database.Snapshot) -> Bool {
return sqlite3_snapshot_cmp(lhs.sqliteSnapshot, rhs.sqliteSnapshot) < 0
}
}
#endif

/// The end of a transaction: Commit, or Rollback
public enum TransactionCompletion {
case commit
Expand Down
118 changes: 118 additions & 0 deletions GRDB/Core/DatabaseHistoricalSnapshot.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#if SQLITE_ENABLE_SNAPSHOT
#if SWIFT_PACKAGE
import CSQLite
#elseif GRDBCIPHER
import SQLCipher
#elseif !GRDBCUSTOMSQLITE && !GRDBCIPHER
import SQLite3
#endif

public class DatabaseHistoricalSnapshot {
private let databasePool: DatabasePool
private let snapshot: Database.Snapshot
private let schemaCache: SharedDatabaseSchemaCache

init(databasePool: DatabasePool, snapshot: Database.Snapshot) {
self.databasePool = databasePool
self.snapshot = snapshot
self.schemaCache = SharedDatabaseSchemaCache()
}

deinit {
if databasePool.configuration.historicalSnapshotsPreventAutomatickCheckpointing {
databasePool.historicalSnapshotCount.decrement()
}
}
}

extension DatabaseHistoricalSnapshot: DatabaseReader {
// :nodoc:
public var configuration: Configuration {
return databasePool.readerConfiguration
}

public func read<T>(_ block: (Database) throws -> T) throws -> T {
return try databasePool.read { db in
try db.openSnapshot(snapshot)
return try db.withSchemaCache(schemaCache) {
try block(db)
}
}
}

#if compiler(>=5.0)
public func asyncRead(_ block: @escaping (Result<Database, Error>) -> Void) {
databasePool.asyncRead { db in
do {
let db = try db.get()
try db.openSnapshot(self.snapshot)
db.withSchemaCache(self.schemaCache) {
block(.success(db))
}
} catch {
block(.failure(error))
}
}
}
#endif

public func unsafeRead<T>(_ block: (Database) throws -> T) throws -> T {
return try read(block)
}

public func unsafeReentrantRead<T>(_ block: (Database) throws -> T) throws -> T {
return try databasePool.unsafeReentrantRead(checkingSnapshot: false) { db in
if db.isInsideTransaction {
guard db.currentSnapshot == self.snapshot else {
fatalError("unsafeReentrantRead misuse: this connection runs in a different snapshot")
}
// db.schemaCache is likely to already be self.schemaCache, but
// we do not harm by forcing it.
return try db.withSchemaCache(schemaCache) {
try block(db)
}
} else {
var result: T? = nil
try db.inTransaction(.deferred) {
try db.openSnapshot(snapshot)
result = try db.withSchemaCache(schemaCache) {
try block(db)
}
return .commit
}
return result!
}
}
}

public func add(function: DatabaseFunction) {
databasePool.add(function: function)
}

public func remove(function: DatabaseFunction) {
databasePool.remove(function: function)
}

public func add(collation: DatabaseCollation) {
databasePool.add(collation: collation)
}

public func remove(collation: DatabaseCollation) {
databasePool.add(collation: collation)
}

public func add<Reducer>(
observation: ValueObservation<Reducer>,
onError: @escaping (Error) -> Void,
onChange: @escaping (Reducer.Value) -> Void)
-> TransactionObserver
where Reducer: ValueReducer
{
fatalError("Not implemented")
}

public func remove(transactionObserver: TransactionObserver) {
fatalError("Not implemented")
}
}
#endif
Loading