Skip to content

Commit 566c924

Browse files
feat: add SqlCursor function improvements (#17)
* feat: add SqlCursor function improvements * feat: add SqlCursor function improvements * docs: update changelog * Update CHANGELOG.md Co-authored-by: stevensJourney <[email protected]> --------- Co-authored-by: stevensJourney <[email protected]>
1 parent 53227f5 commit 566c924

File tree

11 files changed

+294
-28
lines changed

11 files changed

+294
-28
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 1.0.0-Beta.4
4+
* Allow cursor to use column name to get value by including the following functions that accept a column name parameter:
5+
`getBoolean`,`getBooleanOptional`,`getString`,`getStringOptional`, `getLong`,`getLongOptional`, `getDouble`,`getDoubleOptional`
6+
* BREAKING CHANGE: This should not affect anyone but made `KotlinPowerSyncCredentials`, `KotlinPowerSyncDatabase` and `KotlinPowerSyncBackendConnector` private as these should never have been public.
7+
8+
39
## 1.0.0-Beta.3
410

511
* BREAKING CHANGE: Update underlying powersync-kotlin package to BETA18.0 which requires transactions to become synchronous as opposed to asynchronous.

Demo/PowerSyncExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
"kind" : "remoteSourceControl",
1616
"location" : "https://github.com/powersync-ja/powersync-kotlin.git",
1717
"state" : {
18-
"revision" : "074001ad7d02b2b70c77168cdf4958c08dd6121b",
19-
"version" : "1.0.0-BETA18.0"
18+
"revision" : "7cd47ffc9dbec8fae4f9e067945ef2279015a90d",
19+
"version" : "1.0.0-BETA20.0"
2020
}
2121
},
2222
{

Demo/PowerSyncExample/PowerSync/SystemManager.swift

+13-13
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ class SystemManager {
3939
parameters: [],
4040
mapper: { cursor in
4141
ListContent(
42-
id: cursor.getString(index: 0)!,
43-
name: cursor.getString(index: 1)!,
44-
createdAt: cursor.getString(index: 2)!,
45-
ownerId: cursor.getString(index: 3)!
42+
id: try cursor.getString(name: "id"),
43+
name: try cursor.getString(name: "name"),
44+
createdAt: try cursor.getString(name: "created_at"),
45+
ownerId: try cursor.getString(name: "owner_id")
4646
)
4747
}
4848
) {
@@ -77,15 +77,15 @@ class SystemManager {
7777
parameters: [listId],
7878
mapper: { cursor in
7979
return Todo(
80-
id: cursor.getString(index: 0)!,
81-
listId: cursor.getString(index: 1)!,
82-
photoId: cursor.getString(index: 2),
83-
description: cursor.getString(index: 3)!,
84-
isComplete: cursor.getBoolean(index: 4)! as! Bool,
85-
createdAt: cursor.getString(index: 5),
86-
completedAt: cursor.getString(index: 6),
87-
createdBy: cursor.getString(index: 7),
88-
completedBy: cursor.getString(index: 8)
80+
id: try cursor.getString(name: "id"),
81+
listId: try cursor.getString(name: "list_id"),
82+
photoId: try cursor.getStringOptional(name: "photo_id"),
83+
description: try cursor.getString(name: "description"),
84+
isComplete: try cursor.getBoolean(name: "completed"),
85+
createdAt: try cursor.getString(name: "created_at"),
86+
completedAt: try cursor.getStringOptional(name: "completed_at"),
87+
createdBy: try cursor.getStringOptional(name: "created_by"),
88+
completedBy: try cursor.getStringOptional(name: "completed_by")
8989
)
9090
}
9191
) {

Package.resolved

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
"kind" : "remoteSourceControl",
66
"location" : "https://github.com/powersync-ja/powersync-kotlin.git",
77
"state" : {
8-
"revision" : "074001ad7d02b2b70c77168cdf4958c08dd6121b",
9-
"version" : "1.0.0-BETA18.0"
8+
"revision" : "7cd47ffc9dbec8fae4f9e067945ef2279015a90d",
9+
"version" : "1.0.0-BETA20.0"
1010
}
1111
},
1212
{

Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ let package = Package(
1616
targets: ["PowerSync"]),
1717
],
1818
dependencies: [
19-
.package(url: "https://github.com/powersync-ja/powersync-kotlin.git", exact: "1.0.0-BETA18.0"),
19+
.package(url: "https://github.com/powersync-ja/powersync-kotlin.git", exact: "1.0.0-BETA20.0"),
2020
.package(url: "https://github.com/powersync-ja/powersync-sqlite-core-swift.git", "0.3.9"..<"0.4.0")
2121
],
2222
targets: [

Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift

+64-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,21 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
7979
mapper: mapper
8080
) as! RowType
8181
}
82-
82+
83+
func get<RowType>(
84+
sql: String,
85+
parameters: [Any]?,
86+
mapper: @escaping (SqlCursor) throws -> RowType
87+
) async throws -> RowType {
88+
try await kotlinDatabase.get(
89+
sql: sql,
90+
parameters: parameters,
91+
mapper: { cursor in
92+
try! mapper(cursor)
93+
}
94+
) as! RowType
95+
}
96+
8397
func getAll<RowType>(
8498
sql: String,
8599
parameters: [Any]?,
@@ -91,6 +105,20 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
91105
mapper: mapper
92106
) as! [RowType]
93107
}
108+
109+
func getAll<RowType>(
110+
sql: String,
111+
parameters: [Any]?,
112+
mapper: @escaping (SqlCursor) throws -> RowType
113+
) async throws -> [RowType] {
114+
try await kotlinDatabase.getAll(
115+
sql: sql,
116+
parameters: parameters,
117+
mapper: { cursor in
118+
try! mapper(cursor)
119+
}
120+
) as! [RowType]
121+
}
94122

95123
func getOptional<RowType>(
96124
sql: String,
@@ -103,6 +131,20 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
103131
mapper: mapper
104132
) as! RowType?
105133
}
134+
135+
func getOptional<RowType>(
136+
sql: String,
137+
parameters: [Any]?,
138+
mapper: @escaping (SqlCursor) throws -> RowType
139+
) async throws -> RowType? {
140+
try await kotlinDatabase.getOptional(
141+
sql: sql,
142+
parameters: parameters,
143+
mapper: { cursor in
144+
try! mapper(cursor)
145+
}
146+
) as! RowType?
147+
}
106148

107149
func watch<RowType>(
108150
sql: String,
@@ -123,6 +165,27 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
123165
}
124166
}
125167

168+
func watch<RowType>(
169+
sql: String,
170+
parameters: [Any]?,
171+
mapper: @escaping (SqlCursor) throws -> RowType
172+
) -> AsyncStream<[RowType]> {
173+
AsyncStream { continuation in
174+
Task {
175+
for await values in self.kotlinDatabase.watch(
176+
sql: sql,
177+
parameters: parameters,
178+
mapper: { cursor in
179+
try! mapper(cursor)
180+
}
181+
) {
182+
continuation.yield(values as! [RowType])
183+
}
184+
continuation.finish()
185+
}
186+
}
187+
}
188+
126189
public func writeTransaction<R>(callback: @escaping (any PowerSyncTransaction) -> R) async throws -> R {
127190
return try await kotlinDatabase.writeTransaction(callback: callback) as! R
128191
}
+4-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import PowerSyncKotlin
22

3-
public typealias KotlinPowerSyncBackendConnector = PowerSyncKotlin.PowerSyncBackendConnector
3+
typealias KotlinPowerSyncBackendConnector = PowerSyncKotlin.PowerSyncBackendConnector
44
public typealias CrudEntry = PowerSyncKotlin.CrudEntry
55
public typealias CrudBatch = PowerSyncKotlin.CrudBatch
66
public typealias SyncStatus = PowerSyncKotlin.SyncStatus
7-
public typealias SqlCursor = PowerSyncKotlin.RuntimeSqlCursor
7+
public typealias SqlCursor = PowerSyncKotlin.SqlCursor
88
public typealias JsonParam = PowerSyncKotlin.JsonParam
99
public typealias CrudTransaction = PowerSyncKotlin.CrudTransaction
10-
public typealias KotlinPowerSyncCredentials = PowerSyncKotlin.PowerSyncCredentials
11-
public typealias KotlinPowerSyncDatabase = PowerSyncKotlin.PowerSyncDatabase
12-
10+
typealias KotlinPowerSyncCredentials = PowerSyncKotlin.PowerSyncCredentials
11+
typealias KotlinPowerSyncDatabase = PowerSyncKotlin.PowerSyncDatabase
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import Foundation
2+
import PowerSyncKotlin
3+
4+
extension SqlCursor {
5+
private func getColumnIndex(name: String) throws -> Int32 {
6+
guard let columnIndex = columnNames[name]?.int32Value else {
7+
throw SqlCursorError.columnNotFound(name)
8+
}
9+
return columnIndex
10+
}
11+
12+
private func getValue<T>(name: String, getter: (Int32) -> T?) throws -> T {
13+
let columnIndex = try getColumnIndex(name: name)
14+
guard let value = getter(columnIndex) else {
15+
throw SqlCursorError.nullValueFound(name)
16+
}
17+
return value
18+
}
19+
20+
private func getOptionalValue<T>(name: String, getter: (String) -> T?) throws -> T? {
21+
_ = try getColumnIndex(name: name)
22+
return getter(name)
23+
}
24+
25+
public func getBoolean(name: String) throws -> Bool {
26+
try getValue(name: name) { getBoolean(index: $0)?.boolValue }
27+
}
28+
29+
public func getDouble(name: String) throws -> Double {
30+
try getValue(name: name) { getDouble(index: $0)?.doubleValue }
31+
}
32+
33+
public func getLong(name: String) throws -> Int {
34+
try getValue(name: name) { getLong(index: $0)?.intValue }
35+
}
36+
37+
public func getString(name: String) throws -> String {
38+
try getValue(name: name) { getString(index: $0) }
39+
}
40+
41+
public func getBooleanOptional(name: String) throws -> Bool? {
42+
try getOptionalValue(name: name) { getBooleanOptional(name: $0)?.boolValue }
43+
}
44+
45+
public func getDoubleOptional(name: String) throws -> Double? {
46+
try getOptionalValue(name: name) { getDoubleOptional(name: $0)?.doubleValue }
47+
}
48+
49+
public func getLongOptional(name: String) throws -> Int? {
50+
try getOptionalValue(name: name) { getLongOptional(name: $0)?.intValue }
51+
}
52+
53+
public func getStringOptional(name: String) throws -> String? {
54+
try getOptionalValue(name: name) { PowerSyncKotlin.SqlCursorKt.getStringOptional(self, name: $0) }
55+
}
56+
}
57+
58+
enum SqlCursorError: Error {
59+
case nullValue(message: String)
60+
61+
static func columnNotFound(_ name: String) -> SqlCursorError {
62+
.nullValue(message: "Column '\(name)' not found")
63+
}
64+
65+
static func nullValueFound(_ name: String) -> SqlCursorError {
66+
.nullValue(message: "Null value found for column \(name)")
67+
}
68+
}

Sources/PowerSync/QueriesProtocol.swift

+31
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,43 @@ public protocol Queries {
1616
mapper: @escaping (SqlCursor) -> RowType
1717
) async throws -> RowType
1818

19+
/// Execute a read-only (SELECT) query and return a single result.
20+
/// If there is no result, throws an IllegalArgumentException.
21+
/// See `getOptional` for queries where the result might be empty.
22+
func get<RowType>(
23+
sql: String,
24+
parameters: [Any]?,
25+
mapper: @escaping (SqlCursor) throws -> RowType
26+
) async throws -> RowType
27+
1928
/// Execute a read-only (SELECT) query and return the results.
2029
func getAll<RowType>(
2130
sql: String,
2231
parameters: [Any]?,
2332
mapper: @escaping (SqlCursor) -> RowType
2433
) async throws -> [RowType]
2534

35+
/// Execute a read-only (SELECT) query and return the results.
36+
func getAll<RowType>(
37+
sql: String,
38+
parameters: [Any]?,
39+
mapper: @escaping (SqlCursor) throws -> RowType
40+
) async throws -> [RowType]
41+
2642
/// Execute a read-only (SELECT) query and return a single optional result.
2743
func getOptional<RowType>(
2844
sql: String,
2945
parameters: [Any]?,
3046
mapper: @escaping (SqlCursor) -> RowType
3147
) async throws -> RowType?
3248

49+
/// Execute a read-only (SELECT) query and return a single optional result.
50+
func getOptional<RowType>(
51+
sql: String,
52+
parameters: [Any]?,
53+
mapper: @escaping (SqlCursor) throws -> RowType
54+
) async throws -> RowType?
55+
3356
/// Execute a read-only (SELECT) query every time the source tables are modified
3457
/// and return the results as an array in a Publisher.
3558
func watch<RowType>(
@@ -38,6 +61,14 @@ public protocol Queries {
3861
mapper: @escaping (SqlCursor) -> RowType
3962
) -> AsyncStream<[RowType]>
4063

64+
/// Execute a read-only (SELECT) query every time the source tables are modified
65+
/// and return the results as an array in a Publisher.
66+
func watch<RowType>(
67+
sql: String,
68+
parameters: [Any]?,
69+
mapper: @escaping (SqlCursor) throws -> RowType
70+
) -> AsyncStream<[RowType]>
71+
4172
/// Execute a write transaction with the given callback
4273
func writeTransaction<R>(callback: @escaping (any PowerSyncTransaction) -> R) async throws -> R
4374

Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift

+7-4
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ final class KotlinPowerSyncDatabaseImplTests: XCTestCase {
3838
parameters: ["1"]
3939
) { cursor in
4040
(
41-
cursor.getString(index: 0)!,
42-
cursor.getString(index: 1)!,
43-
cursor.getString(index: 2)!
41+
try cursor.getString(name: "id"),
42+
try cursor.getString(name: "name"),
43+
try cursor.getString(name: "email")
4444
)
4545
}
4646

@@ -84,7 +84,10 @@ final class KotlinPowerSyncDatabaseImplTests: XCTestCase {
8484
sql: "SELECT id, name FROM users ORDER BY id",
8585
parameters: nil
8686
) { cursor in
87-
(cursor.getString(index: 0)!, cursor.getString(index: 1)!)
87+
(
88+
try cursor.getString(name: "id"),
89+
try cursor.getString(name: "name")
90+
)
8891
}
8992

9093
XCTAssertEqual(users.count, 2)

0 commit comments

Comments
 (0)