Skip to content

Commit a0682c2

Browse files
committed
Bring back static sqlite linking
1 parent 03796e7 commit a0682c2

File tree

21 files changed

+621
-118
lines changed

21 files changed

+621
-118
lines changed

core/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ kotlin {
193193
// static-sqlite-driver to link SQLite and have our own bindings implementing the
194194
// driver. The reason for this is that androidx.sqlite-bundled causes linker errors for
195195
// our Swift SDK.
196+
implementation(projects.staticSqliteDriver)
196197
}
197198

198199
// Common apple targets where we link the core extension dynamically

core/src/androidMain/kotlin/com/powersync/DatabaseDriverFactory.android.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ public actual class DatabaseDriverFactory(
1212

1313
internal actual fun resolveDefaultDatabasePath(dbFilename: String): String = context.getDatabasePath(dbFilename).path
1414

15-
internal actual fun openConnection(path: String, openFlags: Int): SQLiteConnection {
16-
return driver.open(path, openFlags)
17-
}
15+
internal actual fun openConnection(
16+
path: String,
17+
openFlags: Int,
18+
): SQLiteConnection = driver.open(path, openFlags)
1819
}
1920

2021
public fun BundledSQLiteDriver.addPowerSyncExtension() {

core/src/appleNonWatchOsMain/kotlin/com/powersync/DatabaseDriverFactory.appleNonWatchOs.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import androidx.sqlite.SQLiteConnection
44
import com.powersync.sqlite.Database
55
import com.powersync.sqlite.SqliteException
66

7-
87
@Suppress(names = ["EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING"])
98
public actual class DatabaseDriverFactory {
109
internal actual fun resolveDefaultDatabasePath(dbFilename: String): String = appleDefaultDatabasePath(dbFilename)
1110

12-
internal actual fun openConnection(path: String, openFlags: Int): SQLiteConnection {
11+
internal actual fun openConnection(
12+
path: String,
13+
openFlags: Int,
14+
): SQLiteConnection {
1315
val db = Database.open(path, openFlags)
1416
try {
1517
db.loadExtension(powerSyncExtensionPath, "sqlite3_powersync_init")

core/src/appleTest/kotlin/com/powersync/sqlite/DatabaseTest.kt

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,33 @@ package com.powersync.sqlite
22

33
import androidx.sqlite.SQLiteConnection
44
import androidx.sqlite.execSQL
5+
import io.kotest.assertions.throwables.shouldThrow
56
import io.kotest.matchers.shouldBe
67
import kotlin.test.Test
78

89
class DatabaseTest {
910
@Test
10-
fun testInTransaction() = inMemoryDatabase().use {
11-
it.inTransaction() shouldBe false
12-
it.execSQL("BEGIN")
13-
it.inTransaction() shouldBe true
14-
it.execSQL("COMMIT")
15-
it.inTransaction() shouldBe false
11+
fun testInTransaction() =
12+
inMemoryDatabase().use {
13+
it.inTransaction() shouldBe false
14+
it.execSQL("BEGIN")
15+
it.inTransaction() shouldBe true
16+
it.execSQL("COMMIT")
17+
it.inTransaction() shouldBe false
1618

17-
Unit
18-
}
19+
Unit
20+
}
1921

20-
private companion object {
21-
private fun inMemoryDatabase(): SQLiteConnection {
22-
return Database.open(":memory", 0)
22+
@Test
23+
fun syntaxError() =
24+
inMemoryDatabase().use {
25+
val exception = shouldThrow<SqliteException> { it.execSQL("bad syntax") }
26+
27+
exception.toString() shouldBe "SqliteException(1): SQL logic error at offset 0, near \"bad\": syntax error for SQL: bad syntax"
28+
Unit
2329
}
30+
31+
private companion object {
32+
private fun inMemoryDatabase(): SQLiteConnection = Database.open(":memory:", 2)
2433
}
2534
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package com.powersync.sqlite
2+
3+
import androidx.sqlite.SQLiteConnection
4+
import io.kotest.assertions.throwables.shouldThrow
5+
import io.kotest.matchers.shouldBe
6+
import kotlin.test.Test
7+
8+
class StatementTest {
9+
@Test
10+
fun testBind() =
11+
inMemoryDatabase().use { db ->
12+
db.prepare("SELECT json_array(?, ?, ?, ?, hex(?))").use { stmt ->
13+
stmt.bindDouble(1, 3.14)
14+
stmt.bindLong(2, 42)
15+
stmt.bindText(3, "foo")
16+
stmt.bindNull(4)
17+
stmt.bindBlob(5, byteArrayOf(1, 2, 3))
18+
19+
stmt.step() shouldBe true
20+
stmt.getColumnType(0) shouldBe Statement.SQLITE_TEXT
21+
stmt.getText(0) shouldBe "[3.14,42,\"foo\",null,\"010203\"]"
22+
}
23+
24+
Unit
25+
}
26+
27+
@Test
28+
fun testBindOutOfBounds() =
29+
inMemoryDatabase().use { db ->
30+
db.prepare("SELECT ?").use { stmt ->
31+
shouldThrow<SqliteException> {
32+
stmt.bindText(-1, "foo")
33+
}
34+
shouldThrow<SqliteException> {
35+
stmt.bindText(0, "foo")
36+
}
37+
38+
stmt.bindText(1, "foo")
39+
40+
shouldThrow<SqliteException> {
41+
stmt.bindText(2, "foo")
42+
}
43+
}
44+
Unit
45+
}
46+
47+
@Test
48+
fun getBlob() =
49+
inMemoryDatabase().use { db ->
50+
db.prepare("SELECT unhex('010203')").use { stmt ->
51+
stmt.step() shouldBe true
52+
stmt.getColumnType(0) shouldBe Statement.SQLITE_BLOB
53+
stmt.getBlob(0) shouldBe byteArrayOf(1, 2, 3)
54+
}
55+
Unit
56+
}
57+
58+
@Test
59+
fun getDouble() =
60+
inMemoryDatabase().use { db ->
61+
db.prepare("SELECT 3.14").use { stmt ->
62+
stmt.step() shouldBe true
63+
stmt.getColumnType(0) shouldBe Statement.SQLITE_FLOAT
64+
stmt.getDouble(0) shouldBe 3.14
65+
}
66+
Unit
67+
}
68+
69+
@Test
70+
fun getLong() =
71+
inMemoryDatabase().use { db ->
72+
db.prepare("SELECT 123").use { stmt ->
73+
stmt.step() shouldBe true
74+
stmt.getColumnType(0) shouldBe Statement.SQLITE_INTEGER
75+
stmt.getLong(0) shouldBe 123L
76+
stmt.getInt(0) shouldBe 123
77+
}
78+
Unit
79+
}
80+
81+
@Test
82+
fun getText() =
83+
inMemoryDatabase().use { db ->
84+
db.prepare("SELECT 'hello kotlin'").use { stmt ->
85+
stmt.step() shouldBe true
86+
stmt.getColumnType(0) shouldBe Statement.SQLITE_TEXT
87+
stmt.getText(0) shouldBe "hello kotlin"
88+
}
89+
Unit
90+
}
91+
92+
@Test
93+
fun getNull() =
94+
inMemoryDatabase().use { db ->
95+
db.prepare("SELECT null").use { stmt ->
96+
stmt.step() shouldBe true
97+
stmt.isNull(0) shouldBe true
98+
}
99+
Unit
100+
}
101+
102+
@Test
103+
fun getOutOfBound() =
104+
inMemoryDatabase().use { db ->
105+
db.prepare("SELECT null").use { stmt ->
106+
stmt.step() shouldBe true
107+
108+
shouldThrow<IllegalArgumentException> {
109+
stmt.getInt(-1)
110+
}
111+
112+
shouldThrow<IllegalArgumentException> {
113+
stmt.getInt(1)
114+
}
115+
}
116+
Unit
117+
}
118+
119+
private companion object {
120+
private fun inMemoryDatabase(): SQLiteConnection = Database.open(":memory:", 2)
121+
}
122+
}

core/src/commonMain/kotlin/com/powersync/DatabaseDriverFactory.kt

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.powersync
22

33
import androidx.sqlite.SQLiteConnection
44

5-
65
@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
76
public expect class DatabaseDriverFactory {
87
internal fun resolveDefaultDatabasePath(dbFilename: String): String
@@ -12,7 +11,10 @@ public expect class DatabaseDriverFactory {
1211
*
1312
* The connection should have the PowerSync core extension loaded.
1413
*/
15-
internal fun openConnection(path: String, openFlags: Int): SQLiteConnection
14+
internal fun openConnection(
15+
path: String,
16+
openFlags: Int,
17+
): SQLiteConnection
1618
}
1719

1820
@OptIn(ExperimentalPowerSyncAPI::class)
@@ -29,11 +31,14 @@ internal fun openDatabase(
2931
factory.resolveDefaultDatabasePath(dbFilename)
3032
}
3133

32-
return factory.openConnection(dbPath, if (readOnly) {
33-
SQLITE_OPEN_READONLY
34-
} else {
35-
SQLITE_OPEN_READWRITE or SQLITE_OPEN_CREATE
36-
},)
34+
return factory.openConnection(
35+
dbPath,
36+
if (readOnly) {
37+
SQLITE_OPEN_READONLY
38+
} else {
39+
SQLITE_OPEN_READWRITE or SQLITE_OPEN_CREATE
40+
},
41+
)
3742
}
3843

3944
private const val SQLITE_OPEN_READONLY = 0x01

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ public actual class DatabaseDriverFactory {
99

1010
internal actual fun resolveDefaultDatabasePath(dbFilename: String): String = dbFilename
1111

12-
internal actual fun openConnection(path: String, openFlags: Int): SQLiteConnection {
13-
return driver.open(path, openFlags)
14-
}
12+
internal actual fun openConnection(
13+
path: String,
14+
openFlags: Int,
15+
): SQLiteConnection = driver.open(path, openFlags)
1516
}
1617

1718
public fun BundledSQLiteDriver.addPowerSyncExtension() {

core/src/nativeMain/interop/sqlite3.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,14 @@ int sqlite3_load_extension(
3434
);
3535

3636
// Statements
37-
int sqlite3_prepare_v3(sqlite3 *db, const char *zSql, int nByte,
38-
unsigned int prepFlags, sqlite3_stmt **ppStmt,
39-
const char **pzTail);
37+
int sqlite3_prepare16_v3(
38+
sqlite3 *db, /* Database handle */
39+
const void *zSql, /* SQL statement, UTF-16 encoded */
40+
int nByte, /* Maximum length of zSql in bytes. */
41+
unsigned int prepFlags, /* Zero or more SQLITE_PREPARE_ flags */
42+
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
43+
const void **pzTail /* OUT: Pointer to unused portion of zSql */
44+
);
4045
int sqlite3_finalize(sqlite3_stmt *pStmt);
4146
int sqlite3_step(sqlite3_stmt *pStmt);
4247
int sqlite3_reset(sqlite3_stmt *pStmt);
@@ -57,7 +62,7 @@ int sqlite3_bind_text16(sqlite3_stmt *pStmt, int index, char *data,
5762
void *sqlite3_column_blob(sqlite3_stmt *pStmt, int iCol);
5863
double sqlite3_column_double(sqlite3_stmt *pStmt, int iCol);
5964
int64_t sqlite3_column_int64(sqlite3_stmt *pStmt, int iCol);
60-
char *sqlite3_column_text(sqlite3_stmt *pStmt, int iCol);
65+
void *sqlite3_column_text16(sqlite3_stmt *pStmt, int iCol);
6166
int sqlite3_column_bytes(sqlite3_stmt *pStmt, int iCol);
6267
int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int iCol);
6368
int sqlite3_column_type(sqlite3_stmt *pStmt, int iCol);

0 commit comments

Comments
 (0)