-
Notifications
You must be signed in to change notification settings - Fork 19
Swift Driver Additions #262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
49 commits
Select commit
Hold shift + click to select a range
136e809
New unified driver interfaces
simolus3 435c7c5
Fix leaking statements
simolus3 522f4c6
Add raw connection API
simolus3 2359b48
Add changelog entry
simolus3 f4d98d5
Lease API that works better with Room
simolus3 eccb7f7
Notify updates from raw statements
simolus3 54829bc
Delete more driver stuff
simolus3 f532ba9
Fix deadlock in initialization
simolus3 c7adbab
Make addPowerSyncExtension public
simolus3 011b1da
Actually, use callbacks
simolus3 c87e079
merge main
simolus3 cb1373d
Add docs
simolus3 aa4e7bc
Fix lints
simolus3 03796e7
Add native sqlite driver
simolus3 a0682c2
Bring back static sqlite linking
simolus3 8f5f8cd
Fix linter errors
simolus3 f41b0a4
Fix Swift tests
simolus3 51521d8
Delete proguard rules
simolus3 fd04adc
grdb drivers
stevensJourney b32f7bf
wip: lease all connections
stevensJourney 9008648
revert databasegroup changes.
stevensJourney d6697d9
Merge branch 'main' into grdb-drivers
stevensJourney a92930a
update after merging
stevensJourney ef4160c
revert test change
stevensJourney 068d8ed
Merge remote-tracking branch 'origin/main' into grdb-drivers
stevensJourney c0bdde9
improve error handling
stevensJourney 937d452
Use SQLite Session API for Swift updates.
stevensJourney 59408b0
Code cleanup. Fix lint error.
stevensJourney 07267c1
Merge remote-tracking branch 'origin/main' into grdb-drivers
stevensJourney 587934c
cleanup APIs for sessions
stevensJourney 5434a5f
Merge remote-tracking branch 'origin/main' into grdb-drivers
stevensJourney 9f855e4
move Swift pool logic
stevensJourney 11add5c
Add changelog entry
stevensJourney be8617e
Start moving code into common
simolus3 97e7c1b
Share common logic in test
simolus3 f16eedf
Well, compiling works
simolus3 eaaeaed
Fixing some tests
simolus3 13dd40e
Update readmes
simolus3 fbaa0da
Reformat
simolus3 f5cd4c7
Fix supabase test
simolus3 23cb1d9
Fix watchos tests
simolus3 55f0f28
Merge branch 'common-module' into grdb-drivers-sqlite-update
stevensJourney 3f0d775
update connection pool
stevensJourney d26b9c6
refactor withSession for return type
stevensJourney 81ef883
refactor withSession for return type
stevensJourney 9641e3b
Helpers for Swift Strict Concurrency
stevensJourney a333ab5
Merge branch 'grdb-drivers-sqlite-update' into grdb-drivers
stevensJourney 1f496d6
Merge branch 'main' into grdb-drivers
stevensJourney 222e575
Linter fix
stevensJourney File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
headers = sqlite3.h | ||
|
||
noStringConversion = sqlite3_prepare_v3 | ||
noStringConversion = sqlite3_prepare_v3,sqlite3session_create |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/pool/RawConnectionLease.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.powersync.pool | ||
|
||
import androidx.sqlite.SQLiteStatement | ||
import com.powersync.db.driver.SQLiteConnectionLease | ||
import com.powersync.sqlite.Database | ||
|
||
internal class RawConnectionLease( | ||
lease: SwiftLeaseAdapter, | ||
) : SQLiteConnectionLease { | ||
private var isCompleted = false | ||
|
||
private var db = Database(lease.pointer) | ||
|
||
private fun checkNotCompleted() { | ||
check(!isCompleted) { "Connection lease already closed" } | ||
} | ||
|
||
override suspend fun isInTransaction(): Boolean = isInTransactionSync() | ||
|
||
override fun isInTransactionSync(): Boolean { | ||
checkNotCompleted() | ||
return db.inTransaction() | ||
} | ||
|
||
override suspend fun <R> usePrepared( | ||
sql: String, | ||
block: (SQLiteStatement) -> R, | ||
): R = usePreparedSync(sql, block) | ||
|
||
override fun <R> usePreparedSync( | ||
sql: String, | ||
block: (SQLiteStatement) -> R, | ||
): R { | ||
checkNotCompleted() | ||
return db.prepare(sql).use(block) | ||
} | ||
} |
173 changes: 173 additions & 0 deletions
173
internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/pool/SessionResult.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
package com.powersync.pool | ||
|
||
import cnames.structs.sqlite3 | ||
import cnames.structs.sqlite3_changeset_iter | ||
import cnames.structs.sqlite3_session | ||
import com.powersync.PowerSyncException | ||
import com.powersync.PowerSyncResult | ||
import com.powersync.db.runWrapped | ||
import com.powersync.internal.sqlite3.sqlite3_free | ||
import com.powersync.internal.sqlite3.sqlite3changeset_finalize | ||
import com.powersync.internal.sqlite3.sqlite3changeset_next | ||
import com.powersync.internal.sqlite3.sqlite3changeset_op | ||
import com.powersync.internal.sqlite3.sqlite3changeset_start | ||
import com.powersync.internal.sqlite3.sqlite3session_attach | ||
import com.powersync.internal.sqlite3.sqlite3session_changeset | ||
import com.powersync.internal.sqlite3.sqlite3session_create | ||
import com.powersync.internal.sqlite3.sqlite3session_delete | ||
import kotlinx.cinterop.ByteVar | ||
import kotlinx.cinterop.COpaquePointerVar | ||
import kotlinx.cinterop.CPointer | ||
import kotlinx.cinterop.CPointerVar | ||
import kotlinx.cinterop.IntVar | ||
import kotlinx.cinterop.alloc | ||
import kotlinx.cinterop.memScoped | ||
import kotlinx.cinterop.ptr | ||
import kotlinx.cinterop.toKString | ||
import kotlinx.cinterop.value | ||
|
||
public data class SessionResult( | ||
val blockResult: PowerSyncResult, | ||
val affectedTables: Set<String>, | ||
) | ||
|
||
/** | ||
* We typically have a few options for table update hooks: | ||
* 1.) Registering a hook with SQLite | ||
* 2.) Using our Rust core to register update hooks | ||
* 3.) Receiving updates from an external API | ||
* | ||
* In some cases, particularly in the case of GRDB, none of these options are viable. | ||
* GRDB dynamically registers (and unregisters) its own update hooks and its update hook logic | ||
* does not report changes for operations made outside of its own APIs. | ||
* | ||
* 1.) We can't register our own hooks since GRDB might override it or our hook could conflict with GRDB's | ||
* 2.) We can't register hooks due to above | ||
* 3.) The GRDB APIs only report changes if made with their SQLite execution APIs. It's not trivial to implement [com.powersync.db.driver.SQLiteConnectionLease] with their APIs. | ||
* | ||
* This function provides an alternative method of obtaining table changes by using SQLite sessions. | ||
* https://www.sqlite.org/sessionintro.html | ||
* | ||
* We start a session, execute a block of code, and then extract the changeset from the session. | ||
* We then parse the changeset to extract the table names that were modified. | ||
* This approach is more heavyweight than using update hooks, but it works in scenarios where | ||
* update hooks are not currently feasible. | ||
*/ | ||
@Throws(PowerSyncException::class) | ||
public fun withSession( | ||
db: CPointer<sqlite3>, | ||
block: () -> PowerSyncResult, | ||
): SessionResult = | ||
runWrapped { | ||
memScoped { | ||
val sessionPtr = alloc<CPointerVar<sqlite3_session>>() | ||
|
||
val rc = | ||
sqlite3session_create( | ||
db, | ||
"main", | ||
sessionPtr.ptr, | ||
).checkResult("Could not create SQLite session") | ||
|
||
val session = | ||
sessionPtr.value ?: throw PowerSyncException( | ||
"Could not create SQLite session", | ||
cause = Error(), | ||
) | ||
|
||
try { | ||
// Attach all tables to track changes | ||
sqlite3session_attach( | ||
session, | ||
null, | ||
).checkResult("Could not attach all tables to session") // null means all tables | ||
|
||
// Execute the block where changes happen | ||
val result = block() | ||
|
||
// Get the changeset | ||
val changesetSizePtr = alloc<IntVar>() | ||
val changesetPtr = alloc<COpaquePointerVar>() | ||
|
||
sqlite3session_changeset( | ||
session, | ||
changesetSizePtr.ptr, | ||
changesetPtr.ptr, | ||
).checkResult("Could not get changeset from session") | ||
|
||
val changesetSize = changesetSizePtr.value | ||
val changeset = changesetPtr.value | ||
|
||
if (changesetSize == 0 || changeset == null) { | ||
return@memScoped SessionResult( | ||
result, | ||
affectedTables = emptySet(), | ||
) | ||
} | ||
|
||
// Parse the changeset to extract table names | ||
val changedTables = mutableSetOf<String>() | ||
val iterPtr = alloc<CPointerVar<sqlite3_changeset_iter>>() | ||
|
||
sqlite3changeset_start( | ||
iterPtr.ptr, | ||
changesetSize, | ||
changeset, | ||
).checkResult("Could not start changeset iterator") | ||
|
||
val iter = iterPtr.value | ||
|
||
if (iter == null) { | ||
return@memScoped SessionResult( | ||
result, | ||
affectedTables = emptySet(), | ||
) | ||
} | ||
|
||
try { | ||
// Iterate through all changes | ||
while (sqlite3changeset_next(iter) == 100) { | ||
val tableNamePtr = alloc<CPointerVar<ByteVar>>() | ||
val nColPtr = alloc<IntVar>() | ||
val opPtr = alloc<IntVar>() | ||
val indirectPtr = alloc<IntVar>() | ||
|
||
val opRc = | ||
sqlite3changeset_op( | ||
iter, | ||
tableNamePtr.ptr, | ||
nColPtr.ptr, | ||
opPtr.ptr, | ||
indirectPtr.ptr, | ||
) | ||
|
||
if (opRc == 0) { | ||
val tableNameCPtr = tableNamePtr.value | ||
if (tableNameCPtr != null) { | ||
val tableName = tableNameCPtr.toKString() | ||
changedTables.add(tableName) | ||
} | ||
} | ||
} | ||
} finally { | ||
sqlite3changeset_finalize(iter) | ||
// Free the changeset memory | ||
sqlite3_free(changeset) | ||
} | ||
|
||
return@memScoped SessionResult( | ||
result, | ||
affectedTables = changedTables.toSet(), | ||
) | ||
} finally { | ||
// Clean up the session | ||
sqlite3session_delete(session) | ||
} | ||
} | ||
} | ||
|
||
private fun Int.checkResult(message: String) { | ||
if (this != 0) { | ||
throw PowerSyncException("SQLite error code: $this", cause = Error(message)) | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.