Skip to content

Commit 4004fa3

Browse files
committed
Finish tests
1 parent 3f7537a commit 4004fa3

File tree

14 files changed

+297
-82
lines changed

14 files changed

+297
-82
lines changed

core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ internal class PowerSyncDatabaseImpl(
107107
logger.d { "PowerSyncVersion: $powerSyncVersion" }
108108

109109
internalDb.writeTransaction { tx ->
110-
tx.getOptional("SELECT powersync_init()") {}
110+
tx.async.getOptional("SELECT powersync_init()") {}
111111
}
112112

113113
updateSchemaInternal(schema)

core/src/commonMain/kotlin/com/powersync/db/Queries.kt

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,15 @@ import kotlin.time.Duration.Companion.milliseconds
1313

1414
public fun interface ThrowableTransactionCallback<R> {
1515
@Throws(PowerSyncException::class, kotlinx.coroutines.CancellationException::class)
16-
public fun execute(transaction: PowerSyncTransaction): R
16+
public suspend fun execute(transaction: PowerSyncTransaction): R
1717
}
1818

1919
public fun interface ThrowableLockCallback<R> {
2020
@Throws(PowerSyncException::class, kotlinx.coroutines.CancellationException::class)
21-
public fun execute(context: ConnectionContext): R
21+
public suspend fun execute(context: ConnectionContext): R
2222
}
2323

24-
public interface Queries {
25-
public companion object {
26-
/**
27-
* The default throttle duration for [onChange] and [watch] operations.
28-
*/
29-
public val DEFAULT_THROTTLE: Duration = 30.milliseconds
30-
}
31-
24+
public interface QueryRunner {
3225
/**
3326
* Executes a write query (INSERT, UPDATE, DELETE).
3427
*
@@ -94,6 +87,15 @@ public interface Queries {
9487
parameters: List<Any?>? = listOf(),
9588
mapper: (SqlCursor) -> RowType,
9689
): RowType?
90+
}
91+
92+
public interface Queries : QueryRunner {
93+
public companion object {
94+
/**
95+
* The default throttle duration for [onChange] and [watch] operations.
96+
*/
97+
public val DEFAULT_THROTTLE: Duration = 30.milliseconds
98+
}
9799

98100
/**
99101
* Returns a [Flow] that emits whenever the source tables are modified.

core/src/commonMain/kotlin/com/powersync/db/driver/RawConnectionLease.kt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ internal class RawConnectionLease(
1919
}
2020

2121
override suspend fun isInTransaction(): Boolean {
22-
return isInTransactionSync()
23-
}
24-
25-
override fun isInTransactionSync(): Boolean {
2622
checkNotCompleted()
2723
return connection.inTransaction()
2824
}
@@ -31,10 +27,6 @@ internal class RawConnectionLease(
3127
sql: String,
3228
block: (SQLiteStatement) -> R
3329
): R {
34-
return usePreparedSync(sql, block)
35-
}
36-
37-
override fun <R> usePreparedSync(sql: String, block: (SQLiteStatement) -> R): R {
3830
checkNotCompleted()
3931
return connection.prepare(sql).use(block)
4032
}

core/src/commonMain/kotlin/com/powersync/db/driver/SQLiteConnectionPool.kt

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,13 @@ public interface SQLiteConnectionLease {
2727
*/
2828
public suspend fun isInTransaction(): Boolean
2929

30-
public fun isInTransactionSync(): Boolean {
31-
return runBlocking { isInTransaction() }
32-
}
33-
3430
/**
3531
* Prepares [sql] as statement and runs [block] with it.
3632
*
3733
* Block most only run on a single-thread. The statement must not be used once [block] returns.
3834
*/
3935
public suspend fun <R> usePrepared(sql: String, block: (SQLiteStatement) -> R): R
4036

41-
public fun <R> usePreparedSync(sql: String, block: (SQLiteStatement) -> R): R {
42-
return runBlocking {
43-
usePrepared(sql, block)
44-
}
45-
}
46-
4737
public suspend fun execSQL(sql: String) {
4838
usePrepared(sql) {
4939
it.step()

core/src/commonMain/kotlin/com/powersync/db/internal/ConnectionContext.kt

Lines changed: 70 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ package com.powersync.db.internal
33
import androidx.sqlite.SQLiteStatement
44
import com.powersync.ExperimentalPowerSyncAPI
55
import com.powersync.PowerSyncException
6+
import com.powersync.db.QueryRunner
67
import com.powersync.db.SqlCursor
78
import com.powersync.db.StatementBasedCursor
89
import com.powersync.db.driver.SQLiteConnectionLease
10+
import kotlinx.coroutines.runBlocking
911

1012
public interface ConnectionContext {
1113
// TODO (breaking): Make asynchronous, create shared superinterface with Queries
1214

15+
public val async: QueryRunner
16+
1317
@Throws(PowerSyncException::class)
1418
public fun execute(
1519
sql: String,
@@ -38,13 +42,53 @@ public interface ConnectionContext {
3842
): RowType
3943
}
4044

41-
@ExperimentalPowerSyncAPI
42-
internal class ConnectionContextImplementation(
43-
private val rawConnection: SQLiteConnectionLease,
44-
) : ConnectionContext {
45+
/**
46+
* An implementation of a [ConnectionContext] that delegates to a [QueryRunner] via [runBlocking].
47+
*/
48+
internal abstract class BaseConnectionContextImplementation(): ConnectionContext {
4549
override fun execute(
4650
sql: String,
4751
parameters: List<Any?>?,
52+
): Long = runBlocking { async.execute(sql, parameters) }
53+
54+
override fun <RowType : Any> getOptional(
55+
sql: String,
56+
parameters: List<Any?>?,
57+
mapper: (SqlCursor) -> RowType,
58+
): RowType? = runBlocking {
59+
async.getOptional(sql, parameters, mapper)
60+
}
61+
62+
override fun <RowType : Any> getAll(
63+
sql: String,
64+
parameters: List<Any?>?,
65+
mapper: (SqlCursor) -> RowType,
66+
): List<RowType> =
67+
runBlocking {
68+
async.getAll(sql, parameters, mapper)
69+
}
70+
71+
override fun <RowType : Any> get(
72+
sql: String,
73+
parameters: List<Any?>?,
74+
mapper: (SqlCursor) -> RowType,
75+
): RowType = runBlocking {
76+
async.get(sql, parameters, mapper)
77+
}
78+
}
79+
80+
@OptIn(ExperimentalPowerSyncAPI::class)
81+
internal class ConnectionContextImplementation(lease: SQLiteConnectionLease): BaseConnectionContextImplementation() {
82+
override val async = ContextQueryRunner(lease)
83+
}
84+
85+
@OptIn(ExperimentalPowerSyncAPI::class)
86+
internal class ContextQueryRunner(
87+
private val rawConnection: SQLiteConnectionLease
88+
): QueryRunner {
89+
override suspend fun execute(
90+
sql: String,
91+
parameters: List<Any?>?
4892
): Long {
4993
withStatement(sql, parameters) {
5094
while (it.step()) {
@@ -58,45 +102,43 @@ internal class ConnectionContextImplementation(
58102
}
59103
}
60104

61-
override fun <RowType : Any> getOptional(
105+
override suspend fun <RowType : Any> get(
62106
sql: String,
63107
parameters: List<Any?>?,
64-
mapper: (SqlCursor) -> RowType,
65-
): RowType? =
66-
withStatement(sql, parameters) { stmt ->
67-
if (stmt.step()) {
68-
mapper(StatementBasedCursor(stmt))
69-
} else {
70-
null
71-
}
72-
}
108+
mapper: (SqlCursor) -> RowType
109+
): RowType = getOptional(sql, parameters, mapper) ?: throw PowerSyncException("get() called with query that returned no rows", null)
73110

74-
override fun <RowType : Any> getAll(
111+
override suspend fun <RowType : Any> getAll(
75112
sql: String,
76113
parameters: List<Any?>?,
77-
mapper: (SqlCursor) -> RowType,
78-
): List<RowType> =
79-
withStatement(sql, parameters) { stmt ->
80-
buildList {
81-
val cursor = StatementBasedCursor(stmt)
82-
while (stmt.step()) {
83-
add(mapper(cursor))
84-
}
114+
mapper: (SqlCursor) -> RowType
115+
): List<RowType> = withStatement(sql, parameters) { stmt ->
116+
buildList {
117+
val cursor = StatementBasedCursor(stmt)
118+
while (stmt.step()) {
119+
add(mapper(cursor))
85120
}
86121
}
122+
}
87123

88-
override fun <RowType : Any> get(
124+
override suspend fun <RowType : Any> getOptional(
89125
sql: String,
90126
parameters: List<Any?>?,
91-
mapper: (SqlCursor) -> RowType,
92-
): RowType = getOptional(sql, parameters, mapper) ?: throw PowerSyncException("get() called with query that returned no rows", null)
127+
mapper: (SqlCursor) -> RowType
128+
): RowType? = withStatement(sql, parameters) { stmt ->
129+
if (stmt.step()) {
130+
mapper(StatementBasedCursor(stmt))
131+
} else {
132+
null
133+
}
134+
}
93135

94-
private inline fun <T> withStatement(
136+
private suspend inline fun <T> withStatement(
95137
sql: String,
96138
parameters: List<Any?>?,
97139
crossinline block: (SQLiteStatement) -> T,
98140
): T {
99-
return rawConnection.usePreparedSync(sql) { stmt ->
141+
return rawConnection.usePrepared(sql) { stmt ->
100142
stmt.bind(parameters)
101143
block(stmt)
102144
}

core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@ internal class InternalDatabaseImpl(
3636
parameters: List<Any?>?,
3737
): Long =
3838
writeLock { context ->
39-
context.execute(sql, parameters)
39+
context.async.execute(sql, parameters)
4040
}
4141

4242
override suspend fun updateSchema(schemaJson: String) {
4343
withContext(dbContext) {
4444
runWrapped {
4545
pool.withAllConnections { writer, readers ->
4646
writer.runTransaction { tx ->
47-
tx.getOptional(
47+
tx.async.getOptional(
4848
"SELECT powersync_replace_schema(?);",
4949
listOf(schemaJson),
5050
) {}
@@ -63,19 +63,19 @@ internal class InternalDatabaseImpl(
6363
sql: String,
6464
parameters: List<Any?>?,
6565
mapper: (SqlCursor) -> RowType,
66-
): RowType = readLock { connection -> connection.get(sql, parameters, mapper) }
66+
): RowType = readLock { connection -> connection.async.get(sql, parameters, mapper) }
6767

6868
override suspend fun <RowType : Any> getAll(
6969
sql: String,
7070
parameters: List<Any?>?,
7171
mapper: (SqlCursor) -> RowType,
72-
): List<RowType> = readLock { connection -> connection.getAll(sql, parameters, mapper) }
72+
): List<RowType> = readLock { connection -> connection.async.getAll(sql, parameters, mapper) }
7373

7474
override suspend fun <RowType : Any> getOptional(
7575
sql: String,
7676
parameters: List<Any?>?,
7777
mapper: (SqlCursor) -> RowType,
78-
): RowType? = readLock { connection -> connection.getOptional(sql, parameters, mapper) }
78+
): RowType? = readLock { connection -> connection.async.getOptional(sql, parameters, mapper) }
7979

8080
override fun onChange(
8181
tables: Set<String>,

core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,65 @@ package com.powersync.db.internal
22

33
import com.powersync.ExperimentalPowerSyncAPI
44
import com.powersync.PowerSyncException
5+
import com.powersync.db.QueryRunner
56
import com.powersync.db.SqlCursor
67
import com.powersync.db.driver.SQLiteConnectionLease
78

89
public interface PowerSyncTransaction : ConnectionContext
910

1011
@ExperimentalPowerSyncAPI
1112
internal class PowerSyncTransactionImpl(
12-
private val lease: SQLiteConnectionLease,
13-
) : PowerSyncTransaction,
14-
ConnectionContext {
15-
private val delegate = ConnectionContextImplementation(lease)
13+
lease: SQLiteConnectionLease,
14+
) : PowerSyncTransaction, BaseConnectionContextImplementation() {
15+
override val async = AsyncPowerSyncTransactionImpl(lease)
16+
}
17+
18+
@OptIn(ExperimentalPowerSyncAPI::class)
19+
internal class AsyncPowerSyncTransactionImpl(
20+
private val lease: SQLiteConnectionLease
21+
): QueryRunner {
22+
23+
private val delegate = ContextQueryRunner(lease)
1624

17-
private fun checkInTransaction() {
18-
if (!lease.isInTransactionSync()) {
25+
private suspend fun checkInTransaction() {
26+
if (!lease.isInTransaction()) {
1927
throw PowerSyncException("Tried executing statement on a transaction that has been rolled back", cause = null)
2028
}
2129
}
2230

23-
override fun execute(
31+
override suspend fun execute(
2432
sql: String,
25-
parameters: List<Any?>?,
33+
parameters: List<Any?>?
2634
): Long {
2735
checkInTransaction()
2836
return delegate.execute(sql, parameters)
2937
}
3038

31-
override fun <RowType : Any> getOptional(
39+
override suspend fun <RowType : Any> get(
3240
sql: String,
3341
parameters: List<Any?>?,
34-
mapper: (SqlCursor) -> RowType,
35-
): RowType? {
42+
mapper: (SqlCursor) -> RowType
43+
): RowType {
3644
checkInTransaction()
37-
return delegate.getOptional(sql, parameters, mapper)
45+
return delegate.get(sql, parameters, mapper)
3846
}
3947

40-
override fun <RowType : Any> getAll(
48+
override suspend fun <RowType : Any> getAll(
4149
sql: String,
4250
parameters: List<Any?>?,
43-
mapper: (SqlCursor) -> RowType,
51+
mapper: (SqlCursor) -> RowType
4452
): List<RowType> {
4553
checkInTransaction()
4654
return delegate.getAll(sql, parameters, mapper)
4755
}
4856

49-
override fun <RowType : Any> get(
57+
override suspend fun <RowType : Any> getOptional(
5058
sql: String,
5159
parameters: List<Any?>?,
52-
mapper: (SqlCursor) -> RowType,
53-
): RowType {
60+
mapper: (SqlCursor) -> RowType
61+
): RowType? {
5462
checkInTransaction()
55-
return delegate.get(sql, parameters, mapper)
63+
return delegate.getOptional(sql, parameters, mapper)
5664
}
5765
}
5866

core/src/jvmMain/kotlin/com/powersync/DatabaseDriverFactory.jvm.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public actual class DatabaseDriverFactory {
1010
}
1111

1212
public actual fun BundledSQLiteDriver.addPowerSyncExtension() {
13-
addExtension(powersyncExtension, "sqlite3_powersync_init")
13+
addExtension("/Users/simon/src/powersync-sqlite-core/target/debug/libpowersync.dylib", "sqlite3_powersync_init")
1414
}
1515

1616
private val powersyncExtension: String by lazy { extractLib("powersync") }

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ coroutines = "1.10.2"
1616
kotlinx-datetime = "0.7.1"
1717
kotlinx-io = "0.8.0"
1818
ktor = "3.2.3"
19+
serialization = "1.9.0"
1920
uuid = "0.8.4"
2021
powersync-core = "0.4.4"
2122
turbine = "1.2.1"
@@ -90,6 +91,7 @@ ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "kto
9091
ktor-client-contentnegotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
9192
ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" }
9293
ktor-serialization-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
94+
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
9395
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
9496
kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" }
9597

0 commit comments

Comments
 (0)