expo-sqlite 58.0.10 → 58.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/android/build.gradle +6 -2
  3. package/android/src/main/cpp/NativeDatabaseBinding.cpp +11 -8
  4. package/android/src/main/cpp/NativeStatementBinding.cpp +43 -2
  5. package/android/src/main/cpp/NativeStatementBinding.h +3 -0
  6. package/android/src/main/cpp/SQLiteError.h +34 -0
  7. package/android/src/main/java/expo/modules/sqlite/DatabaseConnection.kt +68 -0
  8. package/android/src/main/java/expo/modules/sqlite/NativeDatabase.kt +135 -22
  9. package/android/src/main/java/expo/modules/sqlite/NativeSession.kt +126 -6
  10. package/android/src/main/java/expo/modules/sqlite/NativeStatement.kt +199 -14
  11. package/android/src/main/java/expo/modules/sqlite/NativeStatementBinding.kt +1 -0
  12. package/android/src/main/java/expo/modules/sqlite/SQLRecords.kt +19 -13
  13. package/android/src/main/java/expo/modules/sqlite/SQLiteModule.kt +176 -577
  14. package/android/src/main/java/expo/modules/sqlite/SQLiteOptions.kt +3 -10
  15. package/android/src/main/java/expo/modules/sqlite/SQLiteValues.kt +47 -0
  16. package/build/NativeDatabase.d.ts +2 -2
  17. package/build/SQLiteDatabase.js +9 -3
  18. package/build/SQLiteDatabase.js.map +1 -1
  19. package/build/WebConfiguration.d.ts +31 -0
  20. package/build/WebConfiguration.js +21 -0
  21. package/build/WebConfiguration.js.map +1 -0
  22. package/build/index.d.ts +1 -0
  23. package/build/index.js +1 -0
  24. package/build/index.js.map +1 -1
  25. package/dev-plugin-dist/+not-found.html +2 -4
  26. package/dev-plugin-dist/_expo/static/js/web/{__common-9b94dd2fbdbb825a662f42cf8d2077ee.js → __common-3e95e247f8ee6d975454b07047f39148.js} +52 -48
  27. package/dev-plugin-dist/_expo/static/js/web/{_layout-b2894030c1dfbd95b717a85901b6c271.js → _layout-0bc906c56b363d38baf86696db19957e.js} +23 -23
  28. package/dev-plugin-dist/_expo/static/js/web/{entry-04fdb7d857a65c0ed638ef2d774a4b6e.js → entry-293efbd088ab66af1b3334c3eeafa61f.js} +697 -696
  29. package/dev-plugin-dist/_expo/static/js/web/{index-4bf2da199f699eb47a42074d5e183c07.js → index-454a4f95007576080f4f47d70441d41a.js} +1740 -1740
  30. package/dev-plugin-dist/_expo/static/js/web/{worker-d51aa6be89e271e571111274ab8e7f5c.js → worker-172ab4a50823f055f5bd777d90d1a36a.js} +17 -17
  31. package/dev-plugin-dist/_sitemap.html +2 -4
  32. package/dev-plugin-dist/index.html +2 -4
  33. package/expo-module.config.json +0 -4
  34. package/ios/DatabaseFileUtils.swift +101 -5
  35. package/ios/NativeDatabase.swift +5 -8
  36. package/ios/NativeStatement.swift +20 -13
  37. package/ios/SQLiteModule.swift +8 -2
  38. package/ios/SQLiteResult.swift +31 -0
  39. package/ios/Tests/DatabaseFileUtilsTests.swift +119 -0
  40. package/ios/Tests/test_ErrorReporting.swift +25 -0
  41. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar +0 -0
  42. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar.md5 +1 -0
  43. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar.sha1 +1 -0
  44. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar.sha256 +1 -0
  45. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar.sha512 +1 -0
  46. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar +0 -0
  47. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar.md5 +1 -0
  48. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar.sha1 +1 -0
  49. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar.sha256 +1 -0
  50. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar.sha512 +1 -0
  51. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/{58.0.10/expo.modules.sqlite-58.0.10.module → 58.0.12/expo.modules.sqlite-58.0.12.module} +22 -22
  52. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.module.md5 +1 -0
  53. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.module.sha1 +1 -0
  54. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.module.sha256 +1 -0
  55. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.module.sha512 +1 -0
  56. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/{58.0.10/expo.modules.sqlite-58.0.10.pom → 58.0.12/expo.modules.sqlite-58.0.12.pom} +1 -1
  57. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.pom.md5 +1 -0
  58. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.pom.sha1 +1 -0
  59. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.pom.sha256 +1 -0
  60. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.pom.sha512 +1 -0
  61. package/package.json +4 -4
  62. package/src/NativeDatabase.ts +2 -2
  63. package/src/SQLiteDatabase.ts +11 -3
  64. package/src/WebConfiguration.ts +36 -0
  65. package/src/index.ts +1 -0
  66. package/web/SQLiteModule.ts +16 -0
  67. package/web/WorkerChannel.ts +17 -9
  68. package/web/wa-sqlite/sqlite-api.js +4 -1
  69. package/web/web.types.ts +6 -0
  70. package/web/worker.ts +50 -38
  71. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar +0 -0
  72. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar.md5 +0 -1
  73. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar.sha1 +0 -1
  74. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar.sha256 +0 -1
  75. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar.sha512 +0 -1
  76. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar +0 -0
  77. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar.md5 +0 -1
  78. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar.sha1 +0 -1
  79. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar.sha256 +0 -1
  80. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar.sha512 +0 -1
  81. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.module.md5 +0 -1
  82. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.module.sha1 +0 -1
  83. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.module.sha256 +0 -1
  84. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.module.sha512 +0 -1
  85. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.pom.md5 +0 -1
  86. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.pom.sha1 +0 -1
  87. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.pom.sha256 +0 -1
  88. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.pom.sha512 +0 -1
@@ -2,318 +2,177 @@
2
2
 
3
3
  package expo.modules.sqlite
4
4
 
5
- import android.content.Context
6
5
  import androidx.core.net.toFile
7
6
  import androidx.core.net.toUri
8
- import androidx.core.os.bundleOf
9
- import expo.modules.kotlin.exception.Exceptions
10
- import expo.modules.kotlin.jni.ArrayBuffer
11
- import expo.modules.kotlin.modules.Module
12
- import expo.modules.kotlin.modules.ModuleDefinition
13
- import kotlinx.coroutines.CoroutineScope
14
- import kotlinx.coroutines.Dispatchers
7
+ import io.github.expo.modules.v2.Constant
8
+ import io.github.expo.modules.v2.Event
9
+ import io.github.expo.modules.v2.ExpoModule
10
+ import io.github.expo.modules.v2.JS
11
+ import io.github.expo.modules.v2.Module
12
+ import io.github.expo.modules.v2.react.androidContext
15
13
  import java.io.File
16
14
  import java.io.IOException
15
+ import java.util.concurrent.ExecutorService
16
+ import java.util.concurrent.Executors
17
17
 
18
18
  private const val MEMORY_DB_NAME = ":memory:"
19
19
 
20
- @Suppress("unused")
20
+ @ExpoModule(
21
+ name = "ExpoSQLite",
22
+ classes = [NativeDatabase::class, NativeStatement::class, NativeSession::class]
23
+ )
21
24
  class SQLiteModule : Module() {
22
- private val cachedDatabases: MutableList<NativeDatabase> = mutableListOf()
23
- private var hasListeners = false
24
-
25
- private val context: Context
26
- get() = appContext.reactContext ?: throw Exceptions.ReactContextLost()
27
-
28
- private val moduleCoroutineScope = CoroutineScope(Dispatchers.IO)
25
+ private val cachedDatabases: MutableList<DatabaseConnection> = mutableListOf()
29
26
 
30
- override fun definition() = ModuleDefinition {
31
- Name("ExpoSQLite")
32
-
33
- Constant("defaultDatabaseDirectory") {
34
- context.filesDir.canonicalPath + File.separator + "SQLite"
35
- }
27
+ @Volatile
28
+ private var hasListeners = false
36
29
 
37
- Constant("bundledExtensions") {
38
- buildMap {
39
- if (BuildConfig.WITH_SQLITE_VEC) {
40
- put(
41
- "sqlite-vec",
42
- mapOf(
43
- "libPath" to "vec",
44
- "entryPoint" to "sqlite3_vec_init"
45
- )
30
+ // The update hook runs inside SQLite, which must not be called back from there, and an emit on
31
+ // the JS thread reaches the listeners at once. One background thread delivers the changes later
32
+ // and in order instead.
33
+ private val changeEvents: ExecutorService = Executors.newSingleThreadExecutor { runnable ->
34
+ Thread(runnable, "expo-sqlite-change-events")
35
+ }
36
+
37
+ @JS
38
+ @Constant
39
+ val defaultDatabaseDirectory: String
40
+ get() = androidContext.filesDir.canonicalPath + File.separator + "SQLite"
41
+
42
+ @JS
43
+ @Constant
44
+ val bundledExtensions: Map<String, Map<String, String>>
45
+ get() = buildMap {
46
+ if (BuildConfig.WITH_SQLITE_VEC) {
47
+ put(
48
+ "sqlite-vec",
49
+ mapOf(
50
+ "libPath" to "vec",
51
+ "entryPoint" to "sqlite3_vec_init"
46
52
  )
47
- }
53
+ )
48
54
  }
49
55
  }
50
56
 
51
- Events("onDatabaseChange")
57
+ @Event(name = "onDatabaseChange")
58
+ private val onDatabaseChange = event<DatabaseChangeEvent>(
59
+ onStartObserving = { hasListeners = true },
60
+ onStopObserving = { hasListeners = false }
61
+ )
52
62
 
53
- OnStartObserving {
54
- hasListeners = true
63
+ override fun onDestroy() {
64
+ for (connection in removeAllCachedDatabases()) {
65
+ try {
66
+ closeDatabase(connection)
67
+ } catch (_: Throwable) {
68
+ }
55
69
  }
70
+ changeEvents.shutdown()
71
+ }
56
72
 
57
- OnStopObserving {
58
- hasListeners = false
59
- }
73
+ // region JavaScript members
60
74
 
61
- OnDestroy {
62
- try {
63
- removeAllCachedDatabases().forEach {
64
- closeDatabase(it)
65
- }
66
- } catch (_: Throwable) {}
67
- }
75
+ @JS
76
+ suspend fun deleteDatabaseAsync(databasePath: String): Unit = io { deleteDatabase(databasePath) }
68
77
 
69
- AsyncFunction("deleteDatabaseAsync") { databasePath: String ->
70
- deleteDatabase(databasePath)
71
- }.runOnQueue(moduleCoroutineScope)
72
- Function("deleteDatabaseSync") { databasePath: String ->
73
- deleteDatabase(databasePath)
74
- }
78
+ @JS
79
+ fun deleteDatabaseSync(databasePath: String): Unit = deleteDatabase(databasePath)
75
80
 
76
- AsyncFunction("importAssetDatabaseAsync") { databasePath: String, assetDatabasePath: String, forceOverwrite: Boolean ->
81
+ @JS
82
+ suspend fun importAssetDatabaseAsync(
83
+ databasePath: String,
84
+ assetDatabasePath: String,
85
+ forceOverwrite: Boolean
86
+ ) {
87
+ io {
77
88
  val dbFile = File(ensureDatabasePathExists(databasePath))
78
89
  if (dbFile.exists() && !forceOverwrite) {
79
- return@AsyncFunction
90
+ return@io
80
91
  }
81
92
  val assetFile = assetDatabasePath.toUri().toFile()
82
93
  if (!assetFile.isFile) {
83
94
  throw OpenDatabaseException(assetDatabasePath)
84
95
  }
85
96
  assetFile.copyTo(dbFile, forceOverwrite)
86
- }.runOnQueue(moduleCoroutineScope)
87
-
88
- AsyncFunction("ensureDatabasePathExistsAsync") { databasePath: String ->
89
- ensureDatabasePathExists(databasePath)
90
- }.runOnQueue(moduleCoroutineScope)
91
- Function("ensureDatabasePathExistsSync") { databasePath: String ->
92
- ensureDatabasePathExists(databasePath)
93
- }
94
-
95
- AsyncFunction("backupDatabaseAsync") { destDatabase: NativeDatabase, destDatabaseName: String, sourceDatabase: NativeDatabase, sourceDatabaseName: String ->
96
- backupDatabase(destDatabase, destDatabaseName, sourceDatabase, sourceDatabaseName)
97
- }.runOnQueue(moduleCoroutineScope)
98
- Function("backupDatabaseSync") { destDatabase: NativeDatabase, destDatabaseName: String, sourceDatabase: NativeDatabase, sourceDatabaseName: String ->
99
- backupDatabase(destDatabase, destDatabaseName, sourceDatabase, sourceDatabaseName)
100
- }
101
-
102
- // region NativeDatabase
103
-
104
- Class(NativeDatabase::class) {
105
- Constructor { databasePath: String, options: OpenDatabaseOptions, serializedData: ByteArray? ->
106
- val database: NativeDatabase
107
- if (serializedData != null) {
108
- database = deserializeDatabase(serializedData, options)
109
- } else {
110
- // Try to find opened database for fast refresh
111
- findCachedDatabase { it.databasePath == databasePath && it.openOptions == options && !options.useNewConnection }?.let {
112
- it.addRef()
113
- return@Constructor it
114
- }
115
-
116
- val dbPath = ensureDatabasePathExists(databasePath)
117
- database = NativeDatabase(databasePath, options)
118
- if (database.ref.sqlite3_open(dbPath) != NativeDatabaseBinding.SQLITE_OK) {
119
- throw OpenDatabaseException(databasePath)
120
- }
121
- }
122
-
123
- addCachedDatabase(database)
124
- return@Constructor database
125
- }
126
-
127
- AsyncFunction("initAsync") { database: NativeDatabase ->
128
- initDb(database)
129
- }.runOnQueue(moduleCoroutineScope)
130
- Function("initSync") { database: NativeDatabase ->
131
- initDb(database)
132
- }
133
-
134
- AsyncFunction("isInTransactionAsync") { database: NativeDatabase ->
135
- maybeThrowForClosedDatabase(database)
136
- return@AsyncFunction database.ref.sqlite3_get_autocommit() == 0
137
- }.runOnQueue(moduleCoroutineScope)
138
- Function("isInTransactionSync") { database: NativeDatabase ->
139
- maybeThrowForClosedDatabase(database)
140
- return@Function database.ref.sqlite3_get_autocommit() == 0
141
- }
142
-
143
- AsyncFunction("closeAsync") { database: NativeDatabase ->
144
- closeDatabaseIfNeeded(database)
145
- }.runOnQueue(moduleCoroutineScope)
146
- // Interrupt must reach SQLite immediately, without waiting for the running query's queue.
147
- Function("interruptSync") { database: NativeDatabase ->
148
- // Do not block the JS thread or touch a connection being closed on another thread.
149
- if (!database.closeLock.tryLock()) {
150
- throw DatabaseClosingException()
151
- }
152
- try {
153
- maybeThrowForClosedDatabase(database)
154
- database.ref.sqlite3_interrupt()
155
- } finally {
156
- database.closeLock.unlock()
157
- }
158
- }
159
- Function("closeSync") { database: NativeDatabase ->
160
- closeDatabaseIfNeeded(database)
161
- }
162
-
163
- AsyncFunction("execAsync") { database: NativeDatabase, source: String ->
164
- exec(database, source)
165
- }.runOnQueue(moduleCoroutineScope)
166
- Function("execSync") { database: NativeDatabase, source: String ->
167
- exec(database, source)
168
- }
169
-
170
- AsyncFunction("serializeAsync") { database: NativeDatabase, databaseName: String ->
171
- return@AsyncFunction serialize(database, databaseName)
172
- }.runOnQueue(moduleCoroutineScope)
173
- Function("serializeSync") { database: NativeDatabase, databaseName: String ->
174
- return@Function serialize(database, databaseName)
175
- }
176
-
177
- AsyncFunction("prepareAsync") { database: NativeDatabase, statement: NativeStatement, source: String ->
178
- prepareStatement(database, statement, source)
179
- }.runOnQueue(moduleCoroutineScope)
180
- Function("prepareSync") { database: NativeDatabase, statement: NativeStatement, source: String ->
181
- prepareStatement(database, statement, source)
182
- }
183
-
184
- AsyncFunction("createSessionAsync") { database: NativeDatabase, session: NativeSession, dbName: String ->
185
- sessionCreate(database, session, dbName)
186
- }.runOnQueue(moduleCoroutineScope)
187
- Function("createSessionSync") { database: NativeDatabase, session: NativeSession, dbName: String ->
188
- sessionCreate(database, session, dbName)
189
- }
190
-
191
- AsyncFunction("loadExtensionAsync") { database: NativeDatabase, libPath: String, entryPoint: String? ->
192
- loadExtension(database, libPath, entryPoint)
193
- }.runOnQueue(moduleCoroutineScope)
194
- Function("loadExtensionSync") { database: NativeDatabase, libPath: String, entryPoint: String? ->
195
- loadExtension(database, libPath, entryPoint)
196
- }
197
97
  }
98
+ }
198
99
 
199
- // endregion NativeDatabase
200
-
201
- // region NativeStatement
202
-
203
- Class(NativeStatement::class) {
204
- Constructor {
205
- return@Constructor NativeStatement()
206
- }
207
-
208
- AsyncFunction("runAsync") { statement: NativeStatement, database: NativeDatabase, bindParams: Map<String, Any?>, bindBlobParams: Map<String, ArrayBuffer>, shouldPassAsArray: Boolean ->
209
- return@AsyncFunction run(statement, database, bindParams, bindBlobParams, shouldPassAsArray)
210
- }.runOnQueue(moduleCoroutineScope)
211
- Function("runSync") { statement: NativeStatement, database: NativeDatabase, bindParams: Map<String, Any?>, bindBlobParams: Map<String, ArrayBuffer>, shouldPassAsArray: Boolean ->
212
- return@Function run(statement, database, bindParams, bindBlobParams, shouldPassAsArray)
213
- }
214
-
215
- AsyncFunction("stepAsync") { statement: NativeStatement, database: NativeDatabase ->
216
- return@AsyncFunction step(statement, database)
217
- }.runOnQueue(moduleCoroutineScope)
218
- Function("stepSync") { statement: NativeStatement, database: NativeDatabase ->
219
- return@Function step(statement, database)
220
- }
221
-
222
- AsyncFunction("getAllAsync") { statement: NativeStatement, database: NativeDatabase ->
223
- return@AsyncFunction getAll(statement, database)
224
- }.runOnQueue(moduleCoroutineScope)
225
- Function("getAllSync") { statement: NativeStatement, database: NativeDatabase ->
226
- return@Function getAll(statement, database)
227
- }
100
+ @JS
101
+ suspend fun ensureDatabasePathExistsAsync(databasePath: String): Unit =
102
+ io { ensureDatabasePathExists(databasePath) }
103
+
104
+ @JS
105
+ fun ensureDatabasePathExistsSync(databasePath: String) {
106
+ ensureDatabasePathExists(databasePath)
107
+ }
108
+
109
+ @JS
110
+ private suspend fun backupDatabaseAsync(
111
+ destDatabase: NativeDatabase,
112
+ destDatabaseName: String,
113
+ sourceDatabase: NativeDatabase,
114
+ sourceDatabaseName: String
115
+ ): Unit = io {
116
+ backupDatabase(
117
+ destDatabase.connection,
118
+ destDatabaseName,
119
+ sourceDatabase.connection,
120
+ sourceDatabaseName
121
+ )
122
+ }
123
+
124
+ @JS
125
+ private fun backupDatabaseSync(
126
+ destDatabase: NativeDatabase,
127
+ destDatabaseName: String,
128
+ sourceDatabase: NativeDatabase,
129
+ sourceDatabaseName: String
130
+ ): Unit =
131
+ backupDatabase(
132
+ destDatabase.connection,
133
+ destDatabaseName,
134
+ sourceDatabase.connection,
135
+ sourceDatabaseName
136
+ )
228
137
 
229
- AsyncFunction("resetAsync") { statement: NativeStatement, database: NativeDatabase ->
230
- return@AsyncFunction reset(statement, database)
231
- }.runOnQueue(moduleCoroutineScope)
232
- Function("resetSync") { statement: NativeStatement, database: NativeDatabase ->
233
- return@Function reset(statement, database)
234
- }
138
+ // endregion
235
139
 
236
- AsyncFunction("getColumnNamesAsync") { statement: NativeStatement ->
237
- synchronized(statement) {
238
- maybeThrowForFinalizedStatement(statement)
239
- return@AsyncFunction statement.ref.getColumnNames()
240
- }
241
- }.runOnQueue(moduleCoroutineScope)
242
- Function("getColumnNamesSync") { statement: NativeStatement ->
243
- synchronized(statement) {
244
- maybeThrowForFinalizedStatement(statement)
245
- return@Function statement.ref.getColumnNames()
246
- }
140
+ /**
141
+ * The connection a new `NativeDatabase` uses: a cached one that is still open, so a fast refresh
142
+ * keeps an in-memory database, or a new one.
143
+ */
144
+ internal fun openConnection(
145
+ databasePath: String,
146
+ options: OpenDatabaseOptions,
147
+ serializedData: ByteArray?
148
+ ): DatabaseConnection {
149
+ val connection: DatabaseConnection
150
+ if (serializedData != null) {
151
+ connection = deserializeDatabase(serializedData, options)
152
+ } else {
153
+ // Try to find opened database for fast refresh
154
+ findCachedDatabase { it.databasePath == databasePath && it.openOptions == options && !options.useNewConnection }?.let {
155
+ it.addRef()
156
+ return it
247
157
  }
248
158
 
249
- AsyncFunction("finalizeAsync") { statement: NativeStatement, database: NativeDatabase ->
250
- return@AsyncFunction finalize(statement, database)
251
- }.runOnQueue(moduleCoroutineScope)
252
- Function("finalizeSync") { statement: NativeStatement, database: NativeDatabase ->
253
- return@Function finalize(statement, database)
159
+ val dbPath = ensureDatabasePathExists(databasePath)
160
+ connection = DatabaseConnection(databasePath, options)
161
+ if (connection.ref.sqlite3_open(dbPath) != NativeDatabaseBinding.SQLITE_OK) {
162
+ throw OpenDatabaseException(databasePath)
254
163
  }
255
164
  }
256
165
 
257
- // endregion NativeStatement
258
-
259
- // region NativeSession
260
-
261
- Class(NativeSession::class) {
262
- Constructor {
263
- return@Constructor NativeSession()
264
- }
265
-
266
- AsyncFunction("attachAsync") { session: NativeSession, database: NativeDatabase, table: String? ->
267
- sessionAttach(database, session, table)
268
- }.runOnQueue(moduleCoroutineScope)
269
- Function("attachSync") { session: NativeSession, database: NativeDatabase, table: String? ->
270
- sessionAttach(database, session, table)
271
- }
272
-
273
- AsyncFunction("enableAsync") { session: NativeSession, database: NativeDatabase, enabled: Boolean ->
274
- sessionEnable(database, session, enabled)
275
- }.runOnQueue(moduleCoroutineScope)
276
- Function("enableSync") { session: NativeSession, database: NativeDatabase, enabled: Boolean ->
277
- sessionEnable(database, session, enabled)
278
- }
279
-
280
- AsyncFunction("closeAsync") { session: NativeSession, database: NativeDatabase ->
281
- sessionClose(database, session)
282
- }.runOnQueue(moduleCoroutineScope)
283
- Function("closeSync") { session: NativeSession, database: NativeDatabase ->
284
- sessionClose(database, session)
285
- }
286
-
287
- AsyncFunction("createChangesetAsync") { session: NativeSession, database: NativeDatabase ->
288
- return@AsyncFunction sessionCreateChangeset(database, session)
289
- }.runOnQueue(moduleCoroutineScope)
290
- Function("createChangesetSync") { session: NativeSession, database: NativeDatabase ->
291
- return@Function sessionCreateChangeset(database, session)
292
- }
293
-
294
- AsyncFunction("createInvertedChangesetAsync") { session: NativeSession, database: NativeDatabase ->
295
- return@AsyncFunction sessionCreateInvertedChangeset(database, session)
296
- }.runOnQueue(moduleCoroutineScope)
297
- Function("createInvertedChangesetSync") { session: NativeSession, database: NativeDatabase ->
298
- return@Function sessionCreateInvertedChangeset(database, session)
299
- }
300
-
301
- AsyncFunction("applyChangesetAsync") { session: NativeSession, database: NativeDatabase, changeset: ArrayBuffer ->
302
- sessionApplyChangeset(database, session, changeset)
303
- }.runOnQueue(moduleCoroutineScope)
304
- Function("applyChangesetSync") { session: NativeSession, database: NativeDatabase, changeset: ArrayBuffer ->
305
- sessionApplyChangeset(database, session, changeset)
306
- }
166
+ addCachedDatabase(connection)
167
+ return connection
168
+ }
307
169
 
308
- AsyncFunction("invertChangesetAsync") { session: NativeSession, database: NativeDatabase, changeset: ArrayBuffer ->
309
- return@AsyncFunction sessionInvertChangeset(database, session, changeset)
310
- }.runOnQueue(moduleCoroutineScope)
311
- Function("invertChangesetSync") { session: NativeSession, database: NativeDatabase, changeset: ArrayBuffer ->
312
- return@Function sessionInvertChangeset(database, session, changeset)
313
- }
170
+ @Throws(AccessClosedResourceException::class)
171
+ internal fun initDb(connection: DatabaseConnection) {
172
+ connection.maybeThrowForClosed()
173
+ if (connection.openOptions.enableChangeListener) {
174
+ addUpdateHook(connection)
314
175
  }
315
-
316
- // endregion NativeSession
317
176
  }
318
177
 
319
178
  @Throws(OpenDatabaseException::class)
@@ -334,220 +193,48 @@ class SQLiteModule : Module() {
334
193
  }
335
194
  }
336
195
 
337
- private fun deserializeDatabase(serializedData: ByteArray, options: OpenDatabaseOptions): NativeDatabase {
338
- val database = NativeDatabase(MEMORY_DB_NAME, options)
339
- if (database.ref.sqlite3_open(MEMORY_DB_NAME) != NativeDatabaseBinding.SQLITE_OK) {
196
+ private fun deserializeDatabase(serializedData: ByteArray, options: OpenDatabaseOptions): DatabaseConnection {
197
+ val connection = DatabaseConnection(MEMORY_DB_NAME, options)
198
+ if (connection.ref.sqlite3_open(MEMORY_DB_NAME) != NativeDatabaseBinding.SQLITE_OK) {
340
199
  throw OpenDatabaseException(MEMORY_DB_NAME)
341
200
  }
342
- if (database.ref.sqlite3_deserialize("main", serializedData) != NativeDatabaseBinding.SQLITE_OK) {
343
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
344
- }
345
- return database
346
- }
347
-
348
- @Throws(AccessClosedResourceException::class)
349
- private fun initDb(database: NativeDatabase) {
350
- maybeThrowForClosedDatabase(database)
351
- if (database.openOptions.enableChangeListener) {
352
- addUpdateHook(database)
201
+ if (connection.ref.sqlite3_deserialize("main", serializedData) != NativeDatabaseBinding.SQLITE_OK) {
202
+ throw SQLiteErrorException(connection.ref.convertSqlLiteErrorToString())
353
203
  }
204
+ return connection
354
205
  }
355
206
 
356
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
357
- private fun exec(database: NativeDatabase, source: String) {
358
- maybeThrowForClosedDatabase(database)
359
- database.ref.sqlite3_exec(source)
360
- }
361
-
362
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
363
- private fun serialize(database: NativeDatabase, databaseName: String): ByteArray {
364
- maybeThrowForClosedDatabase(database)
365
- return database.ref.sqlite3_serialize(databaseName)
366
- }
367
-
368
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
369
- private fun prepareStatement(database: NativeDatabase, statement: NativeStatement, source: String) {
370
- synchronized(database.statementLifecycleLock) {
371
- synchronized(statement) {
372
- maybeThrowForFinalizedStatement(statement)
373
- maybeThrowForClosedDatabase(database)
374
- if (database.ref.sqlite3_prepare_v2(source, statement.ref) != NativeDatabaseBinding.SQLITE_OK) {
375
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
376
- }
377
- statement.isPrepared = true
378
- database.statements.add(statement)
379
- }
380
- }
381
- }
382
-
383
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
384
- private fun run(statement: NativeStatement, database: NativeDatabase, bindParams: Map<String, Any?>, bindBlobParams: Map<String, ArrayBuffer>, shouldPassAsArray: Boolean): Map<String, Any> {
385
- // The statement with parameter bindings is stateful,
386
- // we have to guard with a critical section for thread safety.
387
- synchronized(statement) {
388
- maybeThrowForFinalizedStatement(statement)
389
- maybeThrowForClosedDatabase(database)
390
-
391
- statement.ref.sqlite3_reset()
392
- statement.ref.sqlite3_clear_bindings()
393
- for ((key, param) in bindParams) {
394
- val index = getBindParamIndex(statement, key, shouldPassAsArray)
395
- if (index > 0) {
396
- // expo-modules-core AnyTypeConverter casts JavaScript Number to Kotlin Double,
397
- // here to cast as Long if the value is an integer.
398
- val normalizedParam =
399
- if (param is Double && param.toDouble() % 1.0 == 0.0) {
400
- param.toLong()
401
- } else {
402
- param
403
- }
404
- statement.ref.bindStatementParam(index, normalizedParam)
405
- }
406
- }
407
- for ((key, param) in bindBlobParams) {
408
- val index = getBindParamIndex(statement, key, shouldPassAsArray)
409
- if (index > 0) {
410
- statement.ref.bindStatementParam(index, param.toDirectBuffer())
411
- }
412
- }
413
-
414
- val ret = statement.ref.sqlite3_step()
415
- if (ret != NativeDatabaseBinding.SQLITE_ROW && ret != NativeDatabaseBinding.SQLITE_DONE) {
416
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
417
- }
418
- val firstRowValues: SQLiteColumnValues =
419
- if (ret == NativeDatabaseBinding.SQLITE_ROW) {
420
- statement.getTransformedColumnValues()
421
- } else {
422
- arrayListOf()
423
- }
424
- return mapOf(
425
- "lastInsertRowId" to database.ref.sqlite3_last_insert_rowid().toInt(),
426
- "changes" to database.ref.sqlite3_changes(),
427
- "firstRowValues" to firstRowValues
428
- )
429
- }
430
- }
431
-
432
- @Throws(AccessClosedResourceException::class, InvalidConvertibleException::class, SQLiteErrorException::class)
433
- private fun step(statement: NativeStatement, database: NativeDatabase): SQLiteColumnValues? {
434
- // Guard the stateful statement, see `run` above.
435
- synchronized(statement) {
436
- maybeThrowForFinalizedStatement(statement)
437
- maybeThrowForClosedDatabase(database)
438
-
439
- val ret = statement.ref.sqlite3_step()
440
- if (ret == NativeDatabaseBinding.SQLITE_ROW) {
441
- return statement.getTransformedColumnValues()
442
- }
443
- if (ret != NativeDatabaseBinding.SQLITE_DONE) {
444
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
445
- }
446
- return null
447
- }
448
- }
449
-
450
- @Throws(AccessClosedResourceException::class, InvalidConvertibleException::class, SQLiteErrorException::class)
451
- private fun getAll(statement: NativeStatement, database: NativeDatabase): List<SQLiteColumnValues> {
452
- // Guard the stateful statement, see `run` above.
453
- synchronized(statement) {
454
- maybeThrowForFinalizedStatement(statement)
455
- maybeThrowForClosedDatabase(database)
456
-
457
- val columnValuesList = mutableListOf<SQLiteColumnValues>()
458
- while (true) {
459
- val ret = statement.ref.sqlite3_step()
460
- if (ret == NativeDatabaseBinding.SQLITE_ROW) {
461
- columnValuesList.add(statement.getTransformedColumnValues())
462
- continue
463
- } else if (ret == NativeDatabaseBinding.SQLITE_DONE) {
464
- break
465
- }
466
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
467
- }
468
- return columnValuesList
469
- }
470
- }
471
-
472
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
473
- private fun reset(statement: NativeStatement, database: NativeDatabase) {
474
- // Guard the stateful statement, see `run` above.
475
- synchronized(statement) {
476
- maybeThrowForFinalizedStatement(statement)
477
- maybeThrowForClosedDatabase(database)
478
-
479
- if (statement.ref.sqlite3_reset() != NativeDatabaseBinding.SQLITE_OK) {
480
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
481
- }
482
- }
483
- }
484
-
485
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
486
- private fun finalize(statement: NativeStatement, database: NativeDatabase) {
487
- synchronized(database.statementLifecycleLock) {
488
- // Guard the stateful statement, see `run` above.
489
- synchronized(statement) {
490
- maybeThrowForFinalizedStatement(statement)
491
- maybeThrowForClosedDatabase(database)
492
-
493
- val ret = statement.ref.sqlite3_finalize()
494
- // SQLite destroys the statement even when returning an earlier execution error.
495
- statement.isFinalized = true
496
- database.statements.removeAll { it === statement }
497
- if (statement.releasedByJavaScript) {
498
- statement.ref.close()
499
- }
500
- if (ret != NativeDatabaseBinding.SQLITE_OK) {
501
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
502
- }
503
- }
504
- }
505
- }
506
-
507
- private fun addUpdateHook(database: NativeDatabase) {
508
- database.ref.enableUpdateHook { databaseName, tableName, operationType, rowID ->
207
+ private fun addUpdateHook(connection: DatabaseConnection) {
208
+ connection.ref.enableUpdateHook { databaseName, tableName, operationType, rowID ->
509
209
  if (!hasListeners) {
510
210
  return@enableUpdateHook
511
211
  }
512
- val databaseFilePath = database.ref.sqlite3_db_filename(databaseName)
513
- sendEvent(
514
- "onDatabaseChange",
515
- bundleOf(
516
- "databaseName" to databaseName,
517
- "databaseFilePath" to databaseFilePath,
518
- "tableName" to tableName,
519
- "rowId" to rowID,
520
- "typeId" to SQLAction.fromCode(operationType).value
521
- )
212
+ val event = DatabaseChangeEvent(
213
+ databaseName = databaseName,
214
+ databaseFilePath = connection.ref.sqlite3_db_filename(databaseName),
215
+ tableName = tableName,
216
+ rowId = rowID,
217
+ typeId = SQLAction.fromCode(operationType)
522
218
  )
219
+ changeEvents.execute { onDatabaseChange(event) }
523
220
  }
524
221
  }
525
222
 
526
223
  @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
527
- private fun loadExtension(database: NativeDatabase, libPath: String, entryPoint: String?) {
528
- maybeThrowForClosedDatabase(database)
529
- database.ref.sqlite3_enable_load_extension(1)
530
- val ret = database.ref.sqlite3_load_extension(libPath, entryPoint ?: "")
531
- if (ret != NativeDatabaseBinding.SQLITE_OK) {
532
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
533
- }
534
- }
535
-
536
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
537
- private fun closeDatabase(database: NativeDatabase) {
538
- database.closeLock.lock()
224
+ private fun closeDatabase(connection: DatabaseConnection) {
225
+ connection.closeLock.lock()
539
226
  try {
540
- synchronized(database.statementLifecycleLock) {
541
- maybeThrowForClosedDatabase(database)
542
- maybeFinalizeAllStatements(database)
543
- val ret = database.ref.sqlite3_close()
227
+ synchronized(connection.statementLifecycleLock) {
228
+ connection.maybeThrowForClosed()
229
+ maybeFinalizeAllStatements(connection)
230
+ val ret = connection.ref.sqlite3_close()
544
231
  if (ret != NativeDatabaseBinding.SQLITE_OK) {
545
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
232
+ throw SQLiteErrorException(connection.ref.convertSqlLiteErrorToString())
546
233
  }
547
- database.isClosed = true
234
+ connection.markClosed()
548
235
  }
549
236
  } finally {
550
- database.closeLock.unlock()
237
+ connection.closeLock.unlock()
551
238
  }
552
239
  }
553
240
 
@@ -563,45 +250,28 @@ class SQLiteModule : Module() {
563
250
  }
564
251
 
565
252
  @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
566
- private fun backupDatabase(destDatabase: NativeDatabase, destDatabaseName: String, sourceDatabase: NativeDatabase, sourceDatabaseName: String) {
567
- maybeThrowForClosedDatabase(destDatabase)
568
- maybeThrowForClosedDatabase(sourceDatabase)
253
+ private fun backupDatabase(
254
+ destDatabase: DatabaseConnection,
255
+ destDatabaseName: String,
256
+ sourceDatabase: DatabaseConnection,
257
+ sourceDatabaseName: String
258
+ ) {
259
+ destDatabase.maybeThrowForClosed()
260
+ sourceDatabase.maybeThrowForClosed()
569
261
  NativeDatabaseBinding.sqlite3_backup(destDatabase.ref, destDatabaseName, sourceDatabase.ref, sourceDatabaseName)
570
262
  }
571
263
 
572
- @Throws(AccessClosedResourceException::class)
573
- private fun maybeThrowForClosedDatabase(database: NativeDatabase) {
574
- if (database.isClosed) {
575
- throw AccessClosedResourceException()
576
- }
577
- }
578
-
579
- @Throws(AccessClosedResourceException::class)
580
- private fun maybeThrowForFinalizedStatement(statement: NativeStatement) {
581
- if (statement.isFinalized) {
582
- throw AccessClosedResourceException()
583
- }
584
- }
585
-
586
- @Throws(InvalidBindParameterException::class)
587
- private fun getBindParamIndex(statement: NativeStatement, key: String, shouldPassAsArray: Boolean): Int =
588
- if (shouldPassAsArray) {
589
- (key.toIntOrNull() ?: throw InvalidBindParameterException()) + 1
590
- } else {
591
- statement.ref.sqlite3_bind_parameter_index(key)
592
- }
593
-
594
264
  // region cachedDatabases managements
595
265
 
596
266
  @Synchronized
597
- private fun addCachedDatabase(database: NativeDatabase) {
598
- cachedDatabases.add(database)
267
+ private fun addCachedDatabase(connection: DatabaseConnection) {
268
+ cachedDatabases.add(connection)
599
269
  }
600
270
 
601
271
  @Synchronized
602
- private fun closeDatabaseIfNeeded(database: NativeDatabase) {
603
- maybeThrowForClosedDatabase(database)
604
- val index = cachedDatabases.indexOf(database)
272
+ internal fun closeDatabaseIfNeeded(connection: DatabaseConnection) {
273
+ connection.maybeThrowForClosed()
274
+ val index = cachedDatabases.indexOf(connection)
605
275
  if (index >= 0) {
606
276
  val db = cachedDatabases[index]
607
277
  if (db.release() == 0) {
@@ -618,13 +288,13 @@ class SQLiteModule : Module() {
618
288
  }
619
289
 
620
290
  @Synchronized
621
- private fun findCachedDatabase(predicate: (NativeDatabase) -> Boolean): NativeDatabase? {
291
+ private fun findCachedDatabase(predicate: (DatabaseConnection) -> Boolean): DatabaseConnection? {
622
292
  return cachedDatabases.find(predicate)
623
293
  }
624
294
 
625
295
  @Synchronized
626
- private fun removeAllCachedDatabases(): List<NativeDatabase> {
627
- val databases = cachedDatabases
296
+ private fun removeAllCachedDatabases(): List<DatabaseConnection> {
297
+ val databases = cachedDatabases.toList()
628
298
  cachedDatabases.clear()
629
299
  return databases
630
300
  }
@@ -633,16 +303,16 @@ class SQLiteModule : Module() {
633
303
 
634
304
  // region statements managements
635
305
 
636
- private fun maybeFinalizeAllStatements(database: NativeDatabase) {
637
- if (!database.openOptions.finalizeUnusedStatementsBeforeClosing) {
306
+ private fun maybeFinalizeAllStatements(connection: DatabaseConnection) {
307
+ if (!connection.openOptions.finalizeUnusedStatementsBeforeClosing) {
638
308
  return
639
309
  }
640
310
  // Finalize through the wrappers so even a failed close leaves them invalidated.
641
311
  // Do not destroy SQLite-internal statements owned by concurrent exec/backup operations.
642
312
  var firstError: Exception? = null
643
- for (statement in database.statements.toList()) {
313
+ for (statement in connection.statements.toList()) {
644
314
  try {
645
- finalize(statement, database)
315
+ statement.finalizeOn(connection)
646
316
  } catch (error: SQLiteErrorException) {
647
317
  android.util.Log.w("expo-sqlite", "Finalizing a statement during close failed", error)
648
318
  } catch (error: Exception) {
@@ -654,75 +324,4 @@ class SQLiteModule : Module() {
654
324
  }
655
325
 
656
326
  // endregion
657
-
658
- // region Session Extension
659
-
660
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
661
- private fun sessionCreate(database: NativeDatabase, session: NativeSession, dbName: String) {
662
- maybeThrowForClosedDatabase(database)
663
- if (session.ref.sqlite3session_create(database.ref, dbName) != NativeDatabaseBinding.SQLITE_OK) {
664
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
665
- }
666
- }
667
-
668
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
669
- private fun sessionAttach(database: NativeDatabase, session: NativeSession, table: String?) {
670
- maybeThrowForClosedDatabase(database)
671
- if (session.ref.sqlite3session_attach(table) != NativeDatabaseBinding.SQLITE_OK) {
672
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
673
- }
674
- }
675
-
676
- @Throws(AccessClosedResourceException::class)
677
- private fun sessionEnable(database: NativeDatabase, session: NativeSession, enabled: Boolean) {
678
- maybeThrowForClosedDatabase(database)
679
- session.ref.sqlite3session_enable(enabled)
680
- }
681
-
682
- @Throws(AccessClosedResourceException::class)
683
- private fun sessionClose(database: NativeDatabase, session: NativeSession) {
684
- maybeThrowForClosedDatabase(database)
685
- session.ref.sqlite3session_delete()
686
- }
687
-
688
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
689
- private fun sessionCreateChangeset(database: NativeDatabase, session: NativeSession): ArrayBuffer {
690
- maybeThrowForClosedDatabase(database)
691
- val byteBuffer = session.ref.sqlite3session_changeset()
692
- ?: throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
693
-
694
- return ArrayBuffer(byteBuffer)
695
- }
696
-
697
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
698
- private fun sessionCreateInvertedChangeset(database: NativeDatabase, session: NativeSession): ArrayBuffer {
699
- maybeThrowForClosedDatabase(database)
700
- val byteBuffer = session.ref.sqlite3session_changeset_inverted()
701
- ?: throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
702
-
703
- return ArrayBuffer(byteBuffer)
704
- }
705
-
706
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
707
- private fun sessionApplyChangeset(database: NativeDatabase, session: NativeSession, changeset: ArrayBuffer) {
708
- maybeThrowForClosedDatabase(database)
709
- if (session.ref.sqlite3changeset_apply(database.ref, changeset.toDirectBuffer()) != NativeDatabaseBinding.SQLITE_OK) {
710
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
711
- }
712
- }
713
-
714
- @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
715
- private fun sessionInvertChangeset(database: NativeDatabase, session: NativeSession, changeset: ArrayBuffer): ArrayBuffer {
716
- maybeThrowForClosedDatabase(database)
717
- val byteBuffer = session.ref.sqlite3changeset_invert(changeset.toDirectBuffer())
718
- ?: throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
719
-
720
- return ArrayBuffer(byteBuffer)
721
- }
722
-
723
- // endregion
724
-
725
- companion object {
726
- private val TAG = SQLiteModule::class.java.simpleName
727
- }
728
327
  }