Skip to content

Commit 855e82c

Browse files
committed
Use high-level GRDB APIs to run statements
1 parent 2e1b08f commit 855e82c

9 files changed

Lines changed: 172 additions & 50 deletions

File tree

Demos/GRDBDemo/GRDBDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

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

Package.resolved

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

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ let package = Package(
6060
)
6161
],
6262
dependencies: conditionalDependencies + [
63-
.package(url: "https://github.com/groue/GRDB.swift.git", from: "7.9.0"),
63+
.package(url: "https://github.com/groue/GRDB.swift.git", from: "7.11.0"),
6464
.package(url: "https://github.com/powersync-ja/CSQLite.git", exact: "3.51.2"),
6565
.package(url: "https://github.com/apple/swift-async-algorithms.git", from: "1.1.0"),
6666
.package(url: "https://github.com/apple/swift-collections.git", from: "1.4.0")
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
// The current version of the PowerSync Swift SDK. This should be updated to the latest version in `CHANGELOG.md` when a new version is released.
2-
let libraryVersion = "1.14.0"
3-
2+
let libraryVersion = "1.14.1"
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import GRDB
2+
3+
class AllWritesObserver: TransactionObserver {
4+
private(set) var committedTables: Set<String> = []
5+
private var uncomittedTables: Set<String> = []
6+
7+
var databaseEventObservationStrategy: DatabaseEventObservationStrategy {
8+
var strategy = DatabaseEventObservationStrategy.default
9+
// Don't filter on database event kind, so that we are
10+
// notified of changes performed through the SQLite C API:
11+
strategy.requiresDatabaseEventKind = false
12+
return strategy
13+
}
14+
15+
func observes(eventsOfKind eventKind: DatabaseEventKind) -> Bool {
16+
// This will never be called since requiresDatabaseEventKind is false
17+
true
18+
}
19+
20+
func databaseDidChange(with event: DatabaseEvent) {
21+
uncomittedTables.insert(event.tableName)
22+
}
23+
24+
func databaseDidCommit(_ db: GRDB.Database) {
25+
committedTables.formUnion(uncomittedTables)
26+
uncomittedTables.removeAll()
27+
}
28+
29+
func databaseDidRollback(_ db: GRDB.Database) {
30+
uncomittedTables.removeAll()
31+
}
32+
}

Sources/PowerSyncGRDB/Connections/GRDBConnectionLease.swift

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,38 @@ final class GRDBConnectionLease: SQLiteConnectionLease {
1717
self.pointer = connection
1818
self.database = database
1919
}
20-
}
20+
21+
func execute(sql: String, parameters: [PowerSyncDataType?]) throws -> Int64 {
22+
try database.execute(sql: sql, arguments: StatementArguments(parameters))
23+
return Int64(database.changesCount)
24+
}
25+
26+
func withIterator<T>(sql: String, parameters: [PowerSyncDataType?], callback: (any SQLiteStatementIteratorProtocol) throws -> T) throws -> T {
27+
let statement = try database.makeStatement(sql: sql)
28+
let rows = try Row.fetchCursor(statement, arguments: StatementArguments(parameters))
29+
return try callback(RowIterator(rows: rows))
30+
}
31+
}
32+
33+
extension PowerSync.PowerSyncDataType: DatabaseValueConvertible {
34+
public var databaseValue: GRDB.DatabaseValue {
35+
switch self {
36+
case .bool(let value):
37+
return value.databaseValue
38+
case .string(let value):
39+
return value.databaseValue
40+
case .int64(let value):
41+
return value.databaseValue
42+
case .int32(let value):
43+
return value.databaseValue
44+
case .double(let value):
45+
return value.databaseValue
46+
case .data(let value):
47+
return value.databaseValue
48+
}
49+
}
50+
51+
public static func fromDatabaseValue(_ dbValue: GRDB.DatabaseValue) -> PowerSync.PowerSyncDataType? {
52+
nil
53+
}
54+
}

Sources/PowerSyncGRDB/Connections/GRDBConnectionPool.swift

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,12 @@ actor GRDBConnectionPool: SQLiteConnectionPoolProtocol {
5252
) async throws -> T {
5353
// Don't start an explicit transaction, we do this internally.
5454
let (result, updates) = try await pool.writeWithoutTransaction { database in
55-
// This installs a temporary update hook, which breaks if GRDB had its own. Currently, we rely on
56-
// GRDB only installing update hooks for statements that need it (see https://github.com/groue/GRDB.swift/blob/36e30a6f1ef10e4194f6af0cff90888526f0c115/GRDB/Core/TransactionObserver.swift#L266-L275),
57-
// note that `statementObservations` is set in `statementWillExecute` and cleared after a statement
58-
// has completed or failed.
59-
// So since we have exclusive access to the write connection here, no GRDB-active statement can run and we
60-
// can safely install our own hooks.
61-
// In the future, we would like to use high-level GRDB APIs for this instead. However, we're blocked
62-
// by https://github.com/groue/GRDB.swift/issues/1863 on that.
63-
try collectWrites(db: database.sqliteConnection!) {
64-
try onConnection(GRDBConnectionLease(database: database))
65-
}
55+
let observer = AllWritesObserver()
56+
database.add(transactionObserver: observer)
57+
defer { database.remove(transactionObserver: observer) }
58+
59+
let result = try onConnection(GRDBConnectionLease(database: database))
60+
return (result, observer.committedTables)
6661
}
6762

6863
if !updates.isEmpty {
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import GRDB
2+
import PowerSync
3+
4+
final class RowIterator: PowerSync.SQLiteStatementIteratorProtocol {
5+
let rows: RowCursor
6+
var columnNames: [String: Int]? = nil
7+
8+
init(rows: RowCursor) {
9+
self.rows = rows
10+
}
11+
12+
func next<T>(callback: (any SqlCursor) throws -> T) throws -> T? {
13+
guard let row = try rows.next() else {
14+
return nil
15+
}
16+
17+
return try callback(RowSqlCursor(columnNames: resolveColumnNames(), row: row))
18+
}
19+
20+
private func resolveColumnNames() -> [String: Int] {
21+
if let columnNames {
22+
return columnNames
23+
}
24+
25+
var names: [String: Int] = [:]
26+
for (i, name) in rows.columnNames.enumerated() {
27+
names[name] = i
28+
}
29+
columnNames = names
30+
return names
31+
}
32+
}
33+
34+
struct RowSqlCursor: PowerSync.SqlCursor {
35+
let columnNames: [String: Int]
36+
let row: Row
37+
38+
private func checkNotNull(index: Int) throws(PowerSync.SqlCursorError) {
39+
if row.hasNull(atIndex: index) {
40+
throw .nullValueFound("\(index)")
41+
}
42+
}
43+
44+
public func getBoolean(index: Int) throws(PowerSync.SqlCursorError) -> Bool {
45+
try checkNotNull(index: index)
46+
return row.self[index]
47+
}
48+
49+
public func getBooleanOptional(index: Int) -> Bool? {
50+
return row.self[index]
51+
}
52+
53+
public func getDouble(index: Int) throws(PowerSync.SqlCursorError) -> Double {
54+
try checkNotNull(index: index)
55+
return row.self[index]
56+
}
57+
58+
public func getDoubleOptional(index: Int) -> Double? {
59+
return row.self[index]
60+
}
61+
62+
public func getInt(index: Int) throws(PowerSync.SqlCursorError) -> Int {
63+
try checkNotNull(index: index)
64+
return row.self[index]
65+
}
66+
67+
public func getIntOptional(index: Int) -> Int? {
68+
return row.self[index]
69+
}
70+
71+
public func getInt64(index: Int) throws(PowerSync.SqlCursorError) -> Int64 {
72+
try self.checkNotNull(index: index)
73+
return row.self[index]
74+
}
75+
76+
public func getInt64Optional(index: Int) -> Int64? {
77+
return row.self[index]
78+
}
79+
80+
public func getString(index: Int) throws(PowerSync.SqlCursorError) -> String {
81+
try self.checkNotNull(index: index)
82+
return row.self[index]
83+
}
84+
85+
public func getStringOptional(index: Int) -> String? {
86+
return row.self[index]
87+
}
88+
89+
public var columnCount: Int {
90+
row.count
91+
}
92+
}

Sources/PowerSyncGRDB/Connections/collectWrites.swift

Lines changed: 0 additions & 30 deletions
This file was deleted.

0 commit comments

Comments
 (0)