Skip to content

Commit dbd9c09

Browse files
Use latest GRDB package. Update tests and queries.
1 parent 8428135 commit dbd9c09

File tree

4 files changed

+87
-34
lines changed

4 files changed

+87
-34
lines changed

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.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ let package = Package(
7878
)
7979
],
8080
dependencies: conditionalDependencies + [
81-
.package(url: "https://github.com/groue/GRDB.swift.git", from: "6.0.0")
81+
.package(url: "https://github.com/groue/GRDB.swift.git", branch: "dev/persistable-views")
8282
],
8383
targets: [
8484
// Targets are the basic building blocks of a package, defining a module or a test suite.

Sources/PowerSyncGRDB/GRDBPool.swift

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,79 @@ import SQLite3
77
// linking PowerSync provides them
88
// Declare the missing function manually
99
@_silgen_name("sqlite3_enable_load_extension")
10-
func sqlite3_enable_load_extension(_ db: OpaquePointer?, _ onoff: Int32) -> Int32
10+
func sqlite3_enable_load_extension(
11+
_ db: OpaquePointer?,
12+
_ onoff: Int32
13+
) -> Int32
1114

1215
// Similarly for sqlite3_load_extension if needed:
1316
@_silgen_name("sqlite3_load_extension")
14-
func sqlite3_load_extension(_ db: OpaquePointer?, _ fileName: UnsafePointer<Int8>?, _ procName: UnsafePointer<Int8>?, _ errMsg: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?) -> Int32
17+
func sqlite3_load_extension(
18+
_ db: OpaquePointer?,
19+
_ fileName: UnsafePointer<Int8>?,
20+
_ procName: UnsafePointer<Int8>?,
21+
_ errMsg: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?
22+
) -> Int32
1523

16-
enum PowerSyncGRDBConfigError: Error {
17-
case bundleNotFound
24+
enum PowerSyncGRDBError: Error {
25+
case coreBundleNotFound
1826
case extensionLoadFailed(String)
1927
case unknownExtensionLoadError
28+
case connectionUnavailable
2029
}
2130

22-
func configurePowerSync(_ config: inout Configuration) {
31+
struct PowerSyncSchemaSource: DatabaseSchemaSource {
32+
let schema: Schema
33+
34+
func columnsForPrimaryKey(_: Database, inView view: DatabaseObjectID) throws -> [String]? {
35+
if schema.tables.first(where: { table in
36+
table.viewName == view.name
37+
}) != nil {
38+
return ["id"]
39+
}
40+
return nil
41+
}
42+
}
43+
44+
func configurePowerSync(
45+
config: inout Configuration,
46+
schema: Schema
47+
) {
48+
// Register the PowerSync core extension
2349
config.prepareDatabase { database in
2450
guard let bundle = Bundle(identifier: "co.powersync.sqlitecore") else {
25-
throw PowerSyncGRDBConfigError.bundleNotFound
51+
throw PowerSyncGRDBError.coreBundleNotFound
2652
}
2753

2854
// Construct the full path to the shared library inside the bundle
2955
let fullPath = bundle.bundlePath + "/powersync-sqlite-core"
3056

31-
let rc = sqlite3_enable_load_extension(database.sqliteConnection, 1)
32-
if rc != SQLITE_OK {
33-
throw PowerSyncGRDBConfigError.extensionLoadFailed("Could not enable extension loading")
57+
let extensionLoadResult = sqlite3_enable_load_extension(database.sqliteConnection, 1)
58+
if extensionLoadResult != SQLITE_OK {
59+
throw PowerSyncGRDBError.extensionLoadFailed("Could not enable extension loading")
3460
}
3561
var errorMsg: UnsafeMutablePointer<Int8>?
3662
let loadResult = sqlite3_load_extension(database.sqliteConnection, fullPath, "sqlite3_powersync_init", &errorMsg)
3763
if loadResult != SQLITE_OK {
3864
if let errorMsg = errorMsg {
3965
let message = String(cString: errorMsg)
4066
sqlite3_free(errorMsg)
41-
throw PowerSyncGRDBConfigError.extensionLoadFailed(message)
67+
throw PowerSyncGRDBError.extensionLoadFailed(message)
4268
} else {
43-
throw PowerSyncGRDBConfigError.unknownExtensionLoadError
69+
throw PowerSyncGRDBError.unknownExtensionLoadError
4470
}
4571
}
4672
}
73+
74+
// Supply the PowerSync views as a SchemaSource
75+
let powerSyncSchemaSource = PowerSyncSchemaSource(
76+
schema: schema
77+
)
78+
if let schemaSource = config.schemaSource {
79+
config.schemaSource = schemaSource.then(powerSyncSchemaSource)
80+
} else {
81+
config.schemaSource = powerSyncSchemaSource
82+
}
4783
}
4884

4985
class GRDBConnectionPool: SQLiteConnectionPoolProtocol {
@@ -60,7 +96,7 @@ class GRDBConnectionPool: SQLiteConnectionPoolProtocol {
6096
) async throws {
6197
try await pool.read { database in
6298
guard let connection = database.sqliteConnection else {
63-
return
99+
throw PowerSyncGRDBError.connectionUnavailable
64100
}
65101
onConnection(connection)
66102
}
@@ -72,7 +108,7 @@ class GRDBConnectionPool: SQLiteConnectionPoolProtocol {
72108
// Don't start an explicit transaction
73109
try await pool.writeWithoutTransaction { database in
74110
guard let connection = database.sqliteConnection else {
75-
return
111+
throw PowerSyncGRDBError.connectionUnavailable
76112
}
77113
onConnection(connection)
78114
}

Tests/PowerSyncGRDBTests/BasicTest.swift

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@ final class GRDBTests: XCTestCase {
1414
schema = Schema(tables: [
1515
Table(name: "users", columns: [
1616
.text("name"),
17-
.text("count")
1817
])
1918
])
2019

2120
var config = Configuration()
22-
configurePowerSync(&config)
21+
configurePowerSync(
22+
config: &config,
23+
schema: schema
24+
)
25+
2326
let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
2427
let dbURL = documentsDir.appendingPathComponent("test.sqlite")
2528
pool = try DatabasePool(
@@ -45,40 +48,54 @@ final class GRDBTests: XCTestCase {
4548
}
4649

4750
func testValidValues() async throws {
48-
let result = try await database.get(
49-
"SELECT powersync_rs_version() as r"
50-
) { cursor in
51-
try cursor.getString(index: 0)
52-
}
53-
print(result)
51+
// Create users with the PowerSync SDK
52+
let initialUserName = "Bob"
5453

5554
try await database.execute(
56-
"INSERT INTO users(id, name, count) VALUES(uuid(), 'steven', 1)"
55+
sql: "INSERT INTO users(id, name) VALUES(uuid(), ?)",
56+
parameters: [initialUserName]
5757
)
5858

59-
let initialUsers = try await database.getAll(
59+
// Fetch those users
60+
let initialUserNames = try await database.getAll(
6061
"SELECT * FROM users"
6162
) { cursor in
6263
try cursor.getString(name: "name")
6364
}
64-
print("initial users \(initialUsers)")
6565

66-
// Now use a GRDB query
67-
struct Users: Codable, Identifiable, FetchableRecord, PersistableRecord {
66+
XCTAssertTrue(initialUserNames.first == initialUserName)
67+
68+
// Now define a GRDB struct for query purposes
69+
struct User: Codable, Identifiable, FetchableRecord, PersistableRecord {
6870
var id: String
6971
var name: String
70-
var count: Int
72+
73+
static var databaseTableName = "users"
7174

7275
enum Columns {
7376
static let name = Column(CodingKeys.name)
74-
static let count = Column(CodingKeys.count)
7577
}
7678
}
7779

78-
let grdbUsers = try await pool.write { db in
79-
try Users.fetchAll(db)
80+
// Query the Users with GRDB, this should have the same result as with PowerSync
81+
let grdbUserNames = try await pool.read { database in
82+
try User.fetchAll(database)
8083
}
8184

82-
print(grdbUsers)
85+
XCTAssertTrue(grdbUserNames.first?.name == initialUserName)
86+
87+
// Insert a user with GRDB
88+
try await pool.write { database in
89+
try User(
90+
id: UUID().uuidString,
91+
name: "another",
92+
).insert(database)
93+
}
94+
95+
let grdbUserNames2 = try await pool.read { database in
96+
try User.order(User.Columns.name.asc).fetchAll(database)
97+
}
98+
XCTAssert(grdbUserNames2.count == 2)
99+
XCTAssert(grdbUserNames2[1].name == "another")
83100
}
84101
}

0 commit comments

Comments
 (0)