@fto-consult/expo-ui 6.37.3 → 6.37.5
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/App.js +0 -1
- package/node_modules/.package-lock.json +26218 -26188
- package/node_modules/@fto-consult/common/babel.config.alias.js +18 -1
- package/node_modules/@fto-consult/common/package.json +1 -1
- package/node_modules/@react-native-async-storage/async-storage/LICENSE +21 -0
- package/node_modules/@react-native-async-storage/async-storage/README.md +27 -0
- package/node_modules/@react-native-async-storage/async-storage/RNCAsyncStorage.podspec +19 -0
- package/node_modules/@react-native-async-storage/async-storage/android/build.gradle +142 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/AndroidManifest.xml +6 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java +178 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageErrorUtil.java +45 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageExpoMigration.java +154 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageModule.java +424 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java +58 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/ReactDatabaseSupplier.java +163 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/SerialExecutor.java +40 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/ArgumentHelpers.kt +86 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/ErrorHelpers.kt +39 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageModule.kt +90 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageSupplier.kt +161 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/test/java/com/reactnativecommunity/asyncstorage/next/ArgumentHelpersTest.kt +93 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/test/java/com/reactnativecommunity/asyncstorage/next/StorageTest.kt +141 -0
- package/node_modules/@react-native-async-storage/async-storage/android/testresults.gradle +38 -0
- package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.h +51 -0
- package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.m +898 -0
- package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.xcodeproj/project.pbxproj +283 -0
- package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorageDelegate.h +73 -0
- package/node_modules/@react-native-async-storage/async-storage/jest/async-storage-mock.d.ts +9 -0
- package/node_modules/@react-native-async-storage/async-storage/jest/async-storage-mock.js +109 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.js +164 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.native.js +366 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.native.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/RCTAsyncStorage.js +30 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/RCTAsyncStorage.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/helpers.js +69 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/helpers.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/hooks.js +44 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/hooks.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/index.js +22 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/index.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/shouldFallbackToLegacyNativeModule.js +39 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/shouldFallbackToLegacyNativeModule.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/types.js +2 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/types.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.js +153 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.native.js +348 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.native.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/RCTAsyncStorage.js +20 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/RCTAsyncStorage.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/helpers.js +56 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/helpers.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/hooks.js +34 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/hooks.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/index.js +4 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/index.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/shouldFallbackToLegacyNativeModule.js +31 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/shouldFallbackToLegacyNativeModule.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/types.js +2 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/types.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/AsyncStorage.d.ts +10 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/AsyncStorage.native.d.ts +16 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/RCTAsyncStorage.d.ts +2 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/helpers.d.ts +5 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/hooks.d.ts +2 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/index.d.ts +4 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/shouldFallbackToLegacyNativeModule.d.ts +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/types.d.ts +113 -0
- package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/project.pbxproj +385 -0
- package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/xcshareddata/xcschemes/RNCAsyncStorage-macOS.xcscheme +67 -0
- package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/xcshareddata/xcschemes/RNCAsyncStorage.xcscheme +67 -0
- package/node_modules/@react-native-async-storage/async-storage/package.json +197 -0
- package/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.native.ts +356 -0
- package/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.ts +173 -0
- package/node_modules/@react-native-async-storage/async-storage/src/RCTAsyncStorage.ts +28 -0
- package/node_modules/@react-native-async-storage/async-storage/src/helpers.ts +74 -0
- package/node_modules/@react-native-async-storage/async-storage/src/hooks.ts +11 -0
- package/node_modules/@react-native-async-storage/async-storage/src/index.ts +7 -0
- package/node_modules/@react-native-async-storage/async-storage/src/shouldFallbackToLegacyNativeModule.ts +34 -0
- package/node_modules/@react-native-async-storage/async-storage/src/types.ts +155 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/PropertySheet.props +16 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/ReactNativeAsyncStorage.vcxproj +172 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/ReactNativeAsyncStorage.vcxproj.filters +34 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/packages.config +4 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage.sln +172 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/PropertySheet.props +16 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/ReactNativeAsyncStorage61.vcxproj +157 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/ReactNativeAsyncStorage61.vcxproj.filters +34 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/packages.config +4 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61.sln +195 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage62.sln +192 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/DBStorage.cpp +599 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/DBStorage.h +162 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/RNCAsyncStorage.h +118 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactNativeAsyncStorage.def +3 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.cpp +20 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.h +23 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.idl +7 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/pch.cpp +3 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/pch.h +15 -0
- package/node_modules/merge-options/index.d.ts +2 -0
- package/node_modules/merge-options/index.js +171 -0
- package/node_modules/merge-options/index.mjs +8 -0
- package/node_modules/merge-options/license +21 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/index.d.ts +29 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/index.js +10 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/license +9 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/package.json +38 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/readme.md +54 -0
- package/node_modules/merge-options/package.json +59 -0
- package/node_modules/merge-options/readme.md +130 -0
- package/package.json +131 -130
- package/src/components/Chart/appexChart/appexChart.html +23 -23
@@ -0,0 +1,599 @@
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
2
|
+
// Licensed under the MIT License.
|
3
|
+
#include "pch.h"
|
4
|
+
|
5
|
+
#include "DBStorage.h"
|
6
|
+
|
7
|
+
#include <unordered_map>
|
8
|
+
|
9
|
+
namespace winrt
|
10
|
+
{
|
11
|
+
using namespace Microsoft::ReactNative;
|
12
|
+
using namespace Windows::ApplicationModel::Core;
|
13
|
+
using namespace Windows::Data::Json;
|
14
|
+
using namespace Windows::Foundation;
|
15
|
+
using namespace Windows::Storage;
|
16
|
+
} // namespace winrt
|
17
|
+
|
18
|
+
// All functions below return std::nullopt on error.
|
19
|
+
#define CHECK(expr) \
|
20
|
+
if (!(expr)) { \
|
21
|
+
return std::nullopt; \
|
22
|
+
}
|
23
|
+
|
24
|
+
// Convenience macro to call CheckSQLiteResult.
|
25
|
+
#define CHECK_SQL_OK(expr) CHECK(CheckSQLiteResult(db, m_errorManager, (expr)))
|
26
|
+
|
27
|
+
namespace
|
28
|
+
{
|
29
|
+
// To implement safe operator& for unique_ptr.
|
30
|
+
template <typename T, typename TDeleter>
|
31
|
+
struct UniquePtrSetter {
|
32
|
+
UniquePtrSetter(std::unique_ptr<T, TDeleter> &ptr) noexcept : m_ptr(ptr)
|
33
|
+
{
|
34
|
+
}
|
35
|
+
|
36
|
+
~UniquePtrSetter()
|
37
|
+
{
|
38
|
+
m_ptr = {m_rawPtr, m_ptr.get_deleter()};
|
39
|
+
}
|
40
|
+
|
41
|
+
operator T **() noexcept
|
42
|
+
{
|
43
|
+
return &m_rawPtr;
|
44
|
+
}
|
45
|
+
|
46
|
+
private:
|
47
|
+
T *m_rawPtr{};
|
48
|
+
std::unique_ptr<T, TDeleter> &m_ptr;
|
49
|
+
};
|
50
|
+
|
51
|
+
template <typename T, typename TDeleter>
|
52
|
+
UniquePtrSetter<T, TDeleter> operator&(std::unique_ptr<T, TDeleter> &ptr) noexcept
|
53
|
+
{
|
54
|
+
return UniquePtrSetter<T, TDeleter>(ptr);
|
55
|
+
}
|
56
|
+
|
57
|
+
using ExecCallback = int(SQLITE_CALLBACK *)(void *callbackData,
|
58
|
+
int columnCount,
|
59
|
+
char **columnTexts,
|
60
|
+
char **columnNames);
|
61
|
+
|
62
|
+
// Execute the provided SQLite statement (and optional execCallback & user data
|
63
|
+
// in callbackData). On error, report it to the errorManager and return std::nullopt.
|
64
|
+
std::optional<bool> Exec(sqlite3 *db,
|
65
|
+
DBStorage::ErrorManager &errorManager,
|
66
|
+
const char *statement,
|
67
|
+
ExecCallback execCallback = nullptr,
|
68
|
+
void *callbackData = nullptr) noexcept
|
69
|
+
{
|
70
|
+
auto errMsg = std::unique_ptr<char, decltype(&sqlite3_free)>{nullptr, &sqlite3_free};
|
71
|
+
int rc = sqlite3_exec(db, statement, execCallback, callbackData, &errMsg);
|
72
|
+
if (errMsg) {
|
73
|
+
return errorManager.AddError(errMsg.get());
|
74
|
+
}
|
75
|
+
if (rc != SQLITE_OK) {
|
76
|
+
return errorManager.AddError(sqlite3_errmsg(db));
|
77
|
+
}
|
78
|
+
return true;
|
79
|
+
}
|
80
|
+
|
81
|
+
// Convenience wrapper for using Exec with lambda expressions.
|
82
|
+
template <typename Fn>
|
83
|
+
std::optional<bool>
|
84
|
+
Exec(sqlite3 *db, DBStorage::ErrorManager &errorManager, const char *statement, Fn &fn) noexcept
|
85
|
+
{
|
86
|
+
return Exec(
|
87
|
+
db,
|
88
|
+
errorManager,
|
89
|
+
statement,
|
90
|
+
[](void *callbackData, int columnCount, char **columnTexts, char **columnNames) {
|
91
|
+
return (*static_cast<Fn *>(callbackData))(columnCount, columnTexts, columnNames);
|
92
|
+
},
|
93
|
+
&fn);
|
94
|
+
}
|
95
|
+
|
96
|
+
// Check that the args collection size is less than SQLITE_LIMIT_VARIABLE_NUMBER, and that
|
97
|
+
// every member of args is not an empty string.
|
98
|
+
// On error, report it to the errorManager and return std::nullopt.
|
99
|
+
std::optional<bool> CheckArgs(sqlite3 *db,
|
100
|
+
DBStorage::ErrorManager &errorManager,
|
101
|
+
const std::vector<std::string> &args) noexcept
|
102
|
+
{
|
103
|
+
int varLimit = sqlite3_limit(db, SQLITE_LIMIT_VARIABLE_NUMBER, -1);
|
104
|
+
auto argCount = args.size();
|
105
|
+
if (argCount > static_cast<size_t>(std::numeric_limits<int>::max()) ||
|
106
|
+
static_cast<int>(argCount) > varLimit) {
|
107
|
+
char errorMsg[60];
|
108
|
+
sprintf_s(errorMsg, "Too many keys. Maximum supported keys :%d.", varLimit);
|
109
|
+
return errorManager.AddError(errorMsg);
|
110
|
+
}
|
111
|
+
for (int i = 0; i < static_cast<int>(argCount); i++) {
|
112
|
+
if (args[i].empty()) {
|
113
|
+
return errorManager.AddError("The key must be a non-empty string.");
|
114
|
+
}
|
115
|
+
}
|
116
|
+
return true;
|
117
|
+
}
|
118
|
+
|
119
|
+
// RAII object to manage SQLite transaction. On destruction, if
|
120
|
+
// Commit() has not been called, rolls back the transactions.
|
121
|
+
// The provided SQLite connection handle & errorManager must outlive
|
122
|
+
// the Sqlite3Transaction object.
|
123
|
+
struct Sqlite3Transaction {
|
124
|
+
Sqlite3Transaction(sqlite3 *db, DBStorage::ErrorManager &errorManager) noexcept
|
125
|
+
: m_db(db), m_errorManager(errorManager)
|
126
|
+
{
|
127
|
+
if (!Exec(m_db, m_errorManager, "BEGIN TRANSACTION")) {
|
128
|
+
m_db = nullptr;
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
Sqlite3Transaction(const Sqlite3Transaction &) = delete;
|
133
|
+
Sqlite3Transaction &operator=(const Sqlite3Transaction &) = delete;
|
134
|
+
|
135
|
+
~Sqlite3Transaction()
|
136
|
+
{
|
137
|
+
Rollback();
|
138
|
+
}
|
139
|
+
|
140
|
+
explicit operator bool() const noexcept
|
141
|
+
{
|
142
|
+
return m_db != nullptr;
|
143
|
+
}
|
144
|
+
|
145
|
+
std::optional<bool> Commit() noexcept
|
146
|
+
{
|
147
|
+
if (m_db) {
|
148
|
+
return Exec(std::exchange(m_db, nullptr), m_errorManager, "COMMIT");
|
149
|
+
}
|
150
|
+
return std::nullopt;
|
151
|
+
}
|
152
|
+
|
153
|
+
std::optional<bool> Rollback() noexcept
|
154
|
+
{
|
155
|
+
if (m_db) {
|
156
|
+
return Exec(std::exchange(m_db, nullptr), m_errorManager, "ROLLBACK");
|
157
|
+
}
|
158
|
+
return std::nullopt;
|
159
|
+
}
|
160
|
+
|
161
|
+
private:
|
162
|
+
sqlite3 *m_db{};
|
163
|
+
DBStorage::ErrorManager &m_errorManager;
|
164
|
+
};
|
165
|
+
|
166
|
+
// Append argCount variables to prefix in a comma-separated list.
|
167
|
+
std::string MakeSQLiteParameterizedStatement(const char *prefix, int argCount) noexcept
|
168
|
+
{
|
169
|
+
assert(argCount != 0);
|
170
|
+
std::string result(prefix);
|
171
|
+
result.reserve(result.size() + (argCount * 2) + 1);
|
172
|
+
result += '(';
|
173
|
+
for (int x = 0; x < argCount - 1; x++) {
|
174
|
+
result += "?,";
|
175
|
+
}
|
176
|
+
result += "?)";
|
177
|
+
return result;
|
178
|
+
}
|
179
|
+
|
180
|
+
// Check if sqliteResult is SQLITE_OK.
|
181
|
+
// If not, report the error to the errorManager and return std::nullopt.
|
182
|
+
std::optional<bool> CheckSQLiteResult(sqlite3 *db,
|
183
|
+
DBStorage::ErrorManager &errorManager,
|
184
|
+
int sqliteResult) noexcept
|
185
|
+
{
|
186
|
+
if (sqliteResult == SQLITE_OK) {
|
187
|
+
return true;
|
188
|
+
} else {
|
189
|
+
return errorManager.AddError(sqlite3_errmsg(db));
|
190
|
+
}
|
191
|
+
}
|
192
|
+
|
193
|
+
using StatementPtr = std::unique_ptr<sqlite3_stmt, decltype(&sqlite3_finalize)>;
|
194
|
+
|
195
|
+
// A convenience wrapper for sqlite3_prepare_v2 function.
|
196
|
+
int PrepareStatement(sqlite3 *db,
|
197
|
+
const std::string &statementText,
|
198
|
+
sqlite3_stmt **statement) noexcept
|
199
|
+
{
|
200
|
+
return sqlite3_prepare_v2(db, statementText.c_str(), -1, statement, nullptr);
|
201
|
+
}
|
202
|
+
|
203
|
+
// A convenience wrapper for sqlite3_bind_text function.
|
204
|
+
int BindString(StatementPtr &statement, int index, const std::string &str) noexcept
|
205
|
+
{
|
206
|
+
return sqlite3_bind_text(statement.get(), index, str.c_str(), -1, SQLITE_TRANSIENT);
|
207
|
+
}
|
208
|
+
|
209
|
+
// Merge source into destination.
|
210
|
+
// It only merges objects - all other types are just clobbered (including arrays).
|
211
|
+
void MergeJsonObjects(winrt::JsonObject const &destination,
|
212
|
+
winrt::JsonObject const &source) noexcept
|
213
|
+
{
|
214
|
+
for (auto keyValue : source) {
|
215
|
+
auto key = keyValue.Key();
|
216
|
+
auto sourceValue = keyValue.Value();
|
217
|
+
if (destination.HasKey(key)) {
|
218
|
+
auto destinationValue = destination.GetNamedValue(key);
|
219
|
+
if (destinationValue.ValueType() == winrt::JsonValueType::Object &&
|
220
|
+
sourceValue.ValueType() == winrt::JsonValueType::Object) {
|
221
|
+
MergeJsonObjects(destinationValue.GetObject(), sourceValue.GetObject());
|
222
|
+
continue;
|
223
|
+
}
|
224
|
+
}
|
225
|
+
destination.SetNamedValue(key, sourceValue);
|
226
|
+
}
|
227
|
+
}
|
228
|
+
} // namespace
|
229
|
+
|
230
|
+
// Initialize storage. On error, report it to the errorManager and return std::nullopt.
|
231
|
+
std::optional<sqlite3 *>
|
232
|
+
DBStorage::InitializeStorage(DBStorage::ErrorManager &errorManager) noexcept
|
233
|
+
{
|
234
|
+
winrt::slim_lock_guard guard{m_lock};
|
235
|
+
if (m_db) {
|
236
|
+
return m_db.get();
|
237
|
+
}
|
238
|
+
|
239
|
+
std::string path;
|
240
|
+
try {
|
241
|
+
if (auto pathInspectable =
|
242
|
+
winrt::CoreApplication::Properties().TryLookup(s_dbPathProperty)) {
|
243
|
+
path = winrt::to_string(winrt::unbox_value<winrt::hstring>(pathInspectable));
|
244
|
+
} else {
|
245
|
+
auto const localAppDataPath = winrt::ApplicationData::Current().LocalFolder().Path();
|
246
|
+
path = winrt::to_string(localAppDataPath) + "\\AsyncStorage.db";
|
247
|
+
}
|
248
|
+
} catch (const winrt::hresult_error &error) {
|
249
|
+
errorManager.AddError(winrt::to_string(error.message()));
|
250
|
+
return errorManager.AddError(
|
251
|
+
"Please specify 'React-Native-Community-Async-Storage-Database-Path' in "
|
252
|
+
"CoreApplication::Properties");
|
253
|
+
}
|
254
|
+
|
255
|
+
auto db = DatabasePtr{nullptr, &sqlite3_close};
|
256
|
+
if (sqlite3_open_v2(path.c_str(),
|
257
|
+
&db,
|
258
|
+
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX,
|
259
|
+
nullptr) != SQLITE_OK) {
|
260
|
+
if (db) {
|
261
|
+
return errorManager.AddError(sqlite3_errmsg(db.get()));
|
262
|
+
} else {
|
263
|
+
return errorManager.AddError("Storage database cannot be opened.");
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
int userVersion = 0;
|
268
|
+
auto getUserVersionCallback =
|
269
|
+
[](void *callbackData, int colomnCount, char **columnTexts, char ** /*columnNames*/) {
|
270
|
+
if (colomnCount < 1) {
|
271
|
+
return 1;
|
272
|
+
}
|
273
|
+
*static_cast<int *>(callbackData) = atoi(columnTexts[0]);
|
274
|
+
return SQLITE_OK;
|
275
|
+
};
|
276
|
+
|
277
|
+
CHECK(
|
278
|
+
Exec(db.get(), errorManager, "PRAGMA user_version", getUserVersionCallback, &userVersion));
|
279
|
+
|
280
|
+
if (userVersion == 0) {
|
281
|
+
CHECK(Exec(db.get(),
|
282
|
+
errorManager,
|
283
|
+
"CREATE TABLE IF NOT EXISTS AsyncLocalStorage(key TEXT PRIMARY KEY, value TEXT "
|
284
|
+
"NOT NULL); PRAGMA user_version=1"));
|
285
|
+
}
|
286
|
+
|
287
|
+
m_db = std::move(db);
|
288
|
+
return m_db.get();
|
289
|
+
}
|
290
|
+
|
291
|
+
DBStorage::~DBStorage()
|
292
|
+
{
|
293
|
+
decltype(m_tasks) tasks;
|
294
|
+
{
|
295
|
+
// If there is an in-progress async task, cancel it and wait on the condition_variable for
|
296
|
+
// the async task to acknowledge cancellation by nulling out m_action. Once m_action is
|
297
|
+
// null, it is safe to proceed with closing the DB connection. The DB connection is closed
|
298
|
+
// by the m_db destructor.
|
299
|
+
winrt::slim_lock_guard guard{m_lock};
|
300
|
+
swap(tasks, m_tasks);
|
301
|
+
if (m_action) {
|
302
|
+
m_action.Cancel();
|
303
|
+
m_cv.wait(m_lock, [this]() { return m_action == nullptr; });
|
304
|
+
}
|
305
|
+
}
|
306
|
+
}
|
307
|
+
|
308
|
+
// Under the lock, add a task to m_tasks and, if no async task is in progress schedule it.
|
309
|
+
void DBStorage::AddTask(ErrorManager &errorManager,
|
310
|
+
std::function<void(DBStorage::DBTask &task, sqlite3 *db)> &&onRun) noexcept
|
311
|
+
{
|
312
|
+
winrt::slim_lock_guard guard(m_lock);
|
313
|
+
m_tasks.push_back(std::make_unique<DBTask>(errorManager, std::move(onRun)));
|
314
|
+
if (!m_action) {
|
315
|
+
m_action = RunTasks();
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
// On a background thread, while the async task has not been canceled and
|
320
|
+
// there are more tasks to do, run the tasks. When there are either no more
|
321
|
+
// tasks or cancellation has been requested, set m_action to null to report
|
322
|
+
// that and complete the coroutine. N.B., it is important that detecting that
|
323
|
+
// m_tasks is empty and acknowledging completion is done atomically; otherwise
|
324
|
+
// there would be a race between the background task detecting m_tasks.empty()
|
325
|
+
// and AddTask checking the coroutine is running.
|
326
|
+
winrt::Windows::Foundation::IAsyncAction DBStorage::RunTasks() noexcept
|
327
|
+
{
|
328
|
+
auto cancellationToken = co_await winrt::get_cancellation_token();
|
329
|
+
co_await winrt::resume_background();
|
330
|
+
for (;;) {
|
331
|
+
decltype(m_tasks) tasks;
|
332
|
+
sqlite3 *db{nullptr};
|
333
|
+
{
|
334
|
+
winrt::slim_lock_guard guard(m_lock);
|
335
|
+
if (m_tasks.empty()) {
|
336
|
+
m_action = nullptr;
|
337
|
+
m_cv.notify_all();
|
338
|
+
co_return;
|
339
|
+
}
|
340
|
+
std::swap(tasks, m_tasks);
|
341
|
+
db = m_db.get();
|
342
|
+
}
|
343
|
+
|
344
|
+
for (auto &task : tasks) {
|
345
|
+
if (!cancellationToken()) {
|
346
|
+
task->Run(*this, db);
|
347
|
+
} else {
|
348
|
+
task->Cancel();
|
349
|
+
}
|
350
|
+
}
|
351
|
+
}
|
352
|
+
}
|
353
|
+
|
354
|
+
// Add new Error to the error list.
|
355
|
+
// Return std::nullopt for convenience to other methods that use std::nullopt to indicate error
|
356
|
+
// result.
|
357
|
+
std::nullopt_t DBStorage::ErrorManager::AddError(std::string &&message) noexcept
|
358
|
+
{
|
359
|
+
m_errors.push_back(Error{std::move(message)});
|
360
|
+
return std::nullopt;
|
361
|
+
}
|
362
|
+
|
363
|
+
bool DBStorage::ErrorManager::HasErrors() const noexcept
|
364
|
+
{
|
365
|
+
return !m_errors.empty();
|
366
|
+
}
|
367
|
+
|
368
|
+
const std::vector<DBStorage::Error> &DBStorage::ErrorManager::GetErrorList() const noexcept
|
369
|
+
{
|
370
|
+
if (HasErrors()) {
|
371
|
+
return m_errors;
|
372
|
+
}
|
373
|
+
static std::vector<DBStorage::Error> s_unknownError{Error{"Unknown error."}};
|
374
|
+
return s_unknownError;
|
375
|
+
}
|
376
|
+
|
377
|
+
DBStorage::Error DBStorage::ErrorManager::GetCombinedError() const noexcept
|
378
|
+
{
|
379
|
+
auto &errors = GetErrorList();
|
380
|
+
if (errors.size() == 1) {
|
381
|
+
return errors[0];
|
382
|
+
}
|
383
|
+
|
384
|
+
std::string combinedMessage;
|
385
|
+
for (const auto &error : errors) {
|
386
|
+
combinedMessage += error.Message + '\n';
|
387
|
+
}
|
388
|
+
return Error{std::move(combinedMessage)};
|
389
|
+
}
|
390
|
+
|
391
|
+
DBStorage::DBTask::DBTask(DBStorage::ErrorManager &errorManager,
|
392
|
+
std::function<void(DBTask &task, sqlite3 *db)> &&onRun) noexcept
|
393
|
+
: m_errorManager(errorManager), m_onRun(std::move(onRun))
|
394
|
+
{
|
395
|
+
}
|
396
|
+
|
397
|
+
void DBStorage::DBTask::Run(DBStorage &storage, sqlite3 *db) noexcept
|
398
|
+
{
|
399
|
+
if (!db) {
|
400
|
+
// We initialize DB handler on demand to report errors in the task context.
|
401
|
+
if (auto res = storage.InitializeStorage(m_errorManager)) {
|
402
|
+
db = *res;
|
403
|
+
}
|
404
|
+
}
|
405
|
+
if (db) {
|
406
|
+
m_onRun(*this, db);
|
407
|
+
}
|
408
|
+
}
|
409
|
+
|
410
|
+
void DBStorage::DBTask::Cancel() noexcept
|
411
|
+
{
|
412
|
+
m_errorManager.AddError("Task is canceled.");
|
413
|
+
}
|
414
|
+
|
415
|
+
std::optional<std::vector<DBStorage::KeyValue>>
|
416
|
+
DBStorage::DBTask::MultiGet(sqlite3 *db, const std::vector<std::string> &keys) noexcept
|
417
|
+
{
|
418
|
+
CHECK(!m_errorManager.HasErrors());
|
419
|
+
CHECK(CheckArgs(db, m_errorManager, keys));
|
420
|
+
|
421
|
+
auto argCount = static_cast<int>(keys.size());
|
422
|
+
auto sql = MakeSQLiteParameterizedStatement(
|
423
|
+
"SELECT key, value FROM AsyncLocalStorage WHERE key IN ", argCount);
|
424
|
+
auto statement = StatementPtr{nullptr, &sqlite3_finalize};
|
425
|
+
CHECK_SQL_OK(PrepareStatement(db, sql, &statement));
|
426
|
+
for (int i = 0; i < argCount; i++) {
|
427
|
+
CHECK_SQL_OK(BindString(statement, i + 1, keys[i]));
|
428
|
+
}
|
429
|
+
|
430
|
+
std::vector<DBStorage::KeyValue> result;
|
431
|
+
for (;;) {
|
432
|
+
auto stepResult = sqlite3_step(statement.get());
|
433
|
+
if (stepResult == SQLITE_DONE) {
|
434
|
+
break;
|
435
|
+
}
|
436
|
+
if (stepResult != SQLITE_ROW) {
|
437
|
+
return m_errorManager.AddError(sqlite3_errmsg(db));
|
438
|
+
}
|
439
|
+
|
440
|
+
auto key = reinterpret_cast<const char *>(sqlite3_column_text(statement.get(), 0));
|
441
|
+
if (!key) {
|
442
|
+
return m_errorManager.AddError(sqlite3_errmsg(db));
|
443
|
+
}
|
444
|
+
auto value = reinterpret_cast<const char *>(sqlite3_column_text(statement.get(), 1));
|
445
|
+
if (!value) {
|
446
|
+
return m_errorManager.AddError(sqlite3_errmsg(db));
|
447
|
+
}
|
448
|
+
result.push_back(KeyValue{key, value});
|
449
|
+
}
|
450
|
+
return result;
|
451
|
+
}
|
452
|
+
|
453
|
+
std::optional<bool> DBStorage::DBTask::MultiSet(sqlite3 *db,
|
454
|
+
const std::vector<KeyValue> &keyValues) noexcept
|
455
|
+
{
|
456
|
+
CHECK(!m_errorManager.HasErrors());
|
457
|
+
if (keyValues.empty()) {
|
458
|
+
return true; // nothing to do
|
459
|
+
}
|
460
|
+
|
461
|
+
Sqlite3Transaction transaction(db, m_errorManager);
|
462
|
+
CHECK(transaction);
|
463
|
+
auto statement = StatementPtr{nullptr, &sqlite3_finalize};
|
464
|
+
CHECK_SQL_OK(
|
465
|
+
PrepareStatement(db, "INSERT OR REPLACE INTO AsyncLocalStorage VALUES(?, ?)", &statement));
|
466
|
+
for (const auto &keyValue : keyValues) {
|
467
|
+
CHECK_SQL_OK(BindString(statement, 1, keyValue.Key));
|
468
|
+
CHECK_SQL_OK(BindString(statement, 2, keyValue.Value));
|
469
|
+
auto rc = sqlite3_step(statement.get());
|
470
|
+
CHECK(rc == SQLITE_DONE || CheckSQLiteResult(db, m_errorManager, rc));
|
471
|
+
CHECK_SQL_OK(sqlite3_reset(statement.get()));
|
472
|
+
}
|
473
|
+
CHECK(transaction.Commit());
|
474
|
+
return true;
|
475
|
+
}
|
476
|
+
|
477
|
+
std::optional<bool> DBStorage::DBTask::MultiMerge(sqlite3 *db,
|
478
|
+
const std::vector<KeyValue> &keyValues) noexcept
|
479
|
+
{
|
480
|
+
CHECK(!m_errorManager.HasErrors());
|
481
|
+
|
482
|
+
std::vector<std::string> keys;
|
483
|
+
std::unordered_map<std::string, std::string> newValues;
|
484
|
+
keys.reserve(keyValues.size());
|
485
|
+
for (const auto &keyValue : keyValues) {
|
486
|
+
keys.push_back(keyValue.Key);
|
487
|
+
newValues.try_emplace(keyValue.Key, keyValue.Value);
|
488
|
+
}
|
489
|
+
|
490
|
+
auto oldValues = MultiGet(db, keys);
|
491
|
+
CHECK(oldValues);
|
492
|
+
|
493
|
+
std::vector<KeyValue> mergedResults;
|
494
|
+
for (size_t i = 0; i < oldValues->size(); i++) {
|
495
|
+
auto &key = oldValues->at(i).Key;
|
496
|
+
auto &oldValue = oldValues->at(i).Value;
|
497
|
+
auto &newValue = newValues[key];
|
498
|
+
|
499
|
+
winrt::JsonObject oldJson;
|
500
|
+
winrt::JsonObject newJson;
|
501
|
+
if (winrt::JsonObject::TryParse(winrt::to_hstring(oldValue), oldJson) &&
|
502
|
+
winrt::JsonObject::TryParse(winrt::to_hstring(newValue), newJson)) {
|
503
|
+
MergeJsonObjects(oldJson, newJson);
|
504
|
+
mergedResults.push_back(KeyValue{key, winrt::to_string(oldJson.ToString())});
|
505
|
+
} else {
|
506
|
+
return m_errorManager.AddError("Values must be valid JSON object strings");
|
507
|
+
}
|
508
|
+
}
|
509
|
+
|
510
|
+
return MultiSet(db, mergedResults);
|
511
|
+
}
|
512
|
+
|
513
|
+
std::optional<bool> DBStorage::DBTask::MultiRemove(sqlite3 *db,
|
514
|
+
const std::vector<std::string> &keys) noexcept
|
515
|
+
{
|
516
|
+
CHECK(!m_errorManager.HasErrors());
|
517
|
+
CHECK(CheckArgs(db, m_errorManager, keys));
|
518
|
+
auto argCount = static_cast<int>(keys.size());
|
519
|
+
auto sql =
|
520
|
+
MakeSQLiteParameterizedStatement("DELETE FROM AsyncLocalStorage WHERE key IN ", argCount);
|
521
|
+
auto statement = StatementPtr{nullptr, &sqlite3_finalize};
|
522
|
+
CHECK_SQL_OK(PrepareStatement(db, sql, &statement));
|
523
|
+
for (int i = 0; i < argCount; i++) {
|
524
|
+
CHECK_SQL_OK(BindString(statement, i + 1, keys[i]));
|
525
|
+
}
|
526
|
+
for (;;) {
|
527
|
+
auto stepResult = sqlite3_step(statement.get());
|
528
|
+
if (stepResult == SQLITE_DONE) {
|
529
|
+
break;
|
530
|
+
}
|
531
|
+
if (stepResult != SQLITE_ROW) {
|
532
|
+
return m_errorManager.AddError(sqlite3_errmsg(db));
|
533
|
+
}
|
534
|
+
}
|
535
|
+
return true;
|
536
|
+
}
|
537
|
+
|
538
|
+
std::optional<std::vector<std::string>> DBStorage::DBTask::GetAllKeys(sqlite3 *db) noexcept
|
539
|
+
{
|
540
|
+
CHECK(!m_errorManager.HasErrors());
|
541
|
+
std::vector<std::string> result;
|
542
|
+
auto getAllKeysCallback = [&](int columnCount, char **columnTexts, char **) {
|
543
|
+
if (columnCount > 0) {
|
544
|
+
result.emplace_back(columnTexts[0]);
|
545
|
+
}
|
546
|
+
return SQLITE_OK;
|
547
|
+
};
|
548
|
+
|
549
|
+
CHECK(Exec(db, m_errorManager, "SELECT key FROM AsyncLocalStorage", getAllKeysCallback));
|
550
|
+
return result;
|
551
|
+
}
|
552
|
+
|
553
|
+
std::optional<bool> DBStorage::DBTask::RemoveAll(sqlite3 *db) noexcept
|
554
|
+
{
|
555
|
+
CHECK(!m_errorManager.HasErrors());
|
556
|
+
CHECK(Exec(db, m_errorManager, "DELETE FROM AsyncLocalStorage"));
|
557
|
+
return true;
|
558
|
+
}
|
559
|
+
|
560
|
+
// Read KeyValue from a JSON array.
|
561
|
+
void ReadValue(const winrt::IJSValueReader &reader,
|
562
|
+
/*out*/ DBStorage::KeyValue &value) noexcept
|
563
|
+
{
|
564
|
+
if (reader.ValueType() == winrt::JSValueType::Array) {
|
565
|
+
int index = 0;
|
566
|
+
while (reader.GetNextArrayItem()) {
|
567
|
+
if (index == 0) {
|
568
|
+
ReadValue(reader, value.Key);
|
569
|
+
} else if (index == 1) {
|
570
|
+
ReadValue(reader, value.Value);
|
571
|
+
} else {
|
572
|
+
// Read the array till the end to keep reader in a good state.
|
573
|
+
winrt::SkipValue<winrt::JSValue>(reader);
|
574
|
+
}
|
575
|
+
++index;
|
576
|
+
}
|
577
|
+
} else {
|
578
|
+
// To keep reader in a good state.
|
579
|
+
winrt::SkipValue<winrt::JSValue>(reader);
|
580
|
+
}
|
581
|
+
}
|
582
|
+
|
583
|
+
// Write KeyValue to a JSON array.
|
584
|
+
void WriteValue(const winrt::Microsoft::ReactNative::IJSValueWriter &writer,
|
585
|
+
const DBStorage::KeyValue &value) noexcept
|
586
|
+
{
|
587
|
+
writer.WriteArrayBegin();
|
588
|
+
WriteValue(writer, value.Key);
|
589
|
+
WriteValue(writer, value.Value);
|
590
|
+
writer.WriteArrayEnd();
|
591
|
+
}
|
592
|
+
|
593
|
+
// Write Error object to JSON.
|
594
|
+
void WriteValue(const winrt::IJSValueWriter &writer, const DBStorage::Error &value) noexcept
|
595
|
+
{
|
596
|
+
writer.WriteObjectBegin();
|
597
|
+
winrt::WriteProperty(writer, L"message", value.Message);
|
598
|
+
writer.WriteObjectEnd();
|
599
|
+
}
|