expo-sqlite 11.8.0 → 12.1.0
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 +34 -0
- package/android/CMakeLists.txt +2 -2
- package/android/build.gradle +52 -5
- package/android/src/main/cpp/NativeDatabaseBinding.cpp +142 -0
- package/android/src/main/cpp/NativeDatabaseBinding.h +75 -0
- package/android/src/main/cpp/NativeStatementBinding.cpp +157 -0
- package/android/src/main/cpp/NativeStatementBinding.h +83 -0
- package/android/src/main/cpp/SQLite3Main.cpp +7 -2
- package/android/src/main/cpp/SQLite3Wrapper.cpp +0 -30
- package/android/src/main/cpp/SQLite3Wrapper.h +0 -4
- package/android/src/main/java/expo/modules/sqlite/NativeDatabase.kt +11 -0
- package/android/src/main/java/expo/modules/sqlite/NativeDatabaseBinding.kt +79 -0
- package/android/src/main/java/expo/modules/sqlite/NativeStatement.kt +11 -0
- package/android/src/main/java/expo/modules/sqlite/NativeStatementBinding.kt +41 -0
- package/android/src/main/java/expo/modules/sqlite/SQLExceptions.kt +17 -6
- package/android/src/main/java/expo/modules/sqlite/SQLRecords.kt +16 -3
- package/android/src/main/java/expo/modules/sqlite/SQLite3Wrapper.kt +0 -30
- package/android/src/main/java/expo/modules/sqlite/SQLiteHelpers.kt +0 -79
- package/android/src/main/java/expo/modules/sqlite/SQLiteModule.kt +2 -55
- package/android/src/main/java/expo/modules/sqlite/SQLiteModuleNext.kt +454 -0
- package/android/src/main/java/expo/modules/sqlite/SQLiteOptions.kt +20 -0
- package/build/SQLite.d.ts +2 -2
- package/build/SQLite.d.ts.map +1 -1
- package/build/SQLite.js.map +1 -1
- package/build/SQLite.types.d.ts +3 -2
- package/build/SQLite.types.d.ts.map +1 -1
- package/build/SQLite.types.js.map +1 -1
- package/build/next/Database.d.ts +254 -0
- package/build/next/Database.d.ts.map +1 -0
- package/build/next/Database.js +320 -0
- package/build/next/Database.js.map +1 -0
- package/build/next/ExpoSQLiteNext.d.ts +12 -0
- package/build/next/ExpoSQLiteNext.d.ts.map +1 -0
- package/build/next/ExpoSQLiteNext.js +26 -0
- package/build/next/ExpoSQLiteNext.js.map +1 -0
- package/build/next/ExpoSQLiteNext.native.d.ts +3 -0
- package/build/next/ExpoSQLiteNext.native.d.ts.map +1 -0
- package/build/next/ExpoSQLiteNext.native.js +3 -0
- package/build/next/ExpoSQLiteNext.native.js.map +1 -0
- package/build/next/NativeDatabase.d.ts +44 -0
- package/build/next/NativeDatabase.d.ts.map +1 -0
- package/build/next/NativeDatabase.js +2 -0
- package/build/next/NativeDatabase.js.map +1 -0
- package/build/next/NativeStatement.d.ts +71 -0
- package/build/next/NativeStatement.d.ts.map +1 -0
- package/build/next/NativeStatement.js +2 -0
- package/build/next/NativeStatement.js.map +1 -0
- package/build/next/Statement.d.ts +138 -0
- package/build/next/Statement.d.ts.map +1 -0
- package/build/next/Statement.js +184 -0
- package/build/next/Statement.js.map +1 -0
- package/build/next/hooks.d.ts +35 -0
- package/build/next/hooks.d.ts.map +1 -0
- package/build/next/hooks.js +60 -0
- package/build/next/hooks.js.map +1 -0
- package/build/next/index.d.ts +4 -0
- package/build/next/index.d.ts.map +1 -0
- package/build/next/index.js +4 -0
- package/build/next/index.js.map +1 -0
- package/expo-module.config.json +2 -2
- package/ios/CRSQLiteLoader.h +3 -1
- package/ios/CRSQLiteLoader.m +11 -6
- package/ios/Exceptions.swift +13 -0
- package/ios/ExpoSQLite.podspec +2 -2
- package/ios/NativeDatabase.swift +26 -0
- package/ios/NativeStatement.swift +13 -0
- package/ios/SQLAction.swift +23 -0
- package/ios/SQLiteModule.swift +0 -53
- package/ios/SQLiteModuleNext.swift +555 -0
- package/ios/SQLiteOptions.swift +26 -0
- package/next.d.ts +1 -0
- package/next.js +1 -0
- package/package.json +4 -2
- package/src/SQLite.ts +2 -1
- package/src/SQLite.types.ts +5 -2
- package/src/next/Database.ts +442 -0
- package/src/next/ExpoSQLiteNext.native.ts +2 -0
- package/src/next/ExpoSQLiteNext.ts +34 -0
- package/src/next/NativeDatabase.ts +58 -0
- package/src/next/NativeStatement.ts +96 -0
- package/src/next/Statement.ts +306 -0
- package/src/next/hooks.tsx +111 -0
- package/src/next/index.ts +3 -0
- package/vendor/README.expo.md +0 -6
- package/vendor/shell.c +0 -28032
- package/vendor/sqlite3.c +0 -247629
- package/vendor/sqlite3.h +0 -13068
- package/vendor/sqlite3ext.h +0 -709
|
@@ -25,10 +25,6 @@ public:
|
|
|
25
25
|
// sqlite3 bindings
|
|
26
26
|
int sqlite3_open(const std::string &dbPath);
|
|
27
27
|
int sqlite3_close();
|
|
28
|
-
int sqlite3_enable_load_extension(int onoff);
|
|
29
|
-
int sqlite3_load_extension(const std::string &libPath,
|
|
30
|
-
const std::string &entryProc);
|
|
31
|
-
void sqlite3_update_hook(bool enabled);
|
|
32
28
|
|
|
33
29
|
private:
|
|
34
30
|
explicit SQLite3Wrapper(jni::alias_ref<SQLite3Wrapper::jhybridobject> jThis)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
package expo.modules.sqlite
|
|
4
|
+
|
|
5
|
+
import expo.modules.kotlin.sharedobjects.SharedRef
|
|
6
|
+
|
|
7
|
+
internal class NativeDatabase(val dbName: String, val openOptions: OpenDatabaseOptions) : SharedRef<NativeDatabaseBinding>(NativeDatabaseBinding()) {
|
|
8
|
+
override fun equals(other: Any?): Boolean {
|
|
9
|
+
return other is NativeDatabase && this.ref == other.ref
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
package expo.modules.sqlite
|
|
4
|
+
|
|
5
|
+
import com.facebook.jni.HybridData
|
|
6
|
+
import expo.modules.core.interfaces.DoNotStrip
|
|
7
|
+
|
|
8
|
+
private typealias UpdateListener = (dbName: String, tableName: String, operationType: Int, rowID: Long) -> Unit
|
|
9
|
+
|
|
10
|
+
@Suppress("KotlinJniMissingFunction")
|
|
11
|
+
@DoNotStrip
|
|
12
|
+
internal class NativeDatabaseBinding {
|
|
13
|
+
@DoNotStrip
|
|
14
|
+
private val mHybridData: HybridData
|
|
15
|
+
|
|
16
|
+
private var mUpdateListener: UpdateListener? = null
|
|
17
|
+
|
|
18
|
+
init {
|
|
19
|
+
mHybridData = initHybrid()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Enable data change notifications
|
|
24
|
+
*/
|
|
25
|
+
fun enableUpdateHook(listener: UpdateListener) {
|
|
26
|
+
sqlite3_update_hook(true)
|
|
27
|
+
mUpdateListener = listener
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Disable data change notifications
|
|
32
|
+
*/
|
|
33
|
+
fun disableUpdateHook() {
|
|
34
|
+
mUpdateListener = null
|
|
35
|
+
sqlite3_update_hook(false)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// region sqlite3 bindings
|
|
39
|
+
|
|
40
|
+
external fun sqlite3_changes(): Int
|
|
41
|
+
external fun sqlite3_close(): Int
|
|
42
|
+
external fun sqlite3_db_filename(dbName: String): String
|
|
43
|
+
external fun sqlite3_enable_load_extension(onoff: Int): Int
|
|
44
|
+
external fun sqlite3_exec(source: String): Int
|
|
45
|
+
external fun sqlite3_get_autocommit(): Int
|
|
46
|
+
external fun sqlite3_last_insert_rowid(): Long
|
|
47
|
+
external fun sqlite3_load_extension(libPath: String, entryProc: String): Int
|
|
48
|
+
external fun sqlite3_open(dbPath: String): Int
|
|
49
|
+
external fun sqlite3_prepare_v2(source: String, statement: NativeStatementBinding): Int
|
|
50
|
+
private external fun sqlite3_update_hook(enabled: Boolean) // Keeps it private internally and uses `enableUpdateHook` publicly
|
|
51
|
+
|
|
52
|
+
external fun convertSqlLiteErrorToString(): String
|
|
53
|
+
|
|
54
|
+
// endregion
|
|
55
|
+
|
|
56
|
+
// region internals
|
|
57
|
+
|
|
58
|
+
private external fun initHybrid(): HybridData
|
|
59
|
+
|
|
60
|
+
@Suppress("unused")
|
|
61
|
+
@DoNotStrip
|
|
62
|
+
private fun onUpdate(action: Int, dbName: String, tableName: String, rowId: Long) {
|
|
63
|
+
mUpdateListener?.invoke(dbName, tableName, action, rowId)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// endregion
|
|
67
|
+
|
|
68
|
+
companion object {
|
|
69
|
+
init {
|
|
70
|
+
System.loadLibrary("expo-sqlite")
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// These error code should be synced with sqlite3.h
|
|
74
|
+
const val SQLITE_OK = 0
|
|
75
|
+
|
|
76
|
+
const val SQLITE_ROW = 100
|
|
77
|
+
const val SQLITE_DONE = 101
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
package expo.modules.sqlite
|
|
4
|
+
|
|
5
|
+
import expo.modules.kotlin.sharedobjects.SharedRef
|
|
6
|
+
|
|
7
|
+
internal class NativeStatement : SharedRef<NativeStatementBinding>(NativeStatementBinding()) {
|
|
8
|
+
override fun equals(other: Any?): Boolean {
|
|
9
|
+
return other is NativeStatement && this.ref == other.ref
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
package expo.modules.sqlite
|
|
4
|
+
|
|
5
|
+
import com.facebook.jni.HybridData
|
|
6
|
+
import expo.modules.core.interfaces.DoNotStrip
|
|
7
|
+
|
|
8
|
+
internal typealias ColumnNames = ArrayList<String>
|
|
9
|
+
internal typealias ColumnValues = ArrayList<Any>
|
|
10
|
+
|
|
11
|
+
@Suppress("KotlinJniMissingFunction")
|
|
12
|
+
@DoNotStrip
|
|
13
|
+
internal class NativeStatementBinding {
|
|
14
|
+
@DoNotStrip
|
|
15
|
+
private val mHybridData: HybridData
|
|
16
|
+
|
|
17
|
+
init {
|
|
18
|
+
mHybridData = initHybrid()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// region sqlite3 bindings
|
|
22
|
+
|
|
23
|
+
external fun sqlite3_bind_parameter_index(name: String): Int
|
|
24
|
+
external fun sqlite3_column_count(): Int
|
|
25
|
+
external fun sqlite3_column_name(index: Int): String
|
|
26
|
+
external fun sqlite3_finalize(): Int
|
|
27
|
+
external fun sqlite3_reset(): Int
|
|
28
|
+
external fun sqlite3_step(): Int
|
|
29
|
+
|
|
30
|
+
external fun bindStatementParam(index: Int, param: Any): Int
|
|
31
|
+
external fun getColumnNames(): ColumnNames
|
|
32
|
+
external fun getColumnValues(): ColumnValues
|
|
33
|
+
|
|
34
|
+
// endregion
|
|
35
|
+
|
|
36
|
+
// region internals
|
|
37
|
+
|
|
38
|
+
private external fun initHybrid(): HybridData
|
|
39
|
+
|
|
40
|
+
// endregion
|
|
41
|
+
}
|
|
@@ -1,15 +1,26 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
1
3
|
package expo.modules.sqlite
|
|
2
4
|
|
|
5
|
+
import expo.modules.core.interfaces.DoNotStrip
|
|
3
6
|
import expo.modules.kotlin.exception.CodedException
|
|
4
7
|
|
|
5
|
-
class OpenDatabaseException(name: String) :
|
|
6
|
-
CodedException("
|
|
8
|
+
internal class OpenDatabaseException(name: String) :
|
|
9
|
+
CodedException("Could not open database - name[$name]")
|
|
7
10
|
|
|
8
|
-
class DatabaseNotFoundException(name: String) :
|
|
11
|
+
internal class DatabaseNotFoundException(name: String) :
|
|
9
12
|
CodedException("Database '$name' not found")
|
|
10
13
|
|
|
11
|
-
class DeleteDatabaseException(name: String) :
|
|
14
|
+
internal class DeleteDatabaseException(name: String) :
|
|
15
|
+
CodedException("Unable to delete database '$name' that is currently open. Close it prior to deletion.")
|
|
16
|
+
|
|
17
|
+
internal class DeleteDatabaseFileException(name: String) :
|
|
12
18
|
CodedException("Unable to delete the database file for '$name' database")
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
@DoNotStrip
|
|
21
|
+
internal class SQLiteErrorException(message: String) :
|
|
22
|
+
CodedException("ERR_INTERNAL_SQLITE_ERROR", message, null)
|
|
23
|
+
|
|
24
|
+
@DoNotStrip
|
|
25
|
+
internal class InvalidConvertibleException(message: String) :
|
|
26
|
+
CodedException(message)
|
|
@@ -1,19 +1,32 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
1
3
|
package expo.modules.sqlite
|
|
2
4
|
|
|
3
5
|
import expo.modules.kotlin.records.Field
|
|
4
6
|
import expo.modules.kotlin.records.Record
|
|
5
7
|
import expo.modules.kotlin.types.Enumerable
|
|
6
8
|
|
|
7
|
-
data class Query(
|
|
9
|
+
internal data class Query(
|
|
8
10
|
@Field
|
|
9
11
|
val sql: String,
|
|
10
12
|
@Field
|
|
11
13
|
val args: List<Any?>
|
|
12
14
|
) : Record
|
|
13
15
|
|
|
14
|
-
enum class
|
|
16
|
+
internal enum class SQLAction(val value: String) : Enumerable {
|
|
15
17
|
INSERT("insert"),
|
|
16
18
|
UPDATE("update"),
|
|
17
19
|
DELETE("delete"),
|
|
18
|
-
UNKNOWN("unknown")
|
|
20
|
+
UNKNOWN("unknown");
|
|
21
|
+
|
|
22
|
+
companion object {
|
|
23
|
+
fun fromCode(value: Int): SQLAction {
|
|
24
|
+
return when (value) {
|
|
25
|
+
9 -> DELETE
|
|
26
|
+
18 -> INSERT
|
|
27
|
+
23 -> UPDATE
|
|
28
|
+
else -> UNKNOWN
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
19
32
|
}
|
|
@@ -3,16 +3,12 @@ package expo.modules.sqlite
|
|
|
3
3
|
import com.facebook.jni.HybridData
|
|
4
4
|
import expo.modules.core.interfaces.DoNotStrip
|
|
5
5
|
|
|
6
|
-
private typealias UpdateListener = (tableName: String, operationType: Int, rowID: Long) -> Unit
|
|
7
|
-
|
|
8
6
|
@Suppress("KotlinJniMissingFunction")
|
|
9
7
|
@DoNotStrip
|
|
10
8
|
class SQLite3Wrapper private constructor() {
|
|
11
9
|
@DoNotStrip
|
|
12
10
|
private val mHybridData: HybridData
|
|
13
11
|
|
|
14
|
-
private var mUpdateListener: UpdateListener? = null
|
|
15
|
-
|
|
16
12
|
init {
|
|
17
13
|
mHybridData = initHybrid()
|
|
18
14
|
}
|
|
@@ -22,43 +18,17 @@ class SQLite3Wrapper private constructor() {
|
|
|
22
18
|
*/
|
|
23
19
|
external fun executeSql(sql: String, args: List<Any?>, readOnly: Boolean): List<Any>
|
|
24
20
|
|
|
25
|
-
/**
|
|
26
|
-
* Enable data change notifications
|
|
27
|
-
*/
|
|
28
|
-
fun enableUpdateHook(listener: UpdateListener) {
|
|
29
|
-
sqlite3_update_hook(true)
|
|
30
|
-
mUpdateListener = listener
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Disable data change notifications
|
|
35
|
-
*/
|
|
36
|
-
fun disableUpdateHook() {
|
|
37
|
-
mUpdateListener = null
|
|
38
|
-
sqlite3_update_hook(false)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
21
|
// region sqlite3 bindings
|
|
42
22
|
|
|
43
23
|
external fun sqlite3_open(dbPath: String): Int
|
|
44
24
|
external fun sqlite3_close(): Int
|
|
45
25
|
|
|
46
|
-
external fun sqlite3_enable_load_extension(onoff: Int): Int
|
|
47
|
-
external fun sqlite3_load_extension(libPath: String, entryProc: String): Int
|
|
48
|
-
|
|
49
|
-
private external fun sqlite3_update_hook(enabled: Boolean)
|
|
50
|
-
|
|
51
26
|
// endregion
|
|
52
27
|
|
|
53
28
|
// region internals
|
|
54
29
|
|
|
55
30
|
private external fun initHybrid(): HybridData
|
|
56
31
|
|
|
57
|
-
@DoNotStrip
|
|
58
|
-
private fun onUpdate(action: Int, dbName: String, tableName: String, rowId: Long) {
|
|
59
|
-
mUpdateListener?.invoke(tableName, action, rowId)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
32
|
// endregion
|
|
63
33
|
|
|
64
34
|
companion object {
|
|
@@ -22,82 +22,3 @@ internal fun ensureDirExists(dir: File): File {
|
|
|
22
22
|
}
|
|
23
23
|
return dir
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
internal fun pluginResultsToPrimitiveData(results: List<SQLiteModule.SQLitePluginResult>): List<Any> {
|
|
27
|
-
return results.map { convertPluginResultToArray(it) }
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
private fun convertPluginResultToArray(result: SQLiteModule.SQLitePluginResult): List<Any?> {
|
|
31
|
-
val rowsContent = result.rows.map { row ->
|
|
32
|
-
row.map { value ->
|
|
33
|
-
when (value) {
|
|
34
|
-
null -> null
|
|
35
|
-
is String -> value
|
|
36
|
-
is Boolean -> value
|
|
37
|
-
else -> (value as Number).toDouble()
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return arrayListOf(
|
|
43
|
-
result.error?.message,
|
|
44
|
-
result.insertId.toInt(),
|
|
45
|
-
result.rowsAffected,
|
|
46
|
-
result.columns,
|
|
47
|
-
rowsContent
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
private fun isPragma(str: String): Boolean {
|
|
52
|
-
return startsWithCaseInsensitive(str, "pragma")
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
private fun isSelectCTE(str: String): Boolean {
|
|
56
|
-
return startsWithCaseInsensitive(str, "with") && containsCaseInsensitive(str, "select")
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
private fun isPragmaReadOnly(str: String): Boolean {
|
|
60
|
-
return isPragma(str) && !str.contains('=')
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
internal fun isSelect(str: String): Boolean {
|
|
64
|
-
return startsWithCaseInsensitive(str, "select") || isSelectCTE(str) || isPragmaReadOnly(str)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
internal fun isInsert(str: String): Boolean {
|
|
68
|
-
return startsWithCaseInsensitive(str, "insert")
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
internal fun isUpdate(str: String): Boolean {
|
|
72
|
-
return startsWithCaseInsensitive(str, "update")
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
internal fun isDelete(str: String): Boolean {
|
|
76
|
-
return startsWithCaseInsensitive(str, "delete")
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
private fun startsWithCaseInsensitive(str: String, substr: String): Boolean {
|
|
80
|
-
return str.trimStart().startsWith(substr, true)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
private fun containsCaseInsensitive(str: String, substr: String): Boolean {
|
|
84
|
-
return str.trimStart().contains(substr, true)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
internal fun convertParamsToStringArray(paramArrayArg: List<Any?>): Array<String?> {
|
|
88
|
-
return paramArrayArg.map { param ->
|
|
89
|
-
when (param) {
|
|
90
|
-
is String -> unescapeBlob(param)
|
|
91
|
-
is Boolean -> if (param) "1" else "0"
|
|
92
|
-
is Double -> param.toString()
|
|
93
|
-
null -> null
|
|
94
|
-
else -> throw ClassCastException("Could not find proper SQLite data type for argument: $")
|
|
95
|
-
}
|
|
96
|
-
}.toTypedArray()
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
private fun unescapeBlob(str: String): String {
|
|
100
|
-
return str.replace("\u0001\u0001", "\u0000")
|
|
101
|
-
.replace("\u0001\u0002", "\u0001")
|
|
102
|
-
.replace("\u0002\u0002", "\u0002")
|
|
103
|
-
}
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
package expo.modules.sqlite
|
|
3
3
|
|
|
4
4
|
import android.content.Context
|
|
5
|
-
import android.util.Log
|
|
6
5
|
import androidx.collection.ArrayMap
|
|
7
|
-
import androidx.core.os.bundleOf
|
|
8
6
|
import expo.modules.kotlin.exception.Exceptions
|
|
9
7
|
import expo.modules.kotlin.modules.Module
|
|
10
8
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
@@ -14,7 +12,6 @@ import java.util.*
|
|
|
14
12
|
|
|
15
13
|
class SQLiteModule : Module() {
|
|
16
14
|
private val cachedDatabase = ArrayMap<String, SQLite3Wrapper>()
|
|
17
|
-
private var hasListeners = false
|
|
18
15
|
|
|
19
16
|
private val context: Context
|
|
20
17
|
get() = appContext.reactContext ?: throw Exceptions.ReactContextLost()
|
|
@@ -22,8 +19,6 @@ class SQLiteModule : Module() {
|
|
|
22
19
|
override fun definition() = ModuleDefinition {
|
|
23
20
|
Name("ExpoSQLite")
|
|
24
21
|
|
|
25
|
-
Events("onDatabaseChange")
|
|
26
|
-
|
|
27
22
|
AsyncFunction("exec") { dbName: String, queries: List<Query>, readOnly: Boolean ->
|
|
28
23
|
return@AsyncFunction execute(dbName, queries, readOnly)
|
|
29
24
|
}
|
|
@@ -46,28 +41,19 @@ class SQLiteModule : Module() {
|
|
|
46
41
|
|
|
47
42
|
AsyncFunction("deleteAsync") { dbName: String ->
|
|
48
43
|
if (cachedDatabase[dbName] != null) {
|
|
49
|
-
throw
|
|
44
|
+
throw DeleteDatabaseException(dbName)
|
|
50
45
|
}
|
|
51
46
|
val dbFile = File(pathForDatabaseName(dbName))
|
|
52
47
|
if (!dbFile.exists()) {
|
|
53
48
|
throw DatabaseNotFoundException(dbName)
|
|
54
49
|
}
|
|
55
50
|
if (!dbFile.delete()) {
|
|
56
|
-
throw
|
|
51
|
+
throw DeleteDatabaseFileException(dbName)
|
|
57
52
|
}
|
|
58
53
|
}
|
|
59
54
|
|
|
60
|
-
OnStartObserving {
|
|
61
|
-
hasListeners = true
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
OnStopObserving {
|
|
65
|
-
hasListeners = false
|
|
66
|
-
}
|
|
67
|
-
|
|
68
55
|
OnDestroy {
|
|
69
56
|
cachedDatabase.values.forEach {
|
|
70
|
-
it.executeSql("SELECT crsql_finalize()", emptyList(), false)
|
|
71
57
|
it.sqlite3_close()
|
|
72
58
|
}
|
|
73
59
|
}
|
|
@@ -96,50 +82,15 @@ class SQLiteModule : Module() {
|
|
|
96
82
|
|
|
97
83
|
cachedDatabase.remove(dbName)
|
|
98
84
|
val db = SQLite3Wrapper.open(path) ?: return null
|
|
99
|
-
loadExtensions(db)
|
|
100
|
-
addUpdateListener(db)
|
|
101
85
|
cachedDatabase[dbName] = db
|
|
102
86
|
return db
|
|
103
87
|
}
|
|
104
88
|
|
|
105
|
-
private fun loadExtensions(db: SQLite3Wrapper) {
|
|
106
|
-
var errCode = db.sqlite3_enable_load_extension(1)
|
|
107
|
-
if (errCode != SQLite3Wrapper.SQLITE_OK) {
|
|
108
|
-
Log.e(TAG, "Failed to enable sqlite3 extensions - errCode[$errCode]")
|
|
109
|
-
return
|
|
110
|
-
}
|
|
111
|
-
errCode = db.sqlite3_load_extension("libcrsqlite", "sqlite3_crsqlite_init")
|
|
112
|
-
if (errCode != SQLite3Wrapper.SQLITE_OK) {
|
|
113
|
-
Log.e(TAG, "Failed to load crsqlite extension - errCode[$errCode]")
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
89
|
private fun execute(dbName: String, queries: List<Query>, readOnly: Boolean): List<Any> {
|
|
118
90
|
val db = openDatabase(dbName) ?: throw OpenDatabaseException(dbName)
|
|
119
91
|
return queries.map { db.executeSql(it.sql, it.args, readOnly) }
|
|
120
92
|
}
|
|
121
93
|
|
|
122
|
-
private fun addUpdateListener(db: SQLite3Wrapper) {
|
|
123
|
-
db.enableUpdateHook { tableName: String, operationType: Int, rowID: Long ->
|
|
124
|
-
if (!hasListeners) {
|
|
125
|
-
return@enableUpdateHook
|
|
126
|
-
}
|
|
127
|
-
sendEvent(
|
|
128
|
-
"onDatabaseChange",
|
|
129
|
-
bundleOf(
|
|
130
|
-
"tableName" to tableName,
|
|
131
|
-
"rowId" to rowID,
|
|
132
|
-
"typeId" to when (operationType) {
|
|
133
|
-
9 -> SqlAction.DELETE.value
|
|
134
|
-
18 -> SqlAction.INSERT.value
|
|
135
|
-
23 -> SqlAction.UPDATE.value
|
|
136
|
-
else -> SqlAction.UNKNOWN.value
|
|
137
|
-
}
|
|
138
|
-
)
|
|
139
|
-
)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
94
|
internal class SQLitePluginResult(
|
|
144
95
|
val rows: Array<Array<Any?>>,
|
|
145
96
|
val columns: Array<String?>,
|
|
@@ -147,8 +98,4 @@ class SQLiteModule : Module() {
|
|
|
147
98
|
val insertId: Long,
|
|
148
99
|
val error: Throwable?
|
|
149
100
|
)
|
|
150
|
-
|
|
151
|
-
companion object {
|
|
152
|
-
private val TAG = SQLiteModule::class.java.simpleName
|
|
153
|
-
}
|
|
154
101
|
}
|