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.
- package/CHANGELOG.md +19 -0
- package/android/build.gradle +6 -2
- package/android/src/main/cpp/NativeDatabaseBinding.cpp +11 -8
- package/android/src/main/cpp/NativeStatementBinding.cpp +43 -2
- package/android/src/main/cpp/NativeStatementBinding.h +3 -0
- package/android/src/main/cpp/SQLiteError.h +34 -0
- package/android/src/main/java/expo/modules/sqlite/DatabaseConnection.kt +68 -0
- package/android/src/main/java/expo/modules/sqlite/NativeDatabase.kt +135 -22
- package/android/src/main/java/expo/modules/sqlite/NativeSession.kt +126 -6
- package/android/src/main/java/expo/modules/sqlite/NativeStatement.kt +199 -14
- package/android/src/main/java/expo/modules/sqlite/NativeStatementBinding.kt +1 -0
- package/android/src/main/java/expo/modules/sqlite/SQLRecords.kt +19 -13
- package/android/src/main/java/expo/modules/sqlite/SQLiteModule.kt +176 -577
- package/android/src/main/java/expo/modules/sqlite/SQLiteOptions.kt +3 -10
- package/android/src/main/java/expo/modules/sqlite/SQLiteValues.kt +47 -0
- package/build/NativeDatabase.d.ts +2 -2
- package/build/SQLiteDatabase.js +9 -3
- package/build/SQLiteDatabase.js.map +1 -1
- package/build/WebConfiguration.d.ts +31 -0
- package/build/WebConfiguration.js +21 -0
- package/build/WebConfiguration.js.map +1 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/dev-plugin-dist/+not-found.html +2 -4
- package/dev-plugin-dist/_expo/static/js/web/{__common-9b94dd2fbdbb825a662f42cf8d2077ee.js → __common-3e95e247f8ee6d975454b07047f39148.js} +52 -48
- package/dev-plugin-dist/_expo/static/js/web/{_layout-b2894030c1dfbd95b717a85901b6c271.js → _layout-0bc906c56b363d38baf86696db19957e.js} +23 -23
- package/dev-plugin-dist/_expo/static/js/web/{entry-04fdb7d857a65c0ed638ef2d774a4b6e.js → entry-293efbd088ab66af1b3334c3eeafa61f.js} +697 -696
- package/dev-plugin-dist/_expo/static/js/web/{index-4bf2da199f699eb47a42074d5e183c07.js → index-454a4f95007576080f4f47d70441d41a.js} +1740 -1740
- package/dev-plugin-dist/_expo/static/js/web/{worker-d51aa6be89e271e571111274ab8e7f5c.js → worker-172ab4a50823f055f5bd777d90d1a36a.js} +17 -17
- package/dev-plugin-dist/_sitemap.html +2 -4
- package/dev-plugin-dist/index.html +2 -4
- package/expo-module.config.json +0 -4
- package/ios/DatabaseFileUtils.swift +101 -5
- package/ios/NativeDatabase.swift +5 -8
- package/ios/NativeStatement.swift +20 -13
- package/ios/SQLiteModule.swift +8 -2
- package/ios/SQLiteResult.swift +31 -0
- package/ios/Tests/DatabaseFileUtilsTests.swift +119 -0
- package/ios/Tests/test_ErrorReporting.swift +25 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12-sources.jar.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.aar.sha512 +1 -0
- 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
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.module.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.module.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.module.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.module.sha512 +1 -0
- 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
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.pom.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.pom.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.pom.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.12/expo.modules.sqlite-58.0.12.pom.sha512 +1 -0
- package/package.json +4 -4
- package/src/NativeDatabase.ts +2 -2
- package/src/SQLiteDatabase.ts +11 -3
- package/src/WebConfiguration.ts +36 -0
- package/src/index.ts +1 -0
- package/web/SQLiteModule.ts +16 -0
- package/web/WorkerChannel.ts +17 -9
- package/web/wa-sqlite/sqlite-api.js +4 -1
- package/web/web.types.ts +6 -0
- package/web/worker.ts +50 -38
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10-sources.jar.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.aar.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.module.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.module.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.module.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.module.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.pom.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.pom.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.10/expo.modules.sqlite-58.0.10.pom.sha256 +0 -1
- 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
|
|
9
|
-
import expo.modules.
|
|
10
|
-
import expo.modules.
|
|
11
|
-
import expo.modules.
|
|
12
|
-
import expo.modules.
|
|
13
|
-
import
|
|
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
|
-
@
|
|
20
|
+
@ExpoModule(
|
|
21
|
+
name = "ExpoSQLite",
|
|
22
|
+
classes = [NativeDatabase::class, NativeStatement::class, NativeSession::class]
|
|
23
|
+
)
|
|
21
24
|
class SQLiteModule : Module() {
|
|
22
|
-
private val cachedDatabases: MutableList<
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Constant("defaultDatabaseDirectory") {
|
|
34
|
-
context.filesDir.canonicalPath + File.separator + "SQLite"
|
|
35
|
-
}
|
|
27
|
+
@Volatile
|
|
28
|
+
private var hasListeners = false
|
|
36
29
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
57
|
+
@Event(name = "onDatabaseChange")
|
|
58
|
+
private val onDatabaseChange = event<DatabaseChangeEvent>(
|
|
59
|
+
onStartObserving = { hasListeners = true },
|
|
60
|
+
onStopObserving = { hasListeners = false }
|
|
61
|
+
)
|
|
52
62
|
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
58
|
-
hasListeners = false
|
|
59
|
-
}
|
|
73
|
+
// region JavaScript members
|
|
60
74
|
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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@
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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):
|
|
338
|
-
val
|
|
339
|
-
if (
|
|
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 (
|
|
343
|
-
throw SQLiteErrorException(
|
|
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
|
-
|
|
357
|
-
|
|
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
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
|
528
|
-
|
|
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(
|
|
541
|
-
|
|
542
|
-
maybeFinalizeAllStatements(
|
|
543
|
-
val ret =
|
|
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(
|
|
232
|
+
throw SQLiteErrorException(connection.ref.convertSqlLiteErrorToString())
|
|
546
233
|
}
|
|
547
|
-
|
|
234
|
+
connection.markClosed()
|
|
548
235
|
}
|
|
549
236
|
} finally {
|
|
550
|
-
|
|
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(
|
|
567
|
-
|
|
568
|
-
|
|
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(
|
|
598
|
-
cachedDatabases.add(
|
|
267
|
+
private fun addCachedDatabase(connection: DatabaseConnection) {
|
|
268
|
+
cachedDatabases.add(connection)
|
|
599
269
|
}
|
|
600
270
|
|
|
601
271
|
@Synchronized
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
val index = cachedDatabases.indexOf(
|
|
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: (
|
|
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<
|
|
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(
|
|
637
|
-
if (!
|
|
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
|
|
313
|
+
for (statement in connection.statements.toList()) {
|
|
644
314
|
try {
|
|
645
|
-
|
|
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
|
}
|