@op-engineering/op-sqlite 1.0.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/LICENSE +7 -0
- package/README.md +274 -0
- package/android/.project +17 -0
- package/android/.settings/org.eclipse.buildship.core.prefs +13 -0
- package/android/CMakeLists.txt +57 -0
- package/android/build.gradle +127 -0
- package/android/cpp-adapter.cpp +42 -0
- package/android/gradle.properties +4 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/op/sqlite/OPSQLiteBridge.java +29 -0
- package/android/src/main/java/com/op/sqlite/OPSQLiteModule.java +46 -0
- package/android/src/main/java/com/op/sqlite/OPSQLitePackage.java +26 -0
- package/cpp/DynamicHostObject.cpp +30 -0
- package/cpp/DynamicHostObject.h +30 -0
- package/cpp/ThreadPool.cpp +95 -0
- package/cpp/ThreadPool.h +46 -0
- package/cpp/bindings.cpp +430 -0
- package/cpp/bindings.h +13 -0
- package/cpp/bridge.cpp +502 -0
- package/cpp/bridge.h +34 -0
- package/cpp/logs.h +38 -0
- package/cpp/macros.h +16 -0
- package/cpp/sqlbatchexecutor.cpp +94 -0
- package/cpp/sqlbatchexecutor.h +28 -0
- package/cpp/sqlite3.c +252611 -0
- package/cpp/sqlite3.h +13257 -0
- package/cpp/utils.cpp +218 -0
- package/cpp/utils.h +55 -0
- package/ios/OPSQLite.h +8 -0
- package/ios/OPSQLite.mm +63 -0
- package/ios/OPSQLite.xcodeproj/project.pbxproj +275 -0
- package/lib/commonjs/index.js +190 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/module/index.js +183 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/index.d.ts +108 -0
- package/op-sqlite.podspec +39 -0
- package/package.json +79 -0
- package/src/index.ts +374 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#ifndef DynamicHostObject_h
|
|
2
|
+
#define DynamicHostObject_h
|
|
3
|
+
|
|
4
|
+
#include <jsi/jsi.h>
|
|
5
|
+
#include <any>
|
|
6
|
+
#include <vector>
|
|
7
|
+
#include <unordered_map>
|
|
8
|
+
|
|
9
|
+
namespace osp {
|
|
10
|
+
|
|
11
|
+
namespace jsi = facebook::jsi;
|
|
12
|
+
|
|
13
|
+
class JSI_EXPORT DynamicHostObject: public jsi::HostObject {
|
|
14
|
+
public:
|
|
15
|
+
DynamicHostObject() {};
|
|
16
|
+
|
|
17
|
+
virtual ~DynamicHostObject() {};
|
|
18
|
+
|
|
19
|
+
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt);
|
|
20
|
+
|
|
21
|
+
jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propNameID);
|
|
22
|
+
|
|
23
|
+
std::unordered_map<std::string, std::any> fields;
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#endif /* DynamicHostObject_h */
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#include "ThreadPool.h"
|
|
2
|
+
|
|
3
|
+
namespace osp {
|
|
4
|
+
|
|
5
|
+
ThreadPool::ThreadPool() : done(false)
|
|
6
|
+
{
|
|
7
|
+
// This returns the number of threads supported by the system. If the
|
|
8
|
+
// function can't figure out this information, it returns 0. 0 is not good,
|
|
9
|
+
// so we create at least 1
|
|
10
|
+
auto numberOfThreads = std::thread::hardware_concurrency();
|
|
11
|
+
if (numberOfThreads == 0)
|
|
12
|
+
{
|
|
13
|
+
numberOfThreads = 1;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
for (unsigned i = 0; i < numberOfThreads; ++i)
|
|
17
|
+
{
|
|
18
|
+
// The threads will execute the private member `doWork`. Note that we need
|
|
19
|
+
// to pass a reference to the function (namespaced with the class name) as
|
|
20
|
+
// the first argument, and the current object as second argument
|
|
21
|
+
threads.push_back(std::thread(&ThreadPool::doWork, this));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// The destructor joins all the threads so the program can exit gracefully.
|
|
26
|
+
// This will be executed if there is any exception (e.g. creating the threads)
|
|
27
|
+
ThreadPool::~ThreadPool()
|
|
28
|
+
{
|
|
29
|
+
// So threads know it's time to shut down
|
|
30
|
+
done = true;
|
|
31
|
+
|
|
32
|
+
// Wake up all the threads, so they can finish and be joined
|
|
33
|
+
workQueueConditionVariable.notify_all();
|
|
34
|
+
for (auto &thread : threads)
|
|
35
|
+
{
|
|
36
|
+
if (thread.joinable())
|
|
37
|
+
{
|
|
38
|
+
thread.join();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// This function will be called by the server every time there is a request
|
|
44
|
+
// that needs to be processed by the thread pool
|
|
45
|
+
void ThreadPool::queueWork(std::function<void(void)> task)
|
|
46
|
+
{
|
|
47
|
+
// Grab the mutex
|
|
48
|
+
std::lock_guard<std::mutex> g(workQueueMutex);
|
|
49
|
+
|
|
50
|
+
// Push the request to the queue
|
|
51
|
+
workQueue.push(task);
|
|
52
|
+
|
|
53
|
+
// Notify one thread that there are requests to process
|
|
54
|
+
workQueueConditionVariable.notify_one();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Function used by the threads to grab work from the queue
|
|
58
|
+
void ThreadPool::doWork()
|
|
59
|
+
{
|
|
60
|
+
// Loop while the queue is not destructing
|
|
61
|
+
while (!done)
|
|
62
|
+
{
|
|
63
|
+
std::function<void(void)> task;
|
|
64
|
+
|
|
65
|
+
// Create a scope, so we don't lock the queue for longer than necessary
|
|
66
|
+
{
|
|
67
|
+
std::unique_lock<std::mutex> g(workQueueMutex);
|
|
68
|
+
workQueueConditionVariable.wait(g, [&]
|
|
69
|
+
{
|
|
70
|
+
// Only wake up if there are elements in the queue or the program is
|
|
71
|
+
// shutting down
|
|
72
|
+
return !workQueue.empty() || done; });
|
|
73
|
+
|
|
74
|
+
// If we are shutting down exit witout trying to process more work
|
|
75
|
+
if (done)
|
|
76
|
+
{
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
task = workQueue.front();
|
|
81
|
+
workQueue.pop();
|
|
82
|
+
}
|
|
83
|
+
++busy;
|
|
84
|
+
task();
|
|
85
|
+
--busy;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
void ThreadPool::waitFinished() {
|
|
90
|
+
std::unique_lock<std::mutex> g(workQueueMutex);
|
|
91
|
+
workQueueConditionVariable.wait(g, [&]{ return workQueue.empty() && (busy == 0); });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
}
|
package/cpp/ThreadPool.h
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#ifndef ThreadPool_h
|
|
2
|
+
#define ThreadPool_h
|
|
3
|
+
|
|
4
|
+
#include <condition_variable>
|
|
5
|
+
#include <exception>
|
|
6
|
+
#include <mutex>
|
|
7
|
+
#include <queue>
|
|
8
|
+
#include <stdio.h>
|
|
9
|
+
#include <thread>
|
|
10
|
+
#include <vector>
|
|
11
|
+
|
|
12
|
+
namespace osp {
|
|
13
|
+
|
|
14
|
+
class ThreadPool {
|
|
15
|
+
public:
|
|
16
|
+
ThreadPool();
|
|
17
|
+
~ThreadPool();
|
|
18
|
+
void queueWork(std::function<void(void)> task);
|
|
19
|
+
void waitFinished();
|
|
20
|
+
|
|
21
|
+
private:
|
|
22
|
+
unsigned int busy;
|
|
23
|
+
// This condition variable is used for the threads to wait until there is work
|
|
24
|
+
// to do
|
|
25
|
+
std::condition_variable_any workQueueConditionVariable;
|
|
26
|
+
|
|
27
|
+
// We store the threads in a vector, so we can later stop them gracefully
|
|
28
|
+
std::vector<std::thread> threads;
|
|
29
|
+
|
|
30
|
+
// Mutex to protect workQueue
|
|
31
|
+
std::mutex workQueueMutex;
|
|
32
|
+
|
|
33
|
+
// Queue of requests waiting to be processed
|
|
34
|
+
std::queue<std::function<void(void)>> workQueue;
|
|
35
|
+
|
|
36
|
+
// This will be set to true when the thread pool is shutting down. This tells
|
|
37
|
+
// the threads to stop looping and finish
|
|
38
|
+
bool done;
|
|
39
|
+
|
|
40
|
+
// Function used by the threads to grab work from the queue
|
|
41
|
+
void doWork();
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#endif /* ThreadPool_h */
|
package/cpp/bindings.cpp
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
#include "bindings.h"
|
|
2
|
+
#include "bridge.h"
|
|
3
|
+
#include "logs.h"
|
|
4
|
+
#include "utils.h"
|
|
5
|
+
#include "ThreadPool.h"
|
|
6
|
+
#include "sqlbatchexecutor.h"
|
|
7
|
+
#include <vector>
|
|
8
|
+
#include <string>
|
|
9
|
+
#include "macros.h"
|
|
10
|
+
#include <iostream>
|
|
11
|
+
|
|
12
|
+
namespace osp {
|
|
13
|
+
|
|
14
|
+
namespace jsi = facebook::jsi;
|
|
15
|
+
|
|
16
|
+
std::string basePath;
|
|
17
|
+
std::shared_ptr<react::CallInvoker> invoker;
|
|
18
|
+
std::shared_ptr<ThreadPool> pool;
|
|
19
|
+
|
|
20
|
+
void clearState() {
|
|
21
|
+
sqliteCloseAll();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker, const char *docPath)
|
|
25
|
+
{
|
|
26
|
+
basePath = std::string(docPath);
|
|
27
|
+
pool = std::make_shared<ThreadPool>();
|
|
28
|
+
invoker = jsCallInvoker;
|
|
29
|
+
|
|
30
|
+
auto open = HOSTFN("open", 2) {
|
|
31
|
+
if (count == 0)
|
|
32
|
+
{
|
|
33
|
+
throw jsi::JSError(rt, "[op-sqlite][open] database name is required");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!args[0].isString())
|
|
37
|
+
{
|
|
38
|
+
throw jsi::JSError(rt, "[op-sqlite][open] database name must be a string");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
std::string dbName = args[0].asString(rt).utf8(rt);
|
|
42
|
+
std::string tempDocPath = std::string(basePath);
|
|
43
|
+
|
|
44
|
+
if (count > 1 && !args[1].isUndefined() && !args[1].isNull())
|
|
45
|
+
{
|
|
46
|
+
if (!args[1].isString())
|
|
47
|
+
{
|
|
48
|
+
throw jsi::JSError(rt, "[op-sqlite][open] database location must be a string");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
tempDocPath = tempDocPath + "/" + args[1].asString(rt).utf8(rt);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
BridgeResult result = sqliteOpenDb(dbName, tempDocPath);
|
|
55
|
+
|
|
56
|
+
if (result.type == SQLiteError)
|
|
57
|
+
{
|
|
58
|
+
throw jsi::JSError(rt, result.message.c_str());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {};
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
auto attach = HOSTFN("attach", 4) {
|
|
65
|
+
if(count < 3) {
|
|
66
|
+
throw jsi::JSError(rt, "[op-sqlite][attach] Incorrect number of arguments");
|
|
67
|
+
}
|
|
68
|
+
if (!args[0].isString() || !args[1].isString() || !args[2].isString())
|
|
69
|
+
{
|
|
70
|
+
throw jsi::JSError(rt, "dbName, databaseToAttach and alias must be a strings");
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
std::string tempDocPath = std::string(basePath);
|
|
75
|
+
if (count > 3 && !args[3].isUndefined() && !args[3].isNull())
|
|
76
|
+
{
|
|
77
|
+
if (!args[3].isString())
|
|
78
|
+
{
|
|
79
|
+
throw jsi::JSError(rt, "[op-sqlite][attach] database location must be a string");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
tempDocPath = tempDocPath + "/" + args[3].asString(rt).utf8(rt);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
std::string dbName = args[0].asString(rt).utf8(rt);
|
|
86
|
+
std::string databaseToAttach = args[1].asString(rt).utf8(rt);
|
|
87
|
+
std::string alias = args[2].asString(rt).utf8(rt);
|
|
88
|
+
BridgeResult result = sqliteAttachDb(dbName, tempDocPath, databaseToAttach, alias);
|
|
89
|
+
|
|
90
|
+
if (result.type == SQLiteError)
|
|
91
|
+
{
|
|
92
|
+
throw jsi::JSError(rt, result.message.c_str());
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {};
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
auto detach = HOSTFN("detach", 2) {
|
|
99
|
+
if(count < 2) {
|
|
100
|
+
throw jsi::JSError(rt, "[op-sqlite][detach] Incorrect number of arguments");
|
|
101
|
+
}
|
|
102
|
+
if (!args[0].isString() || !args[1].isString())
|
|
103
|
+
{
|
|
104
|
+
throw jsi::JSError(rt, "dbName, databaseToAttach and alias must be a strings");
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
std::string dbName = args[0].asString(rt).utf8(rt);
|
|
109
|
+
std::string alias = args[1].asString(rt).utf8(rt);
|
|
110
|
+
BridgeResult result = sqliteDetachDb(dbName, alias);
|
|
111
|
+
|
|
112
|
+
if (result.type == SQLiteError)
|
|
113
|
+
{
|
|
114
|
+
throw jsi::JSError(rt, result.message.c_str());
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {};
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
auto close = HOSTFN("close", 1)
|
|
121
|
+
{
|
|
122
|
+
if (count == 0)
|
|
123
|
+
{
|
|
124
|
+
throw jsi::JSError(rt, "[op-sqlite][close] database name is required");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!args[0].isString())
|
|
128
|
+
{
|
|
129
|
+
throw jsi::JSError(rt, "[op-sqlite][close] database name must be a string");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
std::string dbName = args[0].asString(rt).utf8(rt);
|
|
133
|
+
|
|
134
|
+
BridgeResult result = sqliteCloseDb(dbName);
|
|
135
|
+
|
|
136
|
+
if (result.type == SQLiteError)
|
|
137
|
+
{
|
|
138
|
+
throw jsi::JSError(rt, result.message.c_str());
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {};
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
auto remove = HOSTFN("delete", 2)
|
|
145
|
+
{
|
|
146
|
+
if (count == 0)
|
|
147
|
+
{
|
|
148
|
+
throw jsi::JSError(rt, "[op-sqlite][open] database name is required");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!args[0].isString())
|
|
152
|
+
{
|
|
153
|
+
throw jsi::JSError(rt, "[op-sqlite][open] database name must be a string");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
std::string dbName = args[0].asString(rt).utf8(rt);
|
|
157
|
+
|
|
158
|
+
std::string tempDocPath = std::string(basePath);
|
|
159
|
+
|
|
160
|
+
if (count > 1 && !args[1].isUndefined() && !args[1].isNull())
|
|
161
|
+
{
|
|
162
|
+
if (!args[1].isString())
|
|
163
|
+
{
|
|
164
|
+
throw jsi::JSError(rt, "[op-sqlite][open] database location must be a string");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
tempDocPath = tempDocPath + "/" + args[1].asString(rt).utf8(rt);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
BridgeResult result = sqliteRemoveDb(dbName, tempDocPath);
|
|
172
|
+
|
|
173
|
+
if (result.type == SQLiteError)
|
|
174
|
+
{
|
|
175
|
+
throw jsi::JSError(rt, result.message.c_str());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {};
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
auto execute = HOSTFN("execute", 3)
|
|
182
|
+
{
|
|
183
|
+
const std::string dbName = args[0].asString(rt).utf8(rt);
|
|
184
|
+
const std::string query = args[1].asString(rt).utf8(rt);
|
|
185
|
+
std::vector<std::any> params;
|
|
186
|
+
if(count == 3) {
|
|
187
|
+
const jsi::Value &originalParams = args[2];
|
|
188
|
+
params = toAnyVec(rt, originalParams);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
std::vector<std::shared_ptr<DynamicHostObject>> results;
|
|
192
|
+
std::vector<std::shared_ptr<DynamicHostObject>> metadata;
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
auto status = sqliteExecute(dbName, query, ¶ms, &results, &metadata);
|
|
196
|
+
|
|
197
|
+
if(status.type == SQLiteError) {
|
|
198
|
+
throw jsi::JSError(rt, status.message);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
auto jsiResult = createResult(rt, status, &results, &metadata);
|
|
202
|
+
return jsiResult;
|
|
203
|
+
} catch(std::exception &e) {
|
|
204
|
+
throw jsi::JSError(rt, e.what());
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
auto executeAsync = HOSTFN("executeAsync", 3)
|
|
209
|
+
{
|
|
210
|
+
if (count < 3)
|
|
211
|
+
{
|
|
212
|
+
throw jsi::JSError(rt, "[op-sqlite][executeAsync] Incorrect arguments for executeAsync");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const std::string dbName = args[0].asString(rt).utf8(rt);
|
|
216
|
+
const std::string query = args[1].asString(rt).utf8(rt);
|
|
217
|
+
const jsi::Value &originalParams = args[2];
|
|
218
|
+
|
|
219
|
+
std::vector<std::any> params = toAnyVec(rt, originalParams);
|
|
220
|
+
|
|
221
|
+
auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
|
|
222
|
+
auto promise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2) {
|
|
223
|
+
auto resolve = std::make_shared<jsi::Value>(rt, args[0]);
|
|
224
|
+
auto reject = std::make_shared<jsi::Value>(rt, args[1]);
|
|
225
|
+
|
|
226
|
+
auto task =
|
|
227
|
+
[&rt, dbName, query, params = std::make_shared<std::vector<std::any>>(params), resolve, reject]()
|
|
228
|
+
{
|
|
229
|
+
try
|
|
230
|
+
{
|
|
231
|
+
std::vector<std::shared_ptr<DynamicHostObject>> results;
|
|
232
|
+
std::vector<std::shared_ptr<DynamicHostObject>> metadata;
|
|
233
|
+
|
|
234
|
+
auto status = sqliteExecute(dbName, query, params.get(), &results, &metadata);
|
|
235
|
+
|
|
236
|
+
invoker->invokeAsync([&rt,
|
|
237
|
+
results = std::make_shared<std::vector<std::shared_ptr<DynamicHostObject>>>(results),
|
|
238
|
+
metadata = std::make_shared<std::vector<std::shared_ptr<DynamicHostObject>>>(metadata),
|
|
239
|
+
status = std::move(status),
|
|
240
|
+
resolve,
|
|
241
|
+
reject] {
|
|
242
|
+
if(status.type == SQLiteOk) {
|
|
243
|
+
auto jsiResult = createResult(rt, status, results.get(), metadata.get());
|
|
244
|
+
resolve->asObject(rt).asFunction(rt).call(rt, std::move(jsiResult));
|
|
245
|
+
} else {
|
|
246
|
+
auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
|
|
247
|
+
auto error = errorCtr.callAsConstructor(rt, jsi::String::createFromUtf8(rt, status.message));
|
|
248
|
+
reject->asObject(rt).asFunction(rt).call(rt, error);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
}
|
|
253
|
+
catch (std::exception &exc)
|
|
254
|
+
{
|
|
255
|
+
invoker->invokeAsync([&rt, &exc] {
|
|
256
|
+
jsi::JSError(rt, exc.what());
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
pool->queueWork(task);
|
|
262
|
+
|
|
263
|
+
return {};
|
|
264
|
+
}));
|
|
265
|
+
|
|
266
|
+
return promise;
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Execute a batch of SQL queries in a transaction
|
|
270
|
+
// Parameters can be: [[sql: string, arguments: any[] | arguments: any[][] ]]
|
|
271
|
+
auto executeBatch = HOSTFN("executeBatch", 2)
|
|
272
|
+
{
|
|
273
|
+
if (sizeof(args) < 2)
|
|
274
|
+
{
|
|
275
|
+
throw jsi::JSError(rt, "[op-sqlite][executeBatch] - Incorrect parameter count");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const jsi::Value ¶ms = args[1];
|
|
279
|
+
if (params.isNull() || params.isUndefined())
|
|
280
|
+
{
|
|
281
|
+
throw jsi::JSError(rt, "[op-sqlite][executeBatch] - An array of SQL commands or parameters is needed");
|
|
282
|
+
}
|
|
283
|
+
const std::string dbName = args[0].asString(rt).utf8(rt);
|
|
284
|
+
const jsi::Array &batchParams = params.asObject(rt).asArray(rt);
|
|
285
|
+
std::vector<BatchArguments> commands;
|
|
286
|
+
toBatchArguments(rt, batchParams, &commands);
|
|
287
|
+
|
|
288
|
+
auto batchResult = sqliteExecuteBatch(dbName, &commands);
|
|
289
|
+
if (batchResult.type == SQLiteOk)
|
|
290
|
+
{
|
|
291
|
+
auto res = jsi::Object(rt);
|
|
292
|
+
res.setProperty(rt, "rowsAffected", jsi::Value(batchResult.affectedRows));
|
|
293
|
+
return std::move(res);
|
|
294
|
+
}
|
|
295
|
+
else
|
|
296
|
+
{
|
|
297
|
+
throw jsi::JSError(rt, batchResult.message);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
auto executeBatchAsync = HOSTFN("executeBatchAsync", 2)
|
|
302
|
+
{
|
|
303
|
+
if (sizeof(args) < 2)
|
|
304
|
+
{
|
|
305
|
+
throw jsi::JSError(rt, "[op-sqlite][executeAsyncBatch] Incorrect parameter count");
|
|
306
|
+
return {};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const jsi::Value ¶ms = args[1];
|
|
310
|
+
|
|
311
|
+
if (params.isNull() || params.isUndefined())
|
|
312
|
+
{
|
|
313
|
+
throw jsi::JSError(rt, "[op-sqlite][executeAsyncBatch] - An array of SQL commands or parameters is needed");
|
|
314
|
+
return {};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const std::string dbName = args[0].asString(rt).utf8(rt);
|
|
318
|
+
const jsi::Array &batchParams = params.asObject(rt).asArray(rt);
|
|
319
|
+
|
|
320
|
+
std::vector<BatchArguments> commands;
|
|
321
|
+
toBatchArguments(rt, batchParams, &commands);
|
|
322
|
+
|
|
323
|
+
auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
|
|
324
|
+
auto promise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2) {
|
|
325
|
+
auto resolve = std::make_shared<jsi::Value>(rt, args[0]);
|
|
326
|
+
auto reject = std::make_shared<jsi::Value>(rt, args[1]);
|
|
327
|
+
|
|
328
|
+
auto task =
|
|
329
|
+
[&rt, dbName, commands = std::make_shared<std::vector<BatchArguments>>(commands), resolve, reject]()
|
|
330
|
+
{
|
|
331
|
+
try
|
|
332
|
+
{
|
|
333
|
+
auto batchResult = sqliteExecuteBatch(dbName, commands.get());
|
|
334
|
+
invoker->invokeAsync([&rt, batchResult = std::move(batchResult), resolve, reject]
|
|
335
|
+
{
|
|
336
|
+
if(batchResult.type == SQLiteOk)
|
|
337
|
+
{
|
|
338
|
+
auto res = jsi::Object(rt);
|
|
339
|
+
res.setProperty(rt, "rowsAffected", jsi::Value(batchResult.affectedRows));
|
|
340
|
+
resolve->asObject(rt).asFunction(rt).call(rt, std::move(res));
|
|
341
|
+
} else
|
|
342
|
+
{
|
|
343
|
+
throw jsi::JSError(rt, batchResult.message);
|
|
344
|
+
} });
|
|
345
|
+
}
|
|
346
|
+
catch (std::exception &exc)
|
|
347
|
+
{
|
|
348
|
+
invoker->invokeAsync([&rt, reject, &exc]
|
|
349
|
+
{
|
|
350
|
+
throw jsi::JSError(rt, exc.what());
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
pool->queueWork(task);
|
|
355
|
+
|
|
356
|
+
return {};
|
|
357
|
+
}));
|
|
358
|
+
|
|
359
|
+
return promise;
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
auto loadFile = HOSTFN("loadFile", 2)
|
|
363
|
+
{
|
|
364
|
+
if (sizeof(args) < 2)
|
|
365
|
+
{
|
|
366
|
+
throw jsi::JSError(rt, "[op-sqlite][loadFileAsync] Incorrect parameter count");
|
|
367
|
+
return {};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const std::string dbName = args[0].asString(rt).utf8(rt);
|
|
371
|
+
const std::string sqlFileName = args[1].asString(rt).utf8(rt);
|
|
372
|
+
|
|
373
|
+
auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
|
|
374
|
+
auto promise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2) {
|
|
375
|
+
auto resolve = std::make_shared<jsi::Value>(rt, args[0]);
|
|
376
|
+
auto reject = std::make_shared<jsi::Value>(rt, args[1]);
|
|
377
|
+
|
|
378
|
+
auto task =
|
|
379
|
+
[&rt, dbName, sqlFileName, resolve, reject]()
|
|
380
|
+
{
|
|
381
|
+
try
|
|
382
|
+
{
|
|
383
|
+
const auto importResult = importSQLFile(dbName, sqlFileName);
|
|
384
|
+
|
|
385
|
+
invoker->invokeAsync([&rt, result = std::move(importResult), resolve, reject]
|
|
386
|
+
{
|
|
387
|
+
if(result.type == SQLiteOk)
|
|
388
|
+
{
|
|
389
|
+
auto res = jsi::Object(rt);
|
|
390
|
+
res.setProperty(rt, "rowsAffected", jsi::Value(result.affectedRows));
|
|
391
|
+
res.setProperty(rt, "commands", jsi::Value(result.commands));
|
|
392
|
+
resolve->asObject(rt).asFunction(rt).call(rt, std::move(res));
|
|
393
|
+
} else {
|
|
394
|
+
throw jsi::JSError(rt, result.message);
|
|
395
|
+
} });
|
|
396
|
+
}
|
|
397
|
+
catch (std::exception &exc)
|
|
398
|
+
{
|
|
399
|
+
invoker->invokeAsync([&rt, err = exc.what(), reject]
|
|
400
|
+
{
|
|
401
|
+
throw jsi::JSError(rt, err);
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
pool->queueWork(task);
|
|
406
|
+
return {};
|
|
407
|
+
}));
|
|
408
|
+
|
|
409
|
+
return promise;
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
jsi::Object module = jsi::Object(rt);
|
|
415
|
+
|
|
416
|
+
module.setProperty(rt, "open", std::move(open));
|
|
417
|
+
module.setProperty(rt, "close", std::move(close));
|
|
418
|
+
module.setProperty(rt, "attach", std::move(attach));
|
|
419
|
+
module.setProperty(rt, "detach", std::move(detach));
|
|
420
|
+
module.setProperty(rt, "delete", std::move(remove));
|
|
421
|
+
module.setProperty(rt, "execute", std::move(execute));
|
|
422
|
+
module.setProperty(rt, "executeAsync", std::move(executeAsync));
|
|
423
|
+
module.setProperty(rt, "executeBatch", std::move(executeBatch));
|
|
424
|
+
module.setProperty(rt, "executeBatchAsync", std::move(executeBatchAsync));
|
|
425
|
+
module.setProperty(rt, "loadFile", std::move(loadFile));
|
|
426
|
+
|
|
427
|
+
rt.global().setProperty(rt, "__OPSQLiteProxy", std::move(module));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
}
|
package/cpp/bindings.h
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#include <jsi/jsilib.h>
|
|
2
|
+
#include <jsi/jsi.h>
|
|
3
|
+
#include <ReactCommon/CallInvoker.h>
|
|
4
|
+
|
|
5
|
+
using namespace facebook;
|
|
6
|
+
|
|
7
|
+
namespace osp {
|
|
8
|
+
|
|
9
|
+
void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker, const char *docPath);
|
|
10
|
+
void clearState();
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
|