@op-engineering/op-sqlite 1.0.9 → 1.0.11
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/cpp/ThreadPool.cpp +116 -81
- package/cpp/ThreadPool.h +26 -25
- package/cpp/bindings.cpp +15 -8
- package/cpp/bridge.cpp +390 -389
- package/cpp/bridge.h +1 -1
- package/cpp/macros.h +0 -5
- package/package.json +1 -1
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
|
@@ -16,16 +16,19 @@ namespace jsi = facebook::jsi;
|
|
|
16
16
|
|
|
17
17
|
std::string basePath;
|
|
18
18
|
std::shared_ptr<react::CallInvoker> invoker;
|
|
19
|
-
|
|
19
|
+
ThreadPool pool;
|
|
20
|
+
bool invalidated = false;
|
|
20
21
|
|
|
21
22
|
void clearState() {
|
|
23
|
+
invalidated = true;
|
|
22
24
|
sqliteCloseAll();
|
|
25
|
+
pool.restartPool();
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker, const char *docPath)
|
|
26
29
|
{
|
|
30
|
+
invalidated = false;
|
|
27
31
|
basePath = std::string(docPath);
|
|
28
|
-
pool = std::make_shared<ThreadPool>();
|
|
29
32
|
invoker = jsCallInvoker;
|
|
30
33
|
|
|
31
34
|
auto open = HOSTFN("open", 3) {
|
|
@@ -227,21 +230,25 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
|
|
|
227
230
|
const jsi::Value &originalParams = args[2];
|
|
228
231
|
|
|
229
232
|
std::vector<JSVariant> params = toVariantVec(rt, originalParams);
|
|
230
|
-
|
|
233
|
+
|
|
231
234
|
auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
|
|
232
235
|
auto promise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2) {
|
|
233
236
|
auto resolve = std::make_shared<jsi::Value>(rt, args[0]);
|
|
234
237
|
auto reject = std::make_shared<jsi::Value>(rt, args[1]);
|
|
235
238
|
|
|
236
239
|
auto task =
|
|
237
|
-
[&rt, dbName, query, params = std::
|
|
240
|
+
[&rt, dbName, query, params = std::move(params), resolve, reject]()
|
|
238
241
|
{
|
|
239
242
|
try
|
|
240
243
|
{
|
|
241
244
|
std::vector<DumbHostObject> results;
|
|
242
245
|
std::shared_ptr<std::vector<DynamicHostObject>> metadata = std::make_shared<std::vector<DynamicHostObject>>();;
|
|
243
246
|
|
|
244
|
-
auto status = sqliteExecute(dbName, query, params
|
|
247
|
+
auto status = sqliteExecute(dbName, query, ¶ms, &results, metadata);
|
|
248
|
+
|
|
249
|
+
if(invalidated) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
245
252
|
|
|
246
253
|
invoker->invokeAsync([&rt,
|
|
247
254
|
results = std::make_shared<std::vector<DumbHostObject>>(results),
|
|
@@ -270,7 +277,7 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
|
|
|
270
277
|
}
|
|
271
278
|
};
|
|
272
279
|
|
|
273
|
-
pool
|
|
280
|
+
pool.queueWork(task);
|
|
274
281
|
|
|
275
282
|
return {};
|
|
276
283
|
}));
|
|
@@ -363,7 +370,7 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
|
|
|
363
370
|
});
|
|
364
371
|
}
|
|
365
372
|
};
|
|
366
|
-
pool
|
|
373
|
+
pool.queueWork(task);
|
|
367
374
|
|
|
368
375
|
return {};
|
|
369
376
|
}));
|
|
@@ -414,7 +421,7 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
|
|
|
414
421
|
});
|
|
415
422
|
}
|
|
416
423
|
};
|
|
417
|
-
pool
|
|
424
|
+
pool.queueWork(task);
|
|
418
425
|
return {};
|
|
419
426
|
}));
|
|
420
427
|
|
package/cpp/bridge.cpp
CHANGED
|
@@ -13,277 +13,427 @@
|
|
|
13
13
|
|
|
14
14
|
namespace opsqlite {
|
|
15
15
|
|
|
16
|
-
std::unordered_map<std::string, sqlite3 *> dbMap = std::unordered_map<std::string, sqlite3 *>();
|
|
16
|
+
std::unordered_map<std::string, sqlite3 *> dbMap = std::unordered_map<std::string, sqlite3 *>();
|
|
17
17
|
|
|
18
|
-
bool folder_exists(const std::string &foldername)
|
|
19
|
-
{
|
|
20
|
-
struct stat buffer;
|
|
21
|
-
return (stat(foldername.c_str(), &buffer) == 0);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Portable wrapper for mkdir. Internally used by mkdir()
|
|
26
|
-
* @param[in] path the full path of the directory to create.
|
|
27
|
-
* @return zero on success, otherwise -1.
|
|
28
|
-
*/
|
|
29
|
-
int _mkdir(const char *path)
|
|
30
|
-
{
|
|
31
|
-
#if _POSIX_C_SOURCE
|
|
32
|
-
return mkdir(path);
|
|
33
|
-
#else
|
|
34
|
-
return mkdir(path, 0755); // not sure if this works on mac
|
|
35
|
-
#endif
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Recursive, portable wrapper for mkdir.
|
|
40
|
-
* @param[in] path the full path of the directory to create.
|
|
41
|
-
* @return zero on success, otherwise -1.
|
|
42
|
-
*/
|
|
43
|
-
int mkdir(const char *path)
|
|
44
|
-
{
|
|
45
|
-
std::string current_level = "/";
|
|
46
|
-
std::string level;
|
|
47
|
-
std::stringstream ss(path);
|
|
48
|
-
// First line is empty because it starts with /User
|
|
49
|
-
getline(ss, level, '/');
|
|
50
|
-
// split path using slash as a separator
|
|
51
|
-
while (getline(ss, level, '/'))
|
|
18
|
+
bool folder_exists(const std::string &foldername)
|
|
52
19
|
{
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (!folder_exists(current_level) && _mkdir(current_level.c_str()) != 0)
|
|
56
|
-
return -1;
|
|
57
|
-
|
|
58
|
-
current_level += "/"; // don't forget to append a slash
|
|
20
|
+
struct stat buffer;
|
|
21
|
+
return (stat(foldername.c_str(), &buffer) == 0);
|
|
59
22
|
}
|
|
60
|
-
|
|
61
|
-
return 0;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
inline bool file_exists(const std::string &path)
|
|
65
|
-
{
|
|
66
|
-
struct stat buffer;
|
|
67
|
-
return (stat(path.c_str(), &buffer) == 0);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
std::string get_db_path(std::string const dbName, std::string const docPath)
|
|
71
|
-
{
|
|
72
|
-
mkdir(docPath.c_str());
|
|
73
|
-
return docPath + "/" + dbName;
|
|
74
|
-
}
|
|
75
23
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
sqlite3 *db;
|
|
83
|
-
int exit = 0;
|
|
84
|
-
exit = sqlite3_open_v2(dbPath.c_str(), &db, sqlOpenFlags, nullptr);
|
|
85
|
-
|
|
86
|
-
if (exit != SQLITE_OK)
|
|
24
|
+
/**
|
|
25
|
+
* Portable wrapper for mkdir. Internally used by mkdir()
|
|
26
|
+
* @param[in] path the full path of the directory to create.
|
|
27
|
+
* @return zero on success, otherwise -1.
|
|
28
|
+
*/
|
|
29
|
+
int _mkdir(const char *path)
|
|
87
30
|
{
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
31
|
+
#if _POSIX_C_SOURCE
|
|
32
|
+
return mkdir(path);
|
|
33
|
+
#else
|
|
34
|
+
return mkdir(path, 0755); // not sure if this works on mac
|
|
35
|
+
#endif
|
|
92
36
|
}
|
|
93
|
-
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Recursive, portable wrapper for mkdir.
|
|
40
|
+
* @param[in] path the full path of the directory to create.
|
|
41
|
+
* @return zero on success, otherwise -1.
|
|
42
|
+
*/
|
|
43
|
+
int mkdir(const char *path)
|
|
94
44
|
{
|
|
95
|
-
|
|
45
|
+
std::string current_level = "/";
|
|
46
|
+
std::string level;
|
|
47
|
+
std::stringstream ss(path);
|
|
48
|
+
// First line is empty because it starts with /User
|
|
49
|
+
getline(ss, level, '/');
|
|
50
|
+
// split path using slash as a separator
|
|
51
|
+
while (getline(ss, level, '/'))
|
|
52
|
+
{
|
|
53
|
+
current_level += level; // append folder to the current level
|
|
54
|
+
// create current level
|
|
55
|
+
if (!folder_exists(current_level) && _mkdir(current_level.c_str()) != 0)
|
|
56
|
+
return -1;
|
|
57
|
+
|
|
58
|
+
current_level += "/"; // don't forget to append a slash
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return 0;
|
|
96
62
|
}
|
|
97
|
-
|
|
98
|
-
return BridgeResult{
|
|
99
|
-
.type = SQLiteOk,
|
|
100
|
-
.affectedRows = 0
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
63
|
|
|
104
|
-
|
|
105
|
-
{
|
|
106
|
-
|
|
107
|
-
if (dbMap.count(dbName) == 0)
|
|
64
|
+
inline bool file_exists(const std::string &path)
|
|
108
65
|
{
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
.message = dbName + " is not open",
|
|
112
|
-
};
|
|
66
|
+
struct stat buffer;
|
|
67
|
+
return (stat(path.c_str(), &buffer) == 0);
|
|
113
68
|
}
|
|
114
|
-
|
|
115
|
-
sqlite3 *db = dbMap[dbName];
|
|
116
|
-
|
|
117
|
-
sqlite3_close_v2(db);
|
|
118
|
-
|
|
119
|
-
dbMap.erase(dbName);
|
|
120
|
-
|
|
121
|
-
return BridgeResult{
|
|
122
|
-
.type = SQLiteOk,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
69
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
sqlite3_close_v2(x.second);
|
|
70
|
+
std::string get_db_path(std::string const dbName, std::string const docPath)
|
|
71
|
+
{
|
|
72
|
+
mkdir(docPath.c_str());
|
|
73
|
+
return docPath + "/" + dbName;
|
|
131
74
|
}
|
|
132
|
-
dbMap.clear();
|
|
133
|
-
}
|
|
134
75
|
|
|
135
|
-
BridgeResult
|
|
136
|
-
{
|
|
137
|
-
/**
|
|
138
|
-
* There is no need to check if mainDBName is opened because sqliteExecuteLiteral will do that.
|
|
139
|
-
* */
|
|
140
|
-
std::string dbPath = get_db_path(databaseToAttach, docPath);
|
|
141
|
-
std::string statement = "ATTACH DATABASE '" + dbPath + "' AS " + alias;
|
|
142
|
-
|
|
143
|
-
BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
|
|
144
|
-
|
|
145
|
-
if (result.type == SQLiteError)
|
|
76
|
+
BridgeResult sqliteOpenDb(std::string const dbName, std::string const docPath, bool memoryStorage)
|
|
146
77
|
{
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
78
|
+
std::string dbPath = memoryStorage ? ":memory:" : get_db_path(dbName, docPath);
|
|
79
|
+
|
|
80
|
+
int sqlOpenFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
|
|
81
|
+
|
|
82
|
+
sqlite3 *db;
|
|
83
|
+
int exit = 0;
|
|
84
|
+
exit = sqlite3_open_v2(dbPath.c_str(), &db, sqlOpenFlags, nullptr);
|
|
85
|
+
|
|
86
|
+
if (exit != SQLITE_OK)
|
|
87
|
+
{
|
|
88
|
+
return {
|
|
89
|
+
.type = SQLiteError,
|
|
90
|
+
.message = sqlite3_errmsg(db)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
else
|
|
94
|
+
{
|
|
95
|
+
dbMap[dbName] = db;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return BridgeResult{
|
|
99
|
+
.type = SQLiteOk,
|
|
100
|
+
.affectedRows = 0
|
|
150
101
|
};
|
|
151
102
|
}
|
|
152
|
-
return {
|
|
153
|
-
.type = SQLiteOk,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
103
|
|
|
157
|
-
BridgeResult
|
|
158
|
-
{
|
|
159
|
-
/**
|
|
160
|
-
* There is no need to check if mainDBName is opened because sqliteExecuteLiteral will do that.
|
|
161
|
-
* */
|
|
162
|
-
std::string statement = "DETACH DATABASE " + alias;
|
|
163
|
-
BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
|
|
164
|
-
if (result.type == SQLiteError)
|
|
104
|
+
BridgeResult sqliteCloseDb(std::string const dbName)
|
|
165
105
|
{
|
|
106
|
+
|
|
107
|
+
if (dbMap.count(dbName) == 0)
|
|
108
|
+
{
|
|
109
|
+
return {
|
|
110
|
+
.type = SQLiteError,
|
|
111
|
+
.message = dbName + " is not open",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
sqlite3 *db = dbMap[dbName];
|
|
116
|
+
|
|
117
|
+
sqlite3_close_v2(db);
|
|
118
|
+
|
|
119
|
+
dbMap.erase(dbName);
|
|
120
|
+
|
|
166
121
|
return BridgeResult{
|
|
167
|
-
.type =
|
|
168
|
-
.message = mainDBName + "was unable to detach database: " + std::string(result.message),
|
|
122
|
+
.type = SQLiteOk,
|
|
169
123
|
};
|
|
170
124
|
}
|
|
171
|
-
return BridgeResult{
|
|
172
|
-
.type = SQLiteOk,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
125
|
|
|
176
|
-
BridgeResult
|
|
177
|
-
{
|
|
178
|
-
if (dbMap.count(dbName) == 1)
|
|
126
|
+
BridgeResult sqliteAttachDb(std::string const mainDBName, std::string const docPath, std::string const databaseToAttach, std::string const alias)
|
|
179
127
|
{
|
|
180
|
-
|
|
181
|
-
|
|
128
|
+
/**
|
|
129
|
+
* There is no need to check if mainDBName is opened because sqliteExecuteLiteral will do that.
|
|
130
|
+
* */
|
|
131
|
+
std::string dbPath = get_db_path(databaseToAttach, docPath);
|
|
132
|
+
std::string statement = "ATTACH DATABASE '" + dbPath + "' AS " + alias;
|
|
133
|
+
|
|
134
|
+
BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
|
|
135
|
+
|
|
136
|
+
if (result.type == SQLiteError)
|
|
182
137
|
{
|
|
183
|
-
return
|
|
138
|
+
return {
|
|
139
|
+
.type = SQLiteError,
|
|
140
|
+
.message = mainDBName + " was unable to attach another database: " + std::string(result.message),
|
|
141
|
+
};
|
|
184
142
|
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
std::string dbPath = get_db_path(dbName, docPath);
|
|
188
|
-
|
|
189
|
-
if (!file_exists(dbPath))
|
|
190
|
-
{
|
|
191
143
|
return {
|
|
192
|
-
.type =
|
|
193
|
-
.message = "[op-sqlite]: Database file not found" + dbPath
|
|
144
|
+
.type = SQLiteOk,
|
|
194
145
|
};
|
|
195
146
|
}
|
|
196
|
-
|
|
197
|
-
remove(dbPath.c_str());
|
|
198
|
-
|
|
199
|
-
return {
|
|
200
|
-
.type = SQLiteOk,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
147
|
|
|
204
|
-
|
|
205
|
-
{
|
|
206
|
-
size_t size = values->size();
|
|
207
|
-
|
|
208
|
-
for (int ii = 0; ii < size; ii++)
|
|
148
|
+
BridgeResult sqliteDetachDb(std::string const mainDBName, std::string const alias)
|
|
209
149
|
{
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
150
|
+
/**
|
|
151
|
+
* There is no need to check if mainDBName is opened because sqliteExecuteLiteral will do that.
|
|
152
|
+
* */
|
|
153
|
+
std::string statement = "DETACH DATABASE " + alias;
|
|
154
|
+
BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
|
|
155
|
+
if (result.type == SQLiteError)
|
|
214
156
|
{
|
|
215
|
-
|
|
157
|
+
return BridgeResult{
|
|
158
|
+
.type = SQLiteError,
|
|
159
|
+
.message = mainDBName + "was unable to detach database: " + std::string(result.message),
|
|
160
|
+
};
|
|
216
161
|
}
|
|
217
|
-
|
|
162
|
+
return BridgeResult{
|
|
163
|
+
.type = SQLiteOk,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
BridgeResult sqliteRemoveDb(std::string const dbName, std::string const docPath)
|
|
168
|
+
{
|
|
169
|
+
if (dbMap.count(dbName) == 1)
|
|
218
170
|
{
|
|
219
|
-
|
|
171
|
+
BridgeResult closeResult = sqliteCloseDb(dbName);
|
|
172
|
+
if (closeResult.type == SQLiteError)
|
|
173
|
+
{
|
|
174
|
+
return closeResult;
|
|
175
|
+
}
|
|
220
176
|
}
|
|
221
|
-
|
|
177
|
+
|
|
178
|
+
std::string dbPath = get_db_path(dbName, docPath);
|
|
179
|
+
|
|
180
|
+
if (!file_exists(dbPath))
|
|
222
181
|
{
|
|
223
|
-
|
|
182
|
+
return {
|
|
183
|
+
.type = SQLiteError,
|
|
184
|
+
.message = "[op-sqlite]: Database file not found" + dbPath
|
|
185
|
+
};
|
|
224
186
|
}
|
|
225
|
-
|
|
187
|
+
|
|
188
|
+
remove(dbPath.c_str());
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
.type = SQLiteOk,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
inline void bindStatement(sqlite3_stmt *statement, const std::vector<JSVariant> *values)
|
|
196
|
+
{
|
|
197
|
+
size_t size = values->size();
|
|
198
|
+
|
|
199
|
+
for (int ii = 0; ii < size; ii++)
|
|
226
200
|
{
|
|
227
|
-
|
|
201
|
+
int sqIndex = ii + 1;
|
|
202
|
+
JSVariant value = values->at(ii);
|
|
203
|
+
|
|
204
|
+
if (std::holds_alternative<bool>(value))
|
|
205
|
+
{
|
|
206
|
+
sqlite3_bind_int(statement, sqIndex, std::get<bool>(value));
|
|
207
|
+
}
|
|
208
|
+
else if (std::holds_alternative<int>(value))
|
|
209
|
+
{
|
|
210
|
+
sqlite3_bind_int(statement, sqIndex, std::get<int>(value));
|
|
211
|
+
}
|
|
212
|
+
else if (std::holds_alternative<long long>(value))
|
|
213
|
+
{
|
|
214
|
+
sqlite3_bind_double(statement, sqIndex, std::get<long long>(value));
|
|
215
|
+
}
|
|
216
|
+
else if (std::holds_alternative<double>(value))
|
|
217
|
+
{
|
|
218
|
+
sqlite3_bind_double(statement, sqIndex, std::get<double>(value));
|
|
219
|
+
}
|
|
220
|
+
else if (std::holds_alternative<std::string>(value))
|
|
221
|
+
{
|
|
222
|
+
std::string str = std::get<std::string>(value);
|
|
223
|
+
sqlite3_bind_text(statement, sqIndex, str.c_str(), str.length(), SQLITE_TRANSIENT);
|
|
224
|
+
}
|
|
225
|
+
else if(std::holds_alternative<ArrayBuffer>(value))
|
|
226
|
+
{
|
|
227
|
+
ArrayBuffer buffer = std::get<ArrayBuffer>(value);
|
|
228
|
+
sqlite3_bind_blob(statement, sqIndex, buffer.data.get(), buffer.size, SQLITE_STATIC);
|
|
229
|
+
} else {
|
|
230
|
+
sqlite3_bind_null(statement, sqIndex);
|
|
231
|
+
}
|
|
228
232
|
}
|
|
229
|
-
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
BridgeResult sqliteExecute(std::string const dbName,
|
|
236
|
+
std::string const &query,
|
|
237
|
+
const std::vector<JSVariant> *params,
|
|
238
|
+
std::vector<DumbHostObject> *results,
|
|
239
|
+
std::shared_ptr<std::vector<DynamicHostObject>> metadatas)
|
|
240
|
+
{
|
|
241
|
+
|
|
242
|
+
if (dbMap.find(dbName) == dbMap.end())
|
|
230
243
|
{
|
|
231
|
-
|
|
232
|
-
|
|
244
|
+
return {
|
|
245
|
+
.type = SQLiteError,
|
|
246
|
+
.message = "[op-sqlite]: Database " + dbName + " is not open"
|
|
247
|
+
};
|
|
233
248
|
}
|
|
234
|
-
|
|
249
|
+
|
|
250
|
+
sqlite3 *db = dbMap[dbName];
|
|
251
|
+
|
|
252
|
+
sqlite3_stmt *statement;
|
|
253
|
+
const char *remainingStatement = nullptr;
|
|
254
|
+
|
|
255
|
+
bool isConsuming = true;
|
|
256
|
+
bool isFailed = false;
|
|
257
|
+
|
|
258
|
+
int result = SQLITE_OK;
|
|
259
|
+
|
|
260
|
+
do {
|
|
261
|
+
const char *queryStr = remainingStatement == nullptr ? query.c_str() : remainingStatement;
|
|
262
|
+
|
|
263
|
+
int statementStatus = sqlite3_prepare_v2(db, queryStr, -1, &statement, &remainingStatement);
|
|
264
|
+
|
|
265
|
+
if (statementStatus == SQLITE_ERROR)
|
|
266
|
+
{
|
|
267
|
+
const char *message = sqlite3_errmsg(db);
|
|
268
|
+
return {
|
|
269
|
+
.type = SQLiteError,
|
|
270
|
+
.message = "[op-sqlite] SQL statement error: " + std::string(message),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
bindStatement(statement, params);
|
|
275
|
+
|
|
276
|
+
isConsuming = true;
|
|
277
|
+
|
|
278
|
+
int i, count, column_type;
|
|
279
|
+
std::string column_name, column_declared_type;
|
|
280
|
+
|
|
281
|
+
while (isConsuming)
|
|
282
|
+
{
|
|
283
|
+
result = sqlite3_step(statement);
|
|
284
|
+
|
|
285
|
+
switch (result)
|
|
286
|
+
{
|
|
287
|
+
case SQLITE_ROW: {
|
|
288
|
+
if(results == NULL)
|
|
289
|
+
{
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
i = 0;
|
|
294
|
+
DumbHostObject row = DumbHostObject(metadatas);
|
|
295
|
+
|
|
296
|
+
count = sqlite3_column_count(statement);
|
|
297
|
+
|
|
298
|
+
while (i < count)
|
|
299
|
+
{
|
|
300
|
+
column_type = sqlite3_column_type(statement, i);
|
|
301
|
+
|
|
302
|
+
switch (column_type)
|
|
303
|
+
{
|
|
304
|
+
case SQLITE_INTEGER:
|
|
305
|
+
{
|
|
306
|
+
/**
|
|
307
|
+
* Warning this will loose precision because JS can
|
|
308
|
+
* only represent Integers up to 53 bits
|
|
309
|
+
*/
|
|
310
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
311
|
+
row.values.push_back(JSVariant(column_value));
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
case SQLITE_FLOAT:
|
|
316
|
+
{
|
|
317
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
318
|
+
row.values.push_back(JSVariant(column_value));
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
case SQLITE_TEXT:
|
|
323
|
+
{
|
|
324
|
+
const char *column_value = reinterpret_cast<const char *>(sqlite3_column_text(statement, i));
|
|
325
|
+
int byteLen = sqlite3_column_bytes(statement, i);
|
|
326
|
+
// Specify length too; in case string contains NULL in the middle
|
|
327
|
+
row.values.push_back(JSVariant(std::string(column_value, byteLen)));
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
case SQLITE_BLOB:
|
|
332
|
+
{
|
|
333
|
+
int blob_size = sqlite3_column_bytes(statement, i);
|
|
334
|
+
const void *blob = sqlite3_column_blob(statement, i);
|
|
335
|
+
uint8_t *data = new uint8_t[blob_size];
|
|
336
|
+
// You cannot share raw memory between native and JS
|
|
337
|
+
// always copy the data
|
|
338
|
+
memcpy(data, blob, blob_size);
|
|
339
|
+
row.values.push_back(JSVariant(ArrayBuffer {
|
|
340
|
+
.data = std::shared_ptr<uint8_t>{data},
|
|
341
|
+
.size = static_cast<size_t>(blob_size)
|
|
342
|
+
}));
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
case SQLITE_NULL:
|
|
347
|
+
// Intentionally left blank
|
|
348
|
+
|
|
349
|
+
default:
|
|
350
|
+
row.values.push_back(JSVariant(NULL));
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
i++;
|
|
354
|
+
}
|
|
355
|
+
results->push_back(row);
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
case SQLITE_DONE:
|
|
360
|
+
i = 0;
|
|
361
|
+
count = sqlite3_column_count(statement);
|
|
362
|
+
|
|
363
|
+
while (i < count)
|
|
364
|
+
{
|
|
365
|
+
column_name = sqlite3_column_name(statement, i);
|
|
366
|
+
const char *type = sqlite3_column_decltype(statement, i);
|
|
367
|
+
auto metadata = DynamicHostObject();
|
|
368
|
+
metadata.fields.push_back(std::make_pair("name", column_name));
|
|
369
|
+
metadata.fields.push_back(std::make_pair("index", i));
|
|
370
|
+
metadata.fields.push_back(std::make_pair("type", type));
|
|
371
|
+
|
|
372
|
+
metadatas->push_back(metadata);
|
|
373
|
+
i++;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
isConsuming = false;
|
|
377
|
+
break;
|
|
378
|
+
|
|
379
|
+
default:
|
|
380
|
+
isFailed = true;
|
|
381
|
+
isConsuming = false;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
sqlite3_finalize(statement);
|
|
386
|
+
} while (remainingStatement != NULL && strcmp(remainingStatement, "") != 0 && !isFailed);
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
if (isFailed)
|
|
235
390
|
{
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
391
|
+
const char *message = sqlite3_errmsg(db);
|
|
392
|
+
return {
|
|
393
|
+
.type = SQLiteError,
|
|
394
|
+
.message = "[op-sqlite] SQLite code: " + std::to_string(result) + " execution error: " + std::string(message)
|
|
395
|
+
};
|
|
240
396
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
std::string const &query,
|
|
246
|
-
std::vector<JSVariant> *params,
|
|
247
|
-
std::vector<DumbHostObject> *results,
|
|
248
|
-
std::shared_ptr<std::vector<DynamicHostObject>> metadatas)
|
|
249
|
-
{
|
|
250
|
-
|
|
251
|
-
if (dbMap.find(dbName) == dbMap.end())
|
|
252
|
-
{
|
|
397
|
+
|
|
398
|
+
int changedRowCount = sqlite3_changes(db);
|
|
399
|
+
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
400
|
+
|
|
253
401
|
return {
|
|
254
|
-
.type =
|
|
255
|
-
.
|
|
402
|
+
.type = SQLiteOk,
|
|
403
|
+
.affectedRows = changedRowCount,
|
|
404
|
+
.insertId = static_cast<double>(latestInsertRowId)
|
|
256
405
|
};
|
|
257
406
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
407
|
+
|
|
408
|
+
BridgeResult sqliteExecuteLiteral(std::string const dbName, std::string const &query)
|
|
409
|
+
{
|
|
410
|
+
if (dbMap.count(dbName) == 0)
|
|
411
|
+
{
|
|
412
|
+
return {
|
|
413
|
+
SQLiteError,
|
|
414
|
+
"[op-sqlite] Database not opened: " + dbName
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
sqlite3 *db = dbMap[dbName];
|
|
419
|
+
sqlite3_stmt *statement;
|
|
269
420
|
|
|
270
|
-
int statementStatus = sqlite3_prepare_v2(db,
|
|
421
|
+
int statementStatus = sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
|
|
271
422
|
|
|
272
|
-
if (statementStatus
|
|
423
|
+
if (statementStatus != SQLITE_OK)
|
|
273
424
|
{
|
|
274
425
|
const char *message = sqlite3_errmsg(db);
|
|
275
426
|
return {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
427
|
+
SQLiteError,
|
|
428
|
+
"[op-sqlite] SQL statement error: " + std::string(message),
|
|
429
|
+
0};
|
|
279
430
|
}
|
|
280
431
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
isConsuming = true;
|
|
432
|
+
bool isConsuming = true;
|
|
433
|
+
bool isFailed = false;
|
|
284
434
|
|
|
285
|
-
int result
|
|
286
|
-
std::string column_name
|
|
435
|
+
int result;
|
|
436
|
+
std::string column_name;
|
|
287
437
|
|
|
288
438
|
while (isConsuming)
|
|
289
439
|
{
|
|
@@ -291,95 +441,11 @@ BridgeResult sqliteExecute(std::string const dbName,
|
|
|
291
441
|
|
|
292
442
|
switch (result)
|
|
293
443
|
{
|
|
294
|
-
case SQLITE_ROW:
|
|
295
|
-
|
|
296
|
-
{
|
|
297
|
-
break;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
i = 0;
|
|
301
|
-
DumbHostObject row = DumbHostObject(metadatas);
|
|
302
|
-
|
|
303
|
-
count = sqlite3_column_count(statement);
|
|
304
|
-
|
|
305
|
-
while (i < count)
|
|
306
|
-
{
|
|
307
|
-
column_type = sqlite3_column_type(statement, i);
|
|
308
|
-
|
|
309
|
-
switch (column_type)
|
|
310
|
-
{
|
|
311
|
-
case SQLITE_INTEGER:
|
|
312
|
-
{
|
|
313
|
-
/**
|
|
314
|
-
* Warning this will loose precision because JS can
|
|
315
|
-
* only represent Integers up to 53 bits
|
|
316
|
-
*/
|
|
317
|
-
double column_value = sqlite3_column_double(statement, i);
|
|
318
|
-
row.values.push_back(JSVariant(column_value));
|
|
319
|
-
break;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
case SQLITE_FLOAT:
|
|
323
|
-
{
|
|
324
|
-
double column_value = sqlite3_column_double(statement, i);
|
|
325
|
-
row.values.push_back(JSVariant(column_value));
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
case SQLITE_TEXT:
|
|
330
|
-
{
|
|
331
|
-
const char *column_value = reinterpret_cast<const char *>(sqlite3_column_text(statement, i));
|
|
332
|
-
int byteLen = sqlite3_column_bytes(statement, i);
|
|
333
|
-
// Specify length too; in case string contains NULL in the middle
|
|
334
|
-
row.values.push_back(JSVariant(std::string(column_value, byteLen)));
|
|
335
|
-
break;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
case SQLITE_BLOB:
|
|
339
|
-
{
|
|
340
|
-
int blob_size = sqlite3_column_bytes(statement, i);
|
|
341
|
-
const void *blob = sqlite3_column_blob(statement, i);
|
|
342
|
-
uint8_t *data = new uint8_t[blob_size];
|
|
343
|
-
// You cannot share raw memory between native and JS
|
|
344
|
-
// always copy the data
|
|
345
|
-
memcpy(data, blob, blob_size);
|
|
346
|
-
row.values.push_back(JSVariant(ArrayBuffer {
|
|
347
|
-
.data = std::shared_ptr<uint8_t>{data},
|
|
348
|
-
.size = static_cast<size_t>(blob_size)
|
|
349
|
-
}));
|
|
350
|
-
break;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
case SQLITE_NULL:
|
|
354
|
-
// Intentionally left blank
|
|
355
|
-
|
|
356
|
-
default:
|
|
357
|
-
row.values.push_back(JSVariant(NULL));
|
|
358
|
-
break;
|
|
359
|
-
}
|
|
360
|
-
i++;
|
|
361
|
-
}
|
|
362
|
-
results->push_back(row);
|
|
444
|
+
case SQLITE_ROW:
|
|
445
|
+
isConsuming = true;
|
|
363
446
|
break;
|
|
364
|
-
}
|
|
365
447
|
|
|
366
448
|
case SQLITE_DONE:
|
|
367
|
-
i = 0;
|
|
368
|
-
count = sqlite3_column_count(statement);
|
|
369
|
-
|
|
370
|
-
while (i < count)
|
|
371
|
-
{
|
|
372
|
-
column_name = sqlite3_column_name(statement, i);
|
|
373
|
-
const char *type = sqlite3_column_decltype(statement, i);
|
|
374
|
-
auto metadata = DynamicHostObject();
|
|
375
|
-
metadata.fields.push_back(std::make_pair("name", column_name));
|
|
376
|
-
metadata.fields.push_back(std::make_pair("index", i));
|
|
377
|
-
metadata.fields.push_back(std::make_pair("type", type));
|
|
378
|
-
|
|
379
|
-
metadatas->push_back(metadata);
|
|
380
|
-
i++;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
449
|
isConsuming = false;
|
|
384
450
|
break;
|
|
385
451
|
|
|
@@ -390,96 +456,31 @@ BridgeResult sqliteExecute(std::string const dbName,
|
|
|
390
456
|
}
|
|
391
457
|
|
|
392
458
|
sqlite3_finalize(statement);
|
|
393
|
-
} while (strcmp(remainingStatement, "") != 0 && !isFailed);
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
if (isFailed)
|
|
397
|
-
{
|
|
398
|
-
const char *message = sqlite3_errmsg(db);
|
|
399
|
-
return {
|
|
400
|
-
.type = SQLiteError,
|
|
401
|
-
.message = "[op-sqlite] SQL execution error: " + std::string(message)
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
int changedRowCount = sqlite3_changes(db);
|
|
406
|
-
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
407
|
-
|
|
408
|
-
return {
|
|
409
|
-
.type = SQLiteOk,
|
|
410
|
-
.affectedRows = changedRowCount,
|
|
411
|
-
.insertId = static_cast<double>(latestInsertRowId)
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
BridgeResult sqliteExecuteLiteral(std::string const dbName, std::string const &query)
|
|
416
|
-
{
|
|
417
|
-
// Check if db connection is opened
|
|
418
|
-
if (dbMap.count(dbName) == 0)
|
|
419
|
-
{
|
|
420
|
-
return {
|
|
421
|
-
SQLiteError,
|
|
422
|
-
"[op-sqlite] Database not opened: " + dbName
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
sqlite3 *db = dbMap[dbName];
|
|
427
|
-
sqlite3_stmt *statement;
|
|
428
|
-
|
|
429
|
-
// Compile and move result into statement memory spot
|
|
430
|
-
int statementStatus = sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
|
|
431
|
-
|
|
432
|
-
if (statementStatus != SQLITE_OK)
|
|
433
|
-
{
|
|
434
|
-
const char *message = sqlite3_errmsg(db);
|
|
435
|
-
return {
|
|
436
|
-
SQLiteError,
|
|
437
|
-
"[op-sqlite] SQL execution error: " + std::string(message),
|
|
438
|
-
0};
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
bool isConsuming = true;
|
|
442
|
-
bool isFailed = false;
|
|
443
|
-
|
|
444
|
-
int result;
|
|
445
|
-
std::string column_name;
|
|
446
|
-
|
|
447
|
-
while (isConsuming)
|
|
448
|
-
{
|
|
449
|
-
result = sqlite3_step(statement);
|
|
450
459
|
|
|
451
|
-
|
|
460
|
+
if (isFailed)
|
|
452
461
|
{
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
isConsuming = false;
|
|
459
|
-
break;
|
|
460
|
-
|
|
461
|
-
default:
|
|
462
|
-
isFailed = true;
|
|
463
|
-
isConsuming = false;
|
|
462
|
+
const char *message = sqlite3_errmsg(db);
|
|
463
|
+
return {
|
|
464
|
+
SQLiteError,
|
|
465
|
+
"[op-sqlite] SQL execution error: " + std::string(message),
|
|
466
|
+
0};
|
|
464
467
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
sqlite3_finalize(statement);
|
|
468
|
-
|
|
469
|
-
if (isFailed)
|
|
470
|
-
{
|
|
471
|
-
const char *message = sqlite3_errmsg(db);
|
|
468
|
+
|
|
469
|
+
int changedRowCount = sqlite3_changes(db);
|
|
472
470
|
return {
|
|
473
|
-
|
|
474
|
-
"
|
|
475
|
-
|
|
471
|
+
SQLiteOk,
|
|
472
|
+
"",
|
|
473
|
+
changedRowCount};
|
|
476
474
|
}
|
|
477
|
-
|
|
478
|
-
int changedRowCount = sqlite3_changes(db);
|
|
479
|
-
return {
|
|
480
|
-
SQLiteOk,
|
|
481
|
-
"",
|
|
482
|
-
changedRowCount};
|
|
483
|
-
}
|
|
484
475
|
|
|
476
|
+
void sqliteCloseAll() {
|
|
477
|
+
for (auto const& x : dbMap) {
|
|
478
|
+
sqlite3_interrupt(x.second);
|
|
479
|
+
// In certain cases, this will return SQLITE_OK, mark the database connection as an unusable "zombie",
|
|
480
|
+
// and deallocate the connection later.
|
|
481
|
+
sqlite3_close_v2(x.second);
|
|
482
|
+
}
|
|
483
|
+
dbMap.clear();
|
|
484
|
+
}
|
|
485
|
+
|
|
485
486
|
}
|
package/cpp/bridge.h
CHANGED
|
@@ -23,7 +23,7 @@ BridgeResult sqliteDetachDb(std::string const mainDBName, std::string const alia
|
|
|
23
23
|
|
|
24
24
|
BridgeResult sqliteExecute(std::string const dbName,
|
|
25
25
|
std::string const &query,
|
|
26
|
-
std::vector<JSVariant> *
|
|
26
|
+
const std::vector<JSVariant> *params,
|
|
27
27
|
std::vector<DumbHostObject> *results,
|
|
28
28
|
std::shared_ptr<std::vector<DynamicHostObject>> metadatas);
|
|
29
29
|
|
package/cpp/macros.h
CHANGED
|
@@ -8,9 +8,4 @@ jsi::PropNameID::forAscii(rt, name), \
|
|
|
8
8
|
basecount, \
|
|
9
9
|
[=](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value
|
|
10
10
|
|
|
11
|
-
#define JSIFN(capture) \
|
|
12
|
-
capture(jsi::Runtime &runtime, const jsi::Value &thisValue, \
|
|
13
|
-
const jsi::Value *arguments, size_t count) \
|
|
14
|
-
->jsi::Value
|
|
15
|
-
|
|
16
11
|
#endif /* macros_h */
|