@op-engineering/op-sqlite 1.0.10 → 1.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/README.md +27 -20
- package/cpp/ThreadPool.cpp +116 -81
- package/cpp/ThreadPool.h +26 -25
- package/cpp/bindings.cpp +66 -9
- package/cpp/bridge.cpp +446 -386
- package/cpp/bridge.h +3 -1
- package/cpp/macros.h +0 -5
- package/lib/commonjs/index.js +2 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/index.d.ts +13 -0
- package/package.json +1 -1
- package/src/index.ts +20 -0
package/README.md
CHANGED
|
@@ -8,32 +8,17 @@
|
|
|
8
8
|
</div>
|
|
9
9
|
<br />
|
|
10
10
|
|
|
11
|
-
OP SQLite embeds the latest version of SQLite and provides a low-level
|
|
11
|
+
OP SQLite embeds the latest version of SQLite and provides a low-level API to execute SQL queries.
|
|
12
12
|
|
|
13
13
|
**Current SQLite version: 3.44.0**
|
|
14
14
|
|
|
15
|
-
Created by [@ospfranco](https://twitter.com/ospfranco).
|
|
16
|
-
|
|
17
|
-
**Please consider Sponsoring**, none of this work is for free. I pay for it with my time and knowledge. If you are a company in need of help with your React Native/React apps feel free to reach out. I also do a lot of C++ and nowadays Rust.
|
|
18
|
-
|
|
19
|
-
## Coming up
|
|
20
|
-
|
|
21
|
-
I will gladly review bug fixes, but in order for me to continue support and add new features, I ask you to sponsor me. Some of the things that can still be done to make this package faster and more complete:
|
|
22
|
-
|
|
23
|
-
- Prepared statements
|
|
24
|
-
- Inlining functions
|
|
25
|
-
- Batching queries
|
|
26
|
-
- Update hook
|
|
15
|
+
Created by [@ospfranco](https://twitter.com/ospfranco). **Please consider Sponsoring**, none of this work is for free. I pay for it with my time and knowledge. If you are a company in need of help with your React Native/React apps feel free to reach out. I also do a lot of C++ and nowadays Rust.
|
|
27
16
|
|
|
28
17
|
## Benchmarks
|
|
29
18
|
|
|
30
|
-
You can find the [benchmarking code in the example app](https://github.com/OP-Engineering/op-sqlite/blob/main/example/src/Database.ts#L44).
|
|
19
|
+
You can find the [benchmarking code in the example app](https://github.com/OP-Engineering/op-sqlite/blob/main/example/src/Database.ts#L44). You should expect anywhere between a 5x to a 8x improvement over non-JSI packages, and now a 5x to 8x improvement over quick-sqlite and expo-sqlite. Loading a 300k record database (in milliseconds).
|
|
31
20
|
|
|
32
|
-
|
|
33
|
-
| ------------ | ------------- | ---------- |
|
|
34
|
-
| quick-sqlite | 2719ms | 8851ms |
|
|
35
|
-
| expo-sqlite | 2293ms | 10626ms |
|
|
36
|
-
| op-sqlite | 507ms | 1125ms |
|
|
21
|
+

|
|
37
22
|
|
|
38
23
|
Memory consumption is also is also 1/4 compared to `react-native-quick-sqlite`. This query used to take 1.2gb of peak memory usage, now runs in 250mbs.
|
|
39
24
|
|
|
@@ -231,7 +216,7 @@ db.executeAsync(
|
|
|
231
216
|
|
|
232
217
|
### Blobs
|
|
233
218
|
|
|
234
|
-
Blobs are supported via `ArrayBuffer`, you need to be careful about the semantics though. You cannot
|
|
219
|
+
Blobs are supported via `ArrayBuffer`, you need to be careful about the semantics though. You cannot instantiate an instance of `ArrayBuffer` directly, nor pass a typed array directly. Here is an example:
|
|
235
220
|
|
|
236
221
|
```ts
|
|
237
222
|
db = open({
|
|
@@ -296,6 +281,28 @@ const { rowsAffected, commands } = db
|
|
|
296
281
|
});
|
|
297
282
|
```
|
|
298
283
|
|
|
284
|
+
## Update hook
|
|
285
|
+
|
|
286
|
+
You can subscribe to changes in your database by using an update hook:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
// Bear in mind: rowId is not your table primary key but the internal rowId sqlite uses
|
|
290
|
+
// to keep track of the table rows
|
|
291
|
+
db.updateHook(({ rowId, table, operation, row = {} }) => {
|
|
292
|
+
console.warn(`Hook has been called, rowId: ${rowId}, ${table}, ${operation}`);
|
|
293
|
+
// Will contain the entire row that changed
|
|
294
|
+
// only on UPDATE and INSERT operations
|
|
295
|
+
console.warn(JSON.stringify(row, null, 2));
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
db.execute('INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', [
|
|
299
|
+
id,
|
|
300
|
+
name,
|
|
301
|
+
age,
|
|
302
|
+
networth,
|
|
303
|
+
]);
|
|
304
|
+
```
|
|
305
|
+
|
|
299
306
|
## Use built-in SQLite
|
|
300
307
|
|
|
301
308
|
On iOS you can use the embedded SQLite, when running `pod-install` add an environment flag:
|
package/cpp/ThreadPool.cpp
CHANGED
|
@@ -2,94 +2,129 @@
|
|
|
2
2
|
|
|
3
3
|
namespace opsqlite {
|
|
4
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())
|
|
5
|
+
ThreadPool::ThreadPool() : done(false)
|
|
37
6
|
{
|
|
38
|
-
|
|
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
|
+
}
|
|
39
23
|
}
|
|
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
24
|
|
|
65
|
-
//
|
|
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()
|
|
66
28
|
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
+
|
|
35
|
+
for (auto &thread : threads)
|
|
36
|
+
{
|
|
37
|
+
if (thread.joinable())
|
|
38
|
+
{
|
|
39
|
+
thread.join();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
threads.clear();
|
|
44
|
+
}
|
|
79
45
|
|
|
80
|
-
|
|
81
|
-
|
|
46
|
+
// This function will be called by the server every time there is a request
|
|
47
|
+
// that needs to be processed by the thread pool
|
|
48
|
+
void ThreadPool::queueWork(std::function<void(void)> task)
|
|
49
|
+
{
|
|
50
|
+
// Grab the mutex
|
|
51
|
+
std::lock_guard<std::mutex> g(workQueueMutex);
|
|
52
|
+
|
|
53
|
+
// Push the request to the queue
|
|
54
|
+
workQueue.push(task);
|
|
55
|
+
|
|
56
|
+
// Notify one thread that there are requests to process
|
|
57
|
+
workQueueConditionVariable.notify_one();
|
|
82
58
|
}
|
|
83
|
-
++busy;
|
|
84
|
-
task();
|
|
85
|
-
--busy;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
59
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
60
|
+
// Function used by the threads to grab work from the queue
|
|
61
|
+
void ThreadPool::doWork()
|
|
62
|
+
{
|
|
63
|
+
// Loop while the queue is not destructing
|
|
64
|
+
while (!done)
|
|
65
|
+
{
|
|
66
|
+
std::function<void(void)> task;
|
|
67
|
+
|
|
68
|
+
// Create a scope, so we don't lock the queue for longer than necessary
|
|
69
|
+
{
|
|
70
|
+
std::unique_lock<std::mutex> g(workQueueMutex);
|
|
71
|
+
workQueueConditionVariable.wait(g, [&]
|
|
72
|
+
{
|
|
73
|
+
// Only wake up if there are elements in the queue or the program is
|
|
74
|
+
// shutting down
|
|
75
|
+
return !workQueue.empty() || done; });
|
|
76
|
+
|
|
77
|
+
// If we are shutting down exit witout trying to process more work
|
|
78
|
+
if (done)
|
|
79
|
+
{
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
task = workQueue.front();
|
|
84
|
+
workQueue.pop();
|
|
85
|
+
}
|
|
86
|
+
++busy;
|
|
87
|
+
task();
|
|
88
|
+
--busy;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
93
91
|
|
|
92
|
+
void ThreadPool::waitFinished() {
|
|
93
|
+
std::unique_lock<std::mutex> g(workQueueMutex);
|
|
94
|
+
workQueueConditionVariable.wait(g, [&]{ return workQueue.empty() && (busy == 0); });
|
|
95
|
+
}
|
|
94
96
|
|
|
97
|
+
void ThreadPool::restartPool() {
|
|
98
|
+
// So threads know it's time to shut down
|
|
99
|
+
done = true;
|
|
100
|
+
|
|
101
|
+
// Wake up all the threads, so they can finish and be joined
|
|
102
|
+
workQueueConditionVariable.notify_all();
|
|
103
|
+
|
|
104
|
+
for (auto &thread : threads)
|
|
105
|
+
{
|
|
106
|
+
if (thread.joinable())
|
|
107
|
+
{
|
|
108
|
+
thread.join();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
threads.clear();
|
|
113
|
+
|
|
114
|
+
auto numberOfThreads = std::thread::hardware_concurrency();
|
|
115
|
+
if (numberOfThreads == 0)
|
|
116
|
+
{
|
|
117
|
+
numberOfThreads = 1;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
for (unsigned i = 0; i < numberOfThreads; ++i)
|
|
121
|
+
{
|
|
122
|
+
// The threads will execute the private member `doWork`. Note that we need
|
|
123
|
+
// to pass a reference to the function (namespaced with the class name) as
|
|
124
|
+
// the first argument, and the current object as second argument
|
|
125
|
+
threads.push_back(std::thread(&ThreadPool::doWork, this));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
done = false;
|
|
129
|
+
}
|
|
95
130
|
}
|
package/cpp/ThreadPool.h
CHANGED
|
@@ -13,32 +13,33 @@ namespace opsqlite {
|
|
|
13
13
|
|
|
14
14
|
class ThreadPool {
|
|
15
15
|
public:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
ThreadPool();
|
|
17
|
+
~ThreadPool();
|
|
18
|
+
void queueWork(std::function<void(void)> task);
|
|
19
|
+
void waitFinished();
|
|
20
|
+
void restartPool();
|
|
21
|
+
|
|
21
22
|
private:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
23
|
+
unsigned int busy;
|
|
24
|
+
// This condition variable is used for the threads to wait until there is work
|
|
25
|
+
// to do
|
|
26
|
+
std::condition_variable_any workQueueConditionVariable;
|
|
27
|
+
|
|
28
|
+
// We store the threads in a vector, so we can later stop them gracefully
|
|
29
|
+
std::vector<std::thread> threads;
|
|
30
|
+
|
|
31
|
+
// Mutex to protect workQueue
|
|
32
|
+
std::mutex workQueueMutex;
|
|
33
|
+
|
|
34
|
+
// Queue of requests waiting to be processed
|
|
35
|
+
std::queue<std::function<void(void)>> workQueue;
|
|
36
|
+
|
|
37
|
+
// This will be set to true when the thread pool is shutting down. This tells
|
|
38
|
+
// the threads to stop looping and finish
|
|
39
|
+
bool done;
|
|
40
|
+
|
|
41
|
+
// Function used by the threads to grab work from the queue
|
|
42
|
+
void doWork();
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
}
|
package/cpp/bindings.cpp
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#include "macros.h"
|
|
10
10
|
#include <iostream>
|
|
11
11
|
#include "DumbHostObject.h"
|
|
12
|
+
#include <unordered_map>
|
|
12
13
|
|
|
13
14
|
namespace opsqlite {
|
|
14
15
|
|
|
@@ -16,16 +17,27 @@ namespace jsi = facebook::jsi;
|
|
|
16
17
|
|
|
17
18
|
std::string basePath;
|
|
18
19
|
std::shared_ptr<react::CallInvoker> invoker;
|
|
19
|
-
|
|
20
|
+
ThreadPool pool;
|
|
21
|
+
std::unordered_map<std::string, std::shared_ptr<jsi::Value>> updateHooks = std::unordered_map<std::string, std::shared_ptr<jsi::Value>>();
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
// React native will try to clean the module on JS context invalidation (CodePush/Hot Reload)
|
|
25
|
+
// The clearState function is called and we use this flag to prevent any ongoing
|
|
26
|
+
// operations from continuing work and can return early
|
|
27
|
+
bool invalidated = false;
|
|
20
28
|
|
|
21
29
|
void clearState() {
|
|
30
|
+
invalidated = true;
|
|
31
|
+
// Will terminate all operations and database connections
|
|
22
32
|
sqliteCloseAll();
|
|
33
|
+
// We then join all the threads before the context gets invalidated
|
|
34
|
+
pool.restartPool();
|
|
23
35
|
}
|
|
24
36
|
|
|
25
37
|
void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker, const char *docPath)
|
|
26
38
|
{
|
|
39
|
+
invalidated = false;
|
|
27
40
|
basePath = std::string(docPath);
|
|
28
|
-
pool = std::make_shared<ThreadPool>();
|
|
29
41
|
invoker = jsCallInvoker;
|
|
30
42
|
|
|
31
43
|
auto open = HOSTFN("open", 3) {
|
|
@@ -227,21 +239,25 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
|
|
|
227
239
|
const jsi::Value &originalParams = args[2];
|
|
228
240
|
|
|
229
241
|
std::vector<JSVariant> params = toVariantVec(rt, originalParams);
|
|
230
|
-
|
|
242
|
+
|
|
231
243
|
auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
|
|
232
244
|
auto promise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2) {
|
|
233
245
|
auto resolve = std::make_shared<jsi::Value>(rt, args[0]);
|
|
234
246
|
auto reject = std::make_shared<jsi::Value>(rt, args[1]);
|
|
235
247
|
|
|
236
248
|
auto task =
|
|
237
|
-
[&rt, dbName, query, params = std::
|
|
249
|
+
[&rt, dbName, query, params = std::move(params), resolve, reject]()
|
|
238
250
|
{
|
|
239
251
|
try
|
|
240
252
|
{
|
|
241
253
|
std::vector<DumbHostObject> results;
|
|
242
254
|
std::shared_ptr<std::vector<DynamicHostObject>> metadata = std::make_shared<std::vector<DynamicHostObject>>();;
|
|
243
255
|
|
|
244
|
-
auto status = sqliteExecute(dbName, query, params
|
|
256
|
+
auto status = sqliteExecute(dbName, query, ¶ms, &results, metadata);
|
|
257
|
+
|
|
258
|
+
if(invalidated) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
245
261
|
|
|
246
262
|
invoker->invokeAsync([&rt,
|
|
247
263
|
results = std::make_shared<std::vector<DumbHostObject>>(results),
|
|
@@ -270,7 +286,7 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
|
|
|
270
286
|
}
|
|
271
287
|
};
|
|
272
288
|
|
|
273
|
-
pool
|
|
289
|
+
pool.queueWork(task);
|
|
274
290
|
|
|
275
291
|
return {};
|
|
276
292
|
}));
|
|
@@ -363,7 +379,7 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
|
|
|
363
379
|
});
|
|
364
380
|
}
|
|
365
381
|
};
|
|
366
|
-
pool
|
|
382
|
+
pool.queueWork(task);
|
|
367
383
|
|
|
368
384
|
return {};
|
|
369
385
|
}));
|
|
@@ -414,14 +430,54 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
|
|
|
414
430
|
});
|
|
415
431
|
}
|
|
416
432
|
};
|
|
417
|
-
pool
|
|
433
|
+
pool.queueWork(task);
|
|
418
434
|
return {};
|
|
419
435
|
}));
|
|
420
436
|
|
|
421
437
|
return promise;
|
|
422
438
|
});
|
|
423
439
|
|
|
424
|
-
|
|
440
|
+
auto updateHook = HOSTFN("updateHook", 2)
|
|
441
|
+
{
|
|
442
|
+
if (sizeof(args) < 2)
|
|
443
|
+
{
|
|
444
|
+
throw jsi::JSError(rt, "[op-sqlite][loadFileAsync] Incorrect parameters: dbName and callback needed");
|
|
445
|
+
return {};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
auto dbName = args[0].asString(rt).utf8(rt);
|
|
449
|
+
auto callback = std::make_shared<jsi::Value>(rt, args[1]);
|
|
450
|
+
updateHooks[dbName] = callback;
|
|
451
|
+
|
|
452
|
+
auto hook = [&rt, callback](std::string dbName, std::string tableName, std::string operation, int rowId) {
|
|
453
|
+
std::vector<JSVariant> params;
|
|
454
|
+
std::vector<DumbHostObject> results;
|
|
455
|
+
std::shared_ptr<std::vector<DynamicHostObject>> metadata = std::make_shared<std::vector<DynamicHostObject>>();;
|
|
456
|
+
|
|
457
|
+
if(operation != "DELETE") {
|
|
458
|
+
std::string query = "SELECT * FROM " + tableName + " where rowid = " + std::to_string(rowId) + ";";
|
|
459
|
+
sqliteExecute(dbName, query, ¶ms, &results, metadata);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
invoker->invokeAsync([&rt, results = std::make_shared<std::vector<DumbHostObject>>(results), callback, tableName = std::move(tableName), operation = std::move(operation), &rowId]
|
|
463
|
+
{
|
|
464
|
+
auto res = jsi::Object(rt);
|
|
465
|
+
res.setProperty(rt, "table", jsi::String::createFromUtf8(rt, tableName));
|
|
466
|
+
res.setProperty(rt, "operation", jsi::String::createFromUtf8(rt, operation));
|
|
467
|
+
res.setProperty(rt, "rowId", jsi::Value(rowId));
|
|
468
|
+
if(results->size() != 0) {
|
|
469
|
+
res.setProperty(rt, "row", jsi::Object::createFromHostObject(rt, std::make_shared<DumbHostObject>(results->at(0))));
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
callback->asObject(rt).asFunction(rt).call(rt, res);
|
|
473
|
+
|
|
474
|
+
});
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
registerUpdateHook(dbName, std::move(hook));
|
|
478
|
+
|
|
479
|
+
return {};
|
|
480
|
+
});
|
|
425
481
|
|
|
426
482
|
jsi::Object module = jsi::Object(rt);
|
|
427
483
|
|
|
@@ -435,6 +491,7 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
|
|
|
435
491
|
module.setProperty(rt, "executeBatch", std::move(executeBatch));
|
|
436
492
|
module.setProperty(rt, "executeBatchAsync", std::move(executeBatchAsync));
|
|
437
493
|
module.setProperty(rt, "loadFile", std::move(loadFile));
|
|
494
|
+
module.setProperty(rt, "updateHook", std::move(updateHook));
|
|
438
495
|
|
|
439
496
|
rt.global().setProperty(rt, "__OPSQLiteProxy", std::move(module));
|
|
440
497
|
}
|