Skip to content

Commit 2f643fb

Browse files
Merge remote-tracking branch 'origin/encryption' into grdb-csqlite
2 parents 9e347cf + 9f457dc commit 2f643fb

File tree

5 files changed

+89
-33
lines changed

5 files changed

+89
-33
lines changed

Package.resolved

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,42 +12,26 @@ let localKotlinSdkOverride: String? = nil
1212
// local build of the core extension.
1313
let localCoreExtension: String? = nil
1414

15+
// Currently encryption is required for GRDB integration
16+
let encryption = true
17+
1518
// Our target and dependency setup is different when a local Kotlin SDK is used. Without the local
1619
// SDK, we have no package dependency on Kotlin and download the XCFramework from Kotlin releases as
1720
// a binary target.
1821
// With a local SDK, we point to a `Package.swift` within the Kotlin SDK containing a target pointing
1922
// towards a local framework build
2023
var conditionalDependencies: [Package.Dependency] = [
21-
// We can't currently build with GRDB using this package
22-
// We could use traits for this
23-
24-
// .package(
25-
// url: "https://github.com/sbooth/CSQLite.git",
26-
// from: "3.50.4",
27-
// traits: [
28-
// .defaults,
29-
// // CSQLite uses THREADSAFE=0 by default, which breaks PowerSync because we're using SQLite on
30-
// // multiple threads (it can lead to race conditions when closing connections sharing resources
31-
// // like shared memory, causing crashes).
32-
// // THREADSAFE=2 overrides the default, and is safe to use as long as a single SQLite connection
33-
// // is not shared between threads.
34-
// // TODO: Technically, we should not use .defaults because there's a logical conflict between
35-
// // the threadsafe options. Instead, we should spell out all defaults again and remove that
36-
// // thread-safety option.
37-
// // However, despite the docs explicitly saying something else, it looks like there's no way to
38-
// // disable default traits anyway (XCode compiles sqlite3.c with the default option even without
39-
// // .defaults being included here).
40-
// "THREADSAFE_2",
41-
// "ENABLE_SESSION"
42-
// ]
43-
// )
44-
45-
// Using SQLCipher here since GRDB doesn't compile with CSQLite
46-
.package(url: "https://github.com/sqlcipher/SQLCipher.swift.git", from: "4.10.0")
4724
]
25+
4826
var conditionalTargets: [Target] = []
4927
var kotlinTargetDependency = Target.Dependency.target(name: "PowerSyncKotlin")
5028

29+
if encryption {
30+
conditionalDependencies.append(.package(url: "https://github.com/sqlcipher/SQLCipher.swift.git", from: "4.10.0"))
31+
} else {
32+
conditionalDependencies.append(.package(url: "https://github.com/sbooth/CSQLite.git", from: "3.50.4"))
33+
}
34+
5135
if let kotlinSdkPath = localKotlinSdkOverride {
5236
// We can't depend on local XCFrameworks outside of this project's root, so there's a Package.swift
5337
// in the PowerSyncKotlin project pointing towards a local build.
@@ -114,15 +98,17 @@ let package = Package(
11498
dependencies: [
11599
kotlinTargetDependency,
116100
.product(name: "PowerSyncSQLiteCore", package: corePackageName),
117-
// .product(name: "CSQLite", package: "CSQLite")
118-
.product(name: "SQLCipher", package: "SQLCipher.swift")
101+
encryption ?
102+
.product(name: "SQLCipher", package: "SQLCipher.swift") :
103+
.product(name: "CSQLite", package: "CSQLite")
119104
]
120105
),
121106
.target(
122107
name: "PowerSyncGRDB",
123108
dependencies: [
124109
.target(name: "PowerSync"),
125110
.product(name: "GRDB", package: "GRDB.swift")
111+
126112
]
127113
),
128114
.testTarget(

Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,14 +393,15 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol,
393393
func openKotlinDBDefault(
394394
schema: Schema,
395395
dbFilename: String,
396-
logger: DatabaseLogger
396+
logger: DatabaseLogger,
397+
initialStatements: [String] = []
397398
) -> PowerSyncDatabaseProtocol {
398399
let rc = sqlite3_initialize()
399400
if rc != 0 {
400401
fatalError("Call to sqlite3_initialize() failed with \(rc)")
401402
}
402403

403-
let factory = sqlite3DatabaseFactory(initialStatements: [])
404+
let factory = sqlite3DatabaseFactory(initialStatements: initialStatements)
404405
return KotlinPowerSyncDatabaseImpl(
405406
kotlinDatabase: PowerSyncDatabase(
406407
factory: factory,

Sources/PowerSync/PowerSyncDatabase.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ public let DEFAULT_DB_FILENAME = "powersync.db"
1212
public func PowerSyncDatabase(
1313
schema: Schema,
1414
dbFilename: String = DEFAULT_DB_FILENAME,
15-
logger: (any LoggerProtocol) = DefaultLogger()
15+
logger: (any LoggerProtocol) = DefaultLogger(),
16+
initialStatements: [String] = []
1617
) -> PowerSyncDatabaseProtocol {
1718
return openKotlinDBDefault(
1819
schema: schema,
1920
dbFilename: dbFilename,
20-
logger: DatabaseLogger(logger)
21+
logger: DatabaseLogger(logger),
22+
initialStatements: initialStatements
2123
)
2224
}
2325

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
@testable import PowerSync
2+
import XCTest
3+
4+
5+
final class EncryptionTests: XCTestCase {
6+
7+
func testLinksSqlcipher() async throws {
8+
let database = openKotlinDBDefault(
9+
schema: Schema(),
10+
dbFilename: ":memory:",
11+
logger: DatabaseLogger(DefaultLogger())
12+
)
13+
14+
let version = try await database.get("pragma cipher_version", mapper: {cursor in
15+
try cursor.getString(index: 0)
16+
});
17+
18+
XCTAssertEqual(version, "4.11.0 community")
19+
try await database.close()
20+
}
21+
22+
func testEncryption() async throws {
23+
let database = openKotlinDBDefault(
24+
schema: Schema(tables: [
25+
Table(
26+
name: "users",
27+
columns: [
28+
.text("name")
29+
]
30+
),
31+
]),
32+
dbFilename: "encrypted.db",
33+
logger: DatabaseLogger(DefaultLogger()),
34+
initialStatements: [
35+
"pragma key = 'foobar'"
36+
],
37+
)
38+
39+
try await database.execute("INSERT INTO users (id, name) VALUES (uuid(), 'test')")
40+
try await database.close()
41+
42+
let another = openKotlinDBDefault(
43+
schema: Schema(tables: [
44+
Table(
45+
name: "users",
46+
columns: [
47+
.text("name")
48+
]
49+
),
50+
]),
51+
dbFilename: "encrypted.db",
52+
logger: DatabaseLogger(DefaultLogger()),
53+
initialStatements: [
54+
"pragma key = 'wrong password'"
55+
],
56+
)
57+
58+
var hadError = false
59+
do {
60+
try await database.execute("DELETE FROM users")
61+
} catch let error {
62+
hadError = true
63+
}
64+
65+
XCTAssertTrue(hadError)
66+
}
67+
}

0 commit comments

Comments
 (0)