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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 58.0.12
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [iOS] Remove the module class declarations from `expo-module.config.json`. Autolinking now detects the `@ExpoModule`-annotated classes. ([#49784](https://github.com/expo/expo/pull/49784) by [@tsapeta](https://github.com/tsapeta))
|
|
8
|
+
- [Web] Fix statement finalization and failed-close recovery, same as #50121 on Android and iOS. ([#51293](https://github.com/expo/expo/pull/51293) by [@Kudo](https://github.com/Kudo))
|
|
9
|
+
- [Web] Add `configureWeb()` with a `wasmURL` option to load a custom SQLite WebAssembly build, for example one built with FTS enabled. ([#51294](https://github.com/expo/expo/pull/51294) by [@Kudo](https://github.com/Kudo))
|
|
10
|
+
- [Web] Fix `isInTransactionAsync()` returning `undefined` instead of `false` outside a transaction. ([#51356](https://github.com/expo/expo/pull/51356) by [@Kudo](https://github.com/Kudo))
|
|
11
|
+
- [Web] Fix synchronous calls that lost the SQLite error message or failed on results longer than 255 bytes. ([#51330](https://github.com/expo/expo/pull/51330) by [@Kudo](https://github.com/Kudo))
|
|
12
|
+
- [iOS] Fixed database paths with whitespace, non-ASCII characters, `#` or `?`, both as plain paths and as `file://` URIs such as App Group containers. `importDatabaseFromAssetAsync` with `forceOverwrite` now also removes the `-journal`, `-wal` and `-shm` files so a stale write-ahead log is not replayed over the imported copy, and a database that an earlier release created under a percent-encoded name on iOS 17, tvOS 17 or macOS 14 and later is moved to the decoded path on first open. ([#43939](https://github.com/expo/expo/pull/43939) by [@nickrugge](https://github.com/nickrugge))
|
|
13
|
+
|
|
14
|
+
## 58.0.11
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- [Internal] Bump `react-native-web` to `~0.21.4`. ([#51260](https://github.com/expo/expo/pull/51260) by [@zoontek](https://github.com/zoontek))
|
|
19
|
+
- Preserve statement error codes and messages when queries run concurrently on Android and iOS, and format Android SQLite error codes as numbers. ([#51082](https://github.com/expo/expo/pull/51082) by [@savv](https://github.com/savv))
|
|
20
|
+
- [Android] Migrate to the Expo Modules API 2.0. Reloading the app now closes the databases that are still open, and `lastInsertRowId` is no longer truncated to 32 bits. ([#51208](https://github.com/expo/expo/pull/51208) by [@lukmccall](https://github.com/lukmccall))
|
|
21
|
+
|
|
3
22
|
## 58.0.10
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/android/build.gradle
CHANGED
|
@@ -47,13 +47,17 @@ def reactNativeArchitectures() {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
group = 'host.exp.exponent'
|
|
50
|
-
version = '58.0.
|
|
50
|
+
version = '58.0.12'
|
|
51
|
+
|
|
52
|
+
expoModule {
|
|
53
|
+
v2 true
|
|
54
|
+
}
|
|
51
55
|
|
|
52
56
|
android {
|
|
53
57
|
namespace "expo.modules.sqlite"
|
|
54
58
|
defaultConfig {
|
|
55
59
|
versionCode 18
|
|
56
|
-
versionName "58.0.
|
|
60
|
+
versionName "58.0.12"
|
|
57
61
|
buildConfigField "boolean", "WITH_SQLITE_VEC", project.ext.WITH_SQLITE_VEC.toString()
|
|
58
62
|
|
|
59
63
|
externalNativeBuild {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
2
|
|
|
3
3
|
#include "NativeDatabaseBinding.h"
|
|
4
|
+
#include "SQLiteError.h"
|
|
4
5
|
|
|
5
6
|
#include "Exceptions.h"
|
|
6
7
|
|
|
@@ -101,8 +102,15 @@ int NativeDatabaseBinding::sqlite3_prepare_v2(
|
|
|
101
102
|
const std::string &source,
|
|
102
103
|
jni::alias_ref<NativeStatementBinding::javaobject> statement) {
|
|
103
104
|
NativeStatementBinding *cStatement = cthis(statement);
|
|
104
|
-
|
|
105
|
+
SQLiteDatabaseLock lock(db);
|
|
106
|
+
cStatement->db = db;
|
|
107
|
+
int ret = ::exsqlite3_prepare_v2(db, source.c_str(), source.size(),
|
|
105
108
|
&cStatement->stmt, nullptr);
|
|
109
|
+
if (ret != SQLITE_OK) {
|
|
110
|
+
jni::throwNewJavaException(
|
|
111
|
+
SQLiteErrorException::create(sqliteErrorMessage(db, ret)).get());
|
|
112
|
+
}
|
|
113
|
+
return ret;
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
jni::local_ref<jni::JArrayByte>
|
|
@@ -173,13 +181,8 @@ int NativeDatabaseBinding::sqlite3_backup(
|
|
|
173
181
|
}
|
|
174
182
|
|
|
175
183
|
std::string NativeDatabaseBinding::convertSqlLiteErrorToSTLString() {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
std::string result("Error code ");
|
|
179
|
-
result += code;
|
|
180
|
-
result += ": ";
|
|
181
|
-
result += message;
|
|
182
|
-
return result;
|
|
184
|
+
SQLiteDatabaseLock lock(db);
|
|
185
|
+
return sqliteErrorMessage(db, exsqlite3_errcode(db));
|
|
183
186
|
}
|
|
184
187
|
|
|
185
188
|
jni::local_ref<jni::JString>
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
#include <android/log.h>
|
|
7
7
|
#include "Exceptions.h"
|
|
8
|
+
#include "SQLiteError.h"
|
|
8
9
|
|
|
9
10
|
namespace jni = facebook::jni;
|
|
10
11
|
|
|
@@ -34,6 +35,8 @@ void NativeStatementBinding::registerNatives() {
|
|
|
34
35
|
makeNativeMethod("sqlite3_step", NativeStatementBinding::sqlite3_step),
|
|
35
36
|
makeNativeMethod("bindStatementParam",
|
|
36
37
|
NativeStatementBinding::bindStatementParam),
|
|
38
|
+
makeNativeMethod("getLastErrorMessage",
|
|
39
|
+
NativeStatementBinding::getLastErrorMessage),
|
|
37
40
|
makeNativeMethod("getColumnNames",
|
|
38
41
|
NativeStatementBinding::getColumnNames),
|
|
39
42
|
makeNativeMethod("getColumnValues",
|
|
@@ -59,14 +62,52 @@ std::string NativeStatementBinding::sqlite3_column_name(int index) {
|
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
int NativeStatementBinding::sqlite3_finalize() {
|
|
65
|
+
if (!stmt) {
|
|
66
|
+
lastErrorMessage.clear();
|
|
67
|
+
return SQLITE_OK;
|
|
68
|
+
}
|
|
69
|
+
if (!db) {
|
|
70
|
+
lastErrorMessage = sqliteErrorMessage(SQLITE_MISUSE);
|
|
71
|
+
return SQLITE_MISUSE;
|
|
72
|
+
}
|
|
73
|
+
SQLiteDatabaseLock lock(db);
|
|
62
74
|
int ret = ::exsqlite3_finalize(stmt);
|
|
63
75
|
stmt = nullptr;
|
|
76
|
+
lastErrorMessage = ret == SQLITE_OK ? "" : sqliteErrorMessage(db, ret);
|
|
64
77
|
return ret;
|
|
65
78
|
}
|
|
66
79
|
|
|
67
|
-
int NativeStatementBinding::sqlite3_reset() {
|
|
80
|
+
int NativeStatementBinding::sqlite3_reset() {
|
|
81
|
+
if (!stmt) {
|
|
82
|
+
lastErrorMessage.clear();
|
|
83
|
+
return SQLITE_OK;
|
|
84
|
+
}
|
|
85
|
+
if (!db) {
|
|
86
|
+
lastErrorMessage = sqliteErrorMessage(SQLITE_MISUSE);
|
|
87
|
+
return SQLITE_MISUSE;
|
|
88
|
+
}
|
|
89
|
+
SQLiteDatabaseLock lock(db);
|
|
90
|
+
int ret = ::exsqlite3_reset(stmt);
|
|
91
|
+
lastErrorMessage = ret == SQLITE_OK ? "" : sqliteErrorMessage(db, ret);
|
|
92
|
+
return ret;
|
|
93
|
+
}
|
|
68
94
|
|
|
69
|
-
int NativeStatementBinding::sqlite3_step() {
|
|
95
|
+
int NativeStatementBinding::sqlite3_step() {
|
|
96
|
+
if (!stmt || !db) {
|
|
97
|
+
lastErrorMessage = sqliteErrorMessage(SQLITE_MISUSE);
|
|
98
|
+
return SQLITE_MISUSE;
|
|
99
|
+
}
|
|
100
|
+
SQLiteDatabaseLock lock(db);
|
|
101
|
+
int ret = ::exsqlite3_step(stmt);
|
|
102
|
+
lastErrorMessage = ret == SQLITE_ROW || ret == SQLITE_DONE
|
|
103
|
+
? ""
|
|
104
|
+
: sqliteErrorMessage(db, ret);
|
|
105
|
+
return ret;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
std::string NativeStatementBinding::getLastErrorMessage() {
|
|
109
|
+
return lastErrorMessage;
|
|
110
|
+
}
|
|
70
111
|
|
|
71
112
|
int NativeStatementBinding::bindStatementParam(
|
|
72
113
|
int index, jni::alias_ref<jni::JObject> param) {
|
|
@@ -30,6 +30,7 @@ public:
|
|
|
30
30
|
int sqlite3_step();
|
|
31
31
|
|
|
32
32
|
// helpers
|
|
33
|
+
std::string getLastErrorMessage();
|
|
33
34
|
int bindStatementParam(int index, jni::alias_ref<jni::JObject> param);
|
|
34
35
|
jni::local_ref<jni::JArrayList<jni::JString>> getColumnNames();
|
|
35
36
|
jni::local_ref<jni::JArrayList<jni::JObject>> getColumnValues();
|
|
@@ -47,7 +48,9 @@ private:
|
|
|
47
48
|
friend HybridBase;
|
|
48
49
|
friend NativeDatabaseBinding;
|
|
49
50
|
|
|
51
|
+
sqlite3 *db = nullptr;
|
|
50
52
|
exsqlite3_stmt *stmt = nullptr;
|
|
53
|
+
std::string lastErrorMessage;
|
|
51
54
|
};
|
|
52
55
|
|
|
53
56
|
} // namespace expo
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Copyright 2026-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <string>
|
|
6
|
+
#include "sqlite3.h"
|
|
7
|
+
|
|
8
|
+
namespace expo {
|
|
9
|
+
|
|
10
|
+
// Keep SQLite's connection error state stable until its message has been copied.
|
|
11
|
+
// SQLite's database mutex is recursive, so the operation can acquire it internally too.
|
|
12
|
+
class SQLiteDatabaseLock {
|
|
13
|
+
public:
|
|
14
|
+
explicit SQLiteDatabaseLock(sqlite3 *db) : mutex(exsqlite3_db_mutex(db)) {
|
|
15
|
+
exsqlite3_mutex_enter(mutex);
|
|
16
|
+
}
|
|
17
|
+
~SQLiteDatabaseLock() { exsqlite3_mutex_leave(mutex); }
|
|
18
|
+
SQLiteDatabaseLock(const SQLiteDatabaseLock &) = delete;
|
|
19
|
+
SQLiteDatabaseLock &operator=(const SQLiteDatabaseLock &) = delete;
|
|
20
|
+
|
|
21
|
+
private:
|
|
22
|
+
exsqlite3_mutex *mutex;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Call while holding SQLiteDatabaseLock; use the operation's returned code, not a later errcode().
|
|
26
|
+
inline std::string sqliteErrorMessage(sqlite3 *db, int code) {
|
|
27
|
+
return "Error code " + std::to_string(code) + ": " + exsqlite3_errmsg(db);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
inline std::string sqliteErrorMessage(int code) {
|
|
31
|
+
return "Error code " + std::to_string(code) + ": " + exsqlite3_errstr(code);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
} // namespace expo
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
package expo.modules.sqlite
|
|
4
|
+
|
|
5
|
+
import java.util.concurrent.atomic.AtomicInteger
|
|
6
|
+
import java.util.concurrent.locks.ReentrantLock
|
|
7
|
+
|
|
8
|
+
internal class DatabaseConnection(
|
|
9
|
+
val databasePath: String,
|
|
10
|
+
val openOptions: OpenDatabaseOptions
|
|
11
|
+
) {
|
|
12
|
+
val ref = NativeDatabaseBinding()
|
|
13
|
+
|
|
14
|
+
@Volatile
|
|
15
|
+
var isClosed = false
|
|
16
|
+
private set
|
|
17
|
+
|
|
18
|
+
val closeLock = ReentrantLock()
|
|
19
|
+
|
|
20
|
+
val statementLifecycleLock = Any()
|
|
21
|
+
val statements = mutableListOf<NativeStatement>()
|
|
22
|
+
|
|
23
|
+
/** How many times JavaScript opened this connection without closing it yet. */
|
|
24
|
+
private val refCount = AtomicInteger(1)
|
|
25
|
+
|
|
26
|
+
/** How many `NativeDatabase` objects that use this connection JavaScript still holds. */
|
|
27
|
+
private var holders = 0
|
|
28
|
+
private var isBindingReleased = false
|
|
29
|
+
|
|
30
|
+
@Throws(AccessClosedResourceException::class)
|
|
31
|
+
fun maybeThrowForClosed() {
|
|
32
|
+
if (isClosed) {
|
|
33
|
+
throw AccessClosedResourceException()
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fun addRef(): Int = refCount.incrementAndGet()
|
|
38
|
+
|
|
39
|
+
fun release(): Int = refCount.decrementAndGet()
|
|
40
|
+
|
|
41
|
+
@Synchronized
|
|
42
|
+
fun addHolder() {
|
|
43
|
+
holders++
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** JavaScript let go of a `NativeDatabase` that uses this connection. */
|
|
47
|
+
@Synchronized
|
|
48
|
+
fun removeHolder() {
|
|
49
|
+
holders--
|
|
50
|
+
releaseBindingIfUnused()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@Synchronized
|
|
54
|
+
fun markClosed() {
|
|
55
|
+
isClosed = true
|
|
56
|
+
releaseBindingIfUnused()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The binding goes once the connection is closed and nothing in JavaScript can reach it. A
|
|
60
|
+
// connection JavaScript dropped while it was open stays usable, so it can still be reopened from
|
|
61
|
+
// the cache, or closed when the module is destroyed.
|
|
62
|
+
private fun releaseBindingIfUnused() {
|
|
63
|
+
if (isClosed && holders == 0 && !isBindingReleased) {
|
|
64
|
+
isBindingReleased = true
|
|
65
|
+
ref.close()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -2,38 +2,151 @@
|
|
|
2
2
|
|
|
3
3
|
package expo.modules.sqlite
|
|
4
4
|
|
|
5
|
-
import expo.modules.
|
|
6
|
-
import
|
|
7
|
-
import
|
|
5
|
+
import io.github.expo.modules.v2.ExpoSharedObject
|
|
6
|
+
import io.github.expo.modules.v2.JS
|
|
7
|
+
import io.github.expo.modules.v2.SharedObject
|
|
8
|
+
import io.github.expo.modules.v2.module
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* A database JavaScript opened. Opening a database that is already open, without asking for a new
|
|
12
|
+
* connection, gives a new `NativeDatabase` over the same [DatabaseConnection], so the data and the
|
|
13
|
+
* open count are shared, as one cached object shared them before.
|
|
14
|
+
*/
|
|
15
|
+
@ExpoSharedObject
|
|
16
|
+
internal class NativeDatabase @JS constructor(
|
|
17
|
+
databasePath: String,
|
|
18
|
+
options: OpenDatabaseOptions,
|
|
19
|
+
serializedData: ByteArray?
|
|
20
|
+
) : SharedObject() {
|
|
21
|
+
private val module: SQLiteModule = context.module<SQLiteModule>()
|
|
22
|
+
?: throw IllegalStateException(
|
|
23
|
+
"Cannot open the database because the ExpoSQLite module is not installed in this app. " +
|
|
24
|
+
"Rebuild the app after installing expo-sqlite."
|
|
25
|
+
)
|
|
12
26
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
val connection: DatabaseConnection = module.openConnection(
|
|
28
|
+
databasePath,
|
|
29
|
+
options,
|
|
30
|
+
serializedData
|
|
31
|
+
)
|
|
32
|
+
.also {
|
|
33
|
+
it.addHolder()
|
|
34
|
+
}
|
|
18
35
|
|
|
19
|
-
|
|
20
|
-
|
|
36
|
+
override fun sharedObjectDidRelease() {
|
|
37
|
+
super.sharedObjectDidRelease()
|
|
38
|
+
connection.removeHolder()
|
|
21
39
|
}
|
|
22
40
|
|
|
23
|
-
|
|
24
|
-
|
|
41
|
+
@JS
|
|
42
|
+
suspend fun initAsync(): Unit = io { module.initDb(connection) }
|
|
43
|
+
|
|
44
|
+
@JS
|
|
45
|
+
fun initSync() {
|
|
46
|
+
module.initDb(connection)
|
|
25
47
|
}
|
|
26
48
|
|
|
27
|
-
|
|
28
|
-
|
|
49
|
+
@JS
|
|
50
|
+
suspend fun isInTransactionAsync(): Boolean = io { isInTransaction() }
|
|
51
|
+
|
|
52
|
+
@JS
|
|
53
|
+
fun isInTransactionSync(): Boolean = isInTransaction()
|
|
54
|
+
|
|
55
|
+
@JS
|
|
56
|
+
suspend fun closeAsync(): Unit = io { module.closeDatabaseIfNeeded(connection) }
|
|
57
|
+
|
|
58
|
+
// Interrupt must reach SQLite immediately, without waiting for the running query's queue.
|
|
59
|
+
@JS
|
|
60
|
+
fun interruptSync() {
|
|
61
|
+
// Do not block the JS thread or touch a connection being closed on another thread.
|
|
62
|
+
if (!connection.closeLock.tryLock()) {
|
|
63
|
+
throw DatabaseClosingException()
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
connection.maybeThrowForClosed()
|
|
67
|
+
connection.ref.sqlite3_interrupt()
|
|
68
|
+
} finally {
|
|
69
|
+
connection.closeLock.unlock()
|
|
70
|
+
}
|
|
29
71
|
}
|
|
30
72
|
|
|
31
|
-
|
|
32
|
-
|
|
73
|
+
@JS
|
|
74
|
+
fun closeSync(): Unit = module.closeDatabaseIfNeeded(connection)
|
|
75
|
+
|
|
76
|
+
@JS
|
|
77
|
+
suspend fun execAsync(source: String): Unit = io { exec(source) }
|
|
78
|
+
|
|
79
|
+
@JS
|
|
80
|
+
fun execSync(source: String): Unit = exec(source)
|
|
81
|
+
|
|
82
|
+
@JS
|
|
83
|
+
suspend fun serializeAsync(databaseName: String): ByteArray = io { serialize(databaseName) }
|
|
84
|
+
|
|
85
|
+
@JS
|
|
86
|
+
fun serializeSync(databaseName: String): ByteArray = serialize(databaseName)
|
|
87
|
+
|
|
88
|
+
@JS
|
|
89
|
+
suspend fun prepareAsync(statement: NativeStatement, source: String): Unit =
|
|
90
|
+
io { prepare(statement, source) }
|
|
91
|
+
|
|
92
|
+
@JS
|
|
93
|
+
fun prepareSync(statement: NativeStatement, source: String): Unit =
|
|
94
|
+
prepare(statement, source)
|
|
95
|
+
|
|
96
|
+
@JS
|
|
97
|
+
suspend fun createSessionAsync(session: NativeSession, dbName: String): Unit =
|
|
98
|
+
io { session.create(connection, dbName) }
|
|
99
|
+
|
|
100
|
+
@JS
|
|
101
|
+
fun createSessionSync(session: NativeSession, dbName: String): Unit =
|
|
102
|
+
session.create(connection, dbName)
|
|
103
|
+
|
|
104
|
+
@JS
|
|
105
|
+
suspend fun loadExtensionAsync(libPath: String, entryPoint: String?): Unit =
|
|
106
|
+
io { loadExtension(libPath, entryPoint) }
|
|
107
|
+
|
|
108
|
+
@JS
|
|
109
|
+
fun loadExtensionSync(libPath: String, entryPoint: String?): Unit =
|
|
110
|
+
loadExtension(libPath, entryPoint)
|
|
111
|
+
|
|
112
|
+
@Throws(AccessClosedResourceException::class)
|
|
113
|
+
private fun isInTransaction(): Boolean {
|
|
114
|
+
connection.maybeThrowForClosed()
|
|
115
|
+
return connection.ref.sqlite3_get_autocommit() == 0
|
|
33
116
|
}
|
|
34
117
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
118
|
+
@Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
|
|
119
|
+
private fun exec(source: String) {
|
|
120
|
+
connection.maybeThrowForClosed()
|
|
121
|
+
connection.ref.sqlite3_exec(source)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
|
|
125
|
+
private fun serialize(databaseName: String): ByteArray {
|
|
126
|
+
connection.maybeThrowForClosed()
|
|
127
|
+
return connection.ref.sqlite3_serialize(databaseName)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
|
|
131
|
+
private fun prepare(statement: NativeStatement, source: String) {
|
|
132
|
+
synchronized(connection.statementLifecycleLock) {
|
|
133
|
+
synchronized(statement) {
|
|
134
|
+
statement.maybeThrowForFinalized()
|
|
135
|
+
connection.maybeThrowForClosed()
|
|
136
|
+
connection.ref.sqlite3_prepare_v2(source, statement.ref)
|
|
137
|
+
statement.isPrepared = true
|
|
138
|
+
connection.statements.add(statement)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
|
|
144
|
+
private fun loadExtension(libPath: String, entryPoint: String?) {
|
|
145
|
+
connection.maybeThrowForClosed()
|
|
146
|
+
connection.ref.sqlite3_enable_load_extension(1)
|
|
147
|
+
val ret = connection.ref.sqlite3_load_extension(libPath, entryPoint ?: "")
|
|
148
|
+
if (ret != NativeDatabaseBinding.SQLITE_OK) {
|
|
149
|
+
throw SQLiteErrorException(connection.ref.convertSqlLiteErrorToString())
|
|
150
|
+
}
|
|
38
151
|
}
|
|
39
152
|
}
|
|
@@ -2,19 +2,139 @@
|
|
|
2
2
|
|
|
3
3
|
package expo.modules.sqlite
|
|
4
4
|
|
|
5
|
-
import expo.modules.
|
|
5
|
+
import io.github.expo.modules.v2.ExpoSharedObject
|
|
6
|
+
import io.github.expo.modules.v2.JS
|
|
7
|
+
import io.github.expo.modules.v2.SharedRef
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
@ExpoSharedObject
|
|
10
|
+
internal class NativeSession @JS constructor() : SharedRef<NativeSessionBinding>(NativeSessionBinding()) {
|
|
8
11
|
override fun sharedObjectDidRelease() {
|
|
9
12
|
super.sharedObjectDidRelease()
|
|
10
13
|
this.ref.close()
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
// region JavaScript members
|
|
17
|
+
|
|
18
|
+
@JS
|
|
19
|
+
suspend fun attachAsync(database: NativeDatabase, table: String?) {
|
|
20
|
+
io { attach(database.connection, table) }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@JS
|
|
24
|
+
fun attachSync(database: NativeDatabase, table: String?) {
|
|
25
|
+
attach(database.connection, table)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@JS
|
|
29
|
+
suspend fun enableAsync(database: NativeDatabase, enabled: Boolean) {
|
|
30
|
+
io { enable(database.connection, enabled) }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@JS
|
|
34
|
+
fun enableSync(database: NativeDatabase, enabled: Boolean) {
|
|
35
|
+
enable(database.connection, enabled)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@JS
|
|
39
|
+
suspend fun closeAsync(database: NativeDatabase) {
|
|
40
|
+
io { close(database.connection) }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@JS
|
|
44
|
+
fun closeSync(database: NativeDatabase) {
|
|
45
|
+
close(database.connection)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@JS
|
|
49
|
+
suspend fun createChangesetAsync(database: NativeDatabase): ByteArray = io { createChangeset(database.connection) }
|
|
50
|
+
|
|
51
|
+
@JS
|
|
52
|
+
fun createChangesetSync(database: NativeDatabase): ByteArray = createChangeset(database.connection)
|
|
53
|
+
|
|
54
|
+
@JS
|
|
55
|
+
suspend fun createInvertedChangesetAsync(database: NativeDatabase): ByteArray =
|
|
56
|
+
io { createInvertedChangeset(database.connection) }
|
|
57
|
+
|
|
58
|
+
@JS
|
|
59
|
+
fun createInvertedChangesetSync(database: NativeDatabase): ByteArray = createInvertedChangeset(database.connection)
|
|
60
|
+
|
|
61
|
+
@JS
|
|
62
|
+
suspend fun applyChangesetAsync(database: NativeDatabase, changeset: ByteArray) {
|
|
63
|
+
io { applyChangeset(database.connection, changeset) }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@JS
|
|
67
|
+
fun applyChangesetSync(database: NativeDatabase, changeset: ByteArray) {
|
|
68
|
+
applyChangeset(database.connection, changeset)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@JS
|
|
72
|
+
suspend fun invertChangesetAsync(database: NativeDatabase, changeset: ByteArray): ByteArray =
|
|
73
|
+
io { invertChangeset(database.connection, changeset) }
|
|
74
|
+
|
|
75
|
+
@JS
|
|
76
|
+
fun invertChangesetSync(database: NativeDatabase, changeset: ByteArray): ByteArray =
|
|
77
|
+
invertChangeset(database.connection, changeset)
|
|
78
|
+
|
|
79
|
+
// endregion
|
|
80
|
+
|
|
81
|
+
@Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
|
|
82
|
+
fun create(connection: DatabaseConnection, dbName: String) {
|
|
83
|
+
connection.maybeThrowForClosed()
|
|
84
|
+
if (ref.sqlite3session_create(connection.ref, dbName) != NativeDatabaseBinding.SQLITE_OK) {
|
|
85
|
+
throw SQLiteErrorException(connection.ref.convertSqlLiteErrorToString())
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
|
|
90
|
+
private fun attach(connection: DatabaseConnection, table: String?) {
|
|
91
|
+
connection.maybeThrowForClosed()
|
|
92
|
+
if (ref.sqlite3session_attach(table) != NativeDatabaseBinding.SQLITE_OK) {
|
|
93
|
+
throw SQLiteErrorException(connection.ref.convertSqlLiteErrorToString())
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@Throws(AccessClosedResourceException::class)
|
|
98
|
+
private fun enable(connection: DatabaseConnection, enabled: Boolean) {
|
|
99
|
+
connection.maybeThrowForClosed()
|
|
100
|
+
ref.sqlite3session_enable(enabled)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@Throws(AccessClosedResourceException::class)
|
|
104
|
+
private fun close(connection: DatabaseConnection) {
|
|
105
|
+
connection.maybeThrowForClosed()
|
|
106
|
+
ref.sqlite3session_delete()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
|
|
110
|
+
private fun createChangeset(connection: DatabaseConnection): ByteArray {
|
|
111
|
+
connection.maybeThrowForClosed()
|
|
112
|
+
val byteBuffer = ref.sqlite3session_changeset()
|
|
113
|
+
?: throw SQLiteErrorException(connection.ref.convertSqlLiteErrorToString())
|
|
114
|
+
return byteBuffer.toByteArray()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
|
|
118
|
+
private fun createInvertedChangeset(connection: DatabaseConnection): ByteArray {
|
|
119
|
+
connection.maybeThrowForClosed()
|
|
120
|
+
val byteBuffer = ref.sqlite3session_changeset_inverted()
|
|
121
|
+
?: throw SQLiteErrorException(connection.ref.convertSqlLiteErrorToString())
|
|
122
|
+
return byteBuffer.toByteArray()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
|
|
126
|
+
private fun applyChangeset(connection: DatabaseConnection, changeset: ByteArray) {
|
|
127
|
+
connection.maybeThrowForClosed()
|
|
128
|
+
if (ref.sqlite3changeset_apply(connection.ref, changeset.toDirectBuffer()) != NativeDatabaseBinding.SQLITE_OK) {
|
|
129
|
+
throw SQLiteErrorException(connection.ref.convertSqlLiteErrorToString())
|
|
130
|
+
}
|
|
15
131
|
}
|
|
16
132
|
|
|
17
|
-
|
|
18
|
-
|
|
133
|
+
@Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
|
|
134
|
+
private fun invertChangeset(connection: DatabaseConnection, changeset: ByteArray): ByteArray {
|
|
135
|
+
connection.maybeThrowForClosed()
|
|
136
|
+
val byteBuffer = ref.sqlite3changeset_invert(changeset.toDirectBuffer())
|
|
137
|
+
?: throw SQLiteErrorException(connection.ref.convertSqlLiteErrorToString())
|
|
138
|
+
return byteBuffer.toByteArray()
|
|
19
139
|
}
|
|
20
140
|
}
|