@op-engineering/op-sqlite 11.2.14 → 11.3.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/cpp/DBHostObject.cpp +627 -591
- package/cpp/DBHostObject.h +54 -53
- package/cpp/DumbHostObject.cpp +35 -34
- package/cpp/DumbHostObject.h +12 -11
- package/cpp/PreparedStatementHostObject.cpp +102 -96
- package/cpp/PreparedStatementHostObject.h +28 -26
- package/cpp/SmartHostObject.cpp +13 -13
- package/cpp/SmartHostObject.h +6 -5
- package/cpp/ThreadPool.cpp +80 -78
- package/cpp/ThreadPool.h +28 -28
- package/cpp/bindings.cpp +112 -108
- package/cpp/bridge.cpp +637 -615
- package/cpp/bridge.h +4 -4
- package/cpp/libsql/bridge.cpp +566 -564
- package/cpp/libsql/bridge.h +16 -23
- package/cpp/libsql/libsql.h +62 -48
- package/cpp/logs.h +17 -17
- package/cpp/types.h +12 -12
- package/cpp/utils.cpp +254 -248
- package/lib/commonjs/Storage.js +60 -0
- package/lib/commonjs/Storage.js.map +1 -0
- package/lib/commonjs/index.js +9 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/Storage.js +53 -0
- package/lib/module/Storage.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/Storage.d.ts +23 -0
- package/lib/typescript/src/Storage.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Storage.ts +85 -0
- package/src/index.ts +4 -1
package/cpp/ThreadPool.cpp
CHANGED
|
@@ -3,116 +3,118 @@
|
|
|
3
3
|
namespace opsqlite {
|
|
4
4
|
|
|
5
5
|
ThreadPool::ThreadPool() : done(false) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// auto numberOfThreads = std::thread::hardware_concurrency();
|
|
10
|
-
// if (numberOfThreads == 0) {
|
|
11
|
-
// numberOfThreads = 1;
|
|
12
|
-
// }
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
// This returns the number of threads supported by the system. If the
|
|
7
|
+
// function can't figure out this information, it returns 0. 0 is not good,
|
|
8
|
+
// so we create at least 1
|
|
9
|
+
// auto numberOfThreads = std::thread::hardware_concurrency();
|
|
10
|
+
// if (numberOfThreads == 0) {
|
|
11
|
+
// numberOfThreads = 1;
|
|
12
|
+
// }
|
|
13
|
+
|
|
14
|
+
auto numberOfThreads = 1;
|
|
15
|
+
for (unsigned i = 0; i < numberOfThreads; ++i) {
|
|
16
|
+
// The threads will execute the private member `doWork`. Note that we
|
|
17
|
+
// need to pass a reference to the function (namespaced with the class
|
|
18
|
+
// name) as the first argument, and the current object as second
|
|
19
|
+
// argument
|
|
20
|
+
threads.emplace_back(&ThreadPool::doWork, this);
|
|
21
|
+
}
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
// The destructor joins all the threads so the program can exit gracefully.
|
|
24
25
|
// This will be executed if there is any exception (e.g. creating the threads)
|
|
25
26
|
ThreadPool::~ThreadPool() {
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
// So threads know it's time to shut down
|
|
28
|
+
done = true;
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
// Wake up all the threads, so they can finish and be joined
|
|
31
|
+
workQueueConditionVariable.notify_all();
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
for (auto &thread : threads) {
|
|
34
|
+
if (thread.joinable()) {
|
|
35
|
+
thread.join();
|
|
36
|
+
}
|
|
35
37
|
}
|
|
36
|
-
}
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
threads.clear();
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
// This function will be called by the server every time there is a request
|
|
42
43
|
// that needs to be processed by the thread pool
|
|
43
|
-
void ThreadPool::queueWork(const std::function<void(void)
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
void ThreadPool::queueWork(const std::function<void(void)> &task) {
|
|
45
|
+
// Grab the mutex
|
|
46
|
+
std::lock_guard<std::mutex> g(workQueueMutex);
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
// Push the request to the queue
|
|
49
|
+
workQueue.push(task);
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
// Notify one thread that there are requests to process
|
|
52
|
+
workQueueConditionVariable.notify_one();
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
// Function used by the threads to grab work from the queue
|
|
55
56
|
void ThreadPool::doWork() {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
57
|
+
// Loop while the queue is not destructing
|
|
58
|
+
while (!done) {
|
|
59
|
+
std::function<void(void)> task;
|
|
60
|
+
|
|
61
|
+
// Create a scope, so we don't lock the queue for longer than necessary
|
|
62
|
+
{
|
|
63
|
+
std::unique_lock<std::mutex> g(workQueueMutex);
|
|
64
|
+
workQueueConditionVariable.wait(g, [&] {
|
|
65
|
+
// Only wake up if there are elements in the queue or the
|
|
66
|
+
// program is shutting down
|
|
67
|
+
return !workQueue.empty() || done;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// If we are shutting down exit without trying to process more work
|
|
71
|
+
if (done) {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
task = workQueue.front();
|
|
76
|
+
workQueue.pop();
|
|
77
|
+
}
|
|
78
|
+
++busy;
|
|
79
|
+
task();
|
|
80
|
+
--busy;
|
|
76
81
|
}
|
|
77
|
-
++busy;
|
|
78
|
-
task();
|
|
79
|
-
--busy;
|
|
80
|
-
}
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
void ThreadPool::waitFinished() {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
std::unique_lock<std::mutex> g(workQueueMutex);
|
|
86
|
+
workQueueConditionVariable.wait(
|
|
87
|
+
g, [&] { return workQueue.empty() && (busy == 0); });
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
void ThreadPool::restartPool() {
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
// So threads know it's time to shut down
|
|
92
|
+
done = true;
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
// Wake up all the threads, so they can finish and be joined
|
|
95
|
+
workQueueConditionVariable.notify_all();
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
for (auto &thread : threads) {
|
|
98
|
+
if (thread.joinable()) {
|
|
99
|
+
thread.join();
|
|
100
|
+
}
|
|
99
101
|
}
|
|
100
|
-
}
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
threads.clear();
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
auto numberOfThreads = std::thread::hardware_concurrency();
|
|
106
|
+
if (numberOfThreads == 0) {
|
|
107
|
+
numberOfThreads = 1;
|
|
108
|
+
}
|
|
108
109
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
for (unsigned i = 0; i < numberOfThreads; ++i) {
|
|
111
|
+
// The threads will execute the private member `doWork`. Note that we
|
|
112
|
+
// need to pass a reference to the function (namespaced with the class
|
|
113
|
+
// name) as the first argument, and the current object as second
|
|
114
|
+
// argument
|
|
115
|
+
threads.emplace_back(&ThreadPool::doWork, this);
|
|
116
|
+
}
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
done = false;
|
|
117
119
|
}
|
|
118
120
|
} // namespace opsqlite
|
package/cpp/ThreadPool.h
CHANGED
|
@@ -11,34 +11,34 @@
|
|
|
11
11
|
namespace opsqlite {
|
|
12
12
|
|
|
13
13
|
class ThreadPool {
|
|
14
|
-
public:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
private:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
14
|
+
public:
|
|
15
|
+
ThreadPool();
|
|
16
|
+
~ThreadPool();
|
|
17
|
+
void queueWork(const std::function<void(void)> &task);
|
|
18
|
+
void waitFinished();
|
|
19
|
+
void restartPool();
|
|
20
|
+
|
|
21
|
+
private:
|
|
22
|
+
unsigned int busy{};
|
|
23
|
+
// This condition variable is used for the threads to wait until there is
|
|
24
|
+
// work 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
|
|
37
|
+
// tells 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
42
|
};
|
|
43
43
|
|
|
44
44
|
} // namespace opsqlite
|
package/cpp/bindings.cpp
CHANGED
|
@@ -27,143 +27,147 @@ std::vector<std::shared_ptr<DBHostObject>> dbs;
|
|
|
27
27
|
// React native will try to clean the module on JS context invalidation
|
|
28
28
|
// (CodePush/Hot Reload) The clearState function is called
|
|
29
29
|
void invalidate() {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
for (const auto &db : dbs) {
|
|
31
|
+
db->invalidate();
|
|
32
|
+
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
// Clear our existing vector of shared pointers so they can be garbage
|
|
35
|
+
// collected
|
|
36
|
+
dbs.clear();
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
void install(jsi::Runtime &rt,
|
|
40
40
|
const std::shared_ptr<react::CallInvoker> &invoker,
|
|
41
41
|
const char *base_path, const char *crsqlite_path,
|
|
42
42
|
const char *sqlite_vec_path) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
43
|
+
_base_path = std::string(base_path);
|
|
44
|
+
_crsqlite_path = std::string(crsqlite_path);
|
|
45
|
+
_sqlite_vec_path = std::string(sqlite_vec_path);
|
|
46
|
+
|
|
47
|
+
auto open = HOST_STATIC_FN("open") {
|
|
48
|
+
jsi::Object options = args[0].asObject(rt);
|
|
49
|
+
std::string name =
|
|
50
|
+
options.getProperty(rt, "name").asString(rt).utf8(rt);
|
|
51
|
+
std::string path = std::string(_base_path);
|
|
52
|
+
std::string location;
|
|
53
|
+
std::string encryption_key;
|
|
54
|
+
|
|
55
|
+
if (options.hasProperty(rt, "location")) {
|
|
56
|
+
location =
|
|
57
|
+
options.getProperty(rt, "location").asString(rt).utf8(rt);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (options.hasProperty(rt, "encryptionKey")) {
|
|
61
|
+
encryption_key =
|
|
62
|
+
options.getProperty(rt, "encryptionKey").asString(rt).utf8(rt);
|
|
63
|
+
}
|
|
62
64
|
|
|
63
65
|
#ifdef OP_SQLITE_USE_SQLCIPHER
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
if (encryption_key.empty()) {
|
|
67
|
+
log_to_console(rt, "Encryption key is missing for SQLCipher");
|
|
68
|
+
}
|
|
67
69
|
#endif
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
71
|
+
if (!location.empty()) {
|
|
72
|
+
if (location == ":memory:") {
|
|
73
|
+
path = ":memory:";
|
|
74
|
+
} else if (location.rfind('/', 0) == 0) {
|
|
75
|
+
path = location;
|
|
76
|
+
} else {
|
|
77
|
+
path = path + "/" + location;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
std::shared_ptr<DBHostObject> db = std::make_shared<DBHostObject>(
|
|
82
|
+
rt, path, invoker, name, path, _crsqlite_path, _sqlite_vec_path,
|
|
83
|
+
encryption_key);
|
|
84
|
+
dbs.emplace_back(db);
|
|
85
|
+
return jsi::Object::createFromHostObject(rt, db);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
auto is_sqlcipher = HOST_STATIC_FN("isSQLCipher") {
|
|
87
89
|
#ifdef OP_SQLITE_USE_SQLCIPHER
|
|
88
|
-
|
|
90
|
+
return true;
|
|
89
91
|
#else
|
|
90
|
-
|
|
92
|
+
return false;
|
|
91
93
|
#endif
|
|
92
|
-
|
|
94
|
+
});
|
|
93
95
|
|
|
94
|
-
|
|
96
|
+
auto is_ios_embedded = HOST_STATIC_FN("isIOSEmbedded") {
|
|
95
97
|
#ifdef OP_SQLITE_USE_PHONE_VERSION
|
|
96
|
-
|
|
98
|
+
return true;
|
|
97
99
|
#else
|
|
98
|
-
|
|
100
|
+
return false;
|
|
99
101
|
#endif
|
|
100
|
-
|
|
102
|
+
});
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
auto is_libsql = HOST_STATIC_FN("isLibsql") {
|
|
103
105
|
#ifdef OP_SQLITE_USE_LIBSQL
|
|
104
|
-
|
|
106
|
+
return true;
|
|
105
107
|
#else
|
|
106
|
-
|
|
108
|
+
return false;
|
|
107
109
|
#endif
|
|
108
|
-
|
|
110
|
+
});
|
|
109
111
|
|
|
110
112
|
#ifdef OP_SQLITE_USE_LIBSQL
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
113
|
+
auto open_remote = HOST_STATIC_FN("openRemote") {
|
|
114
|
+
jsi::Object options = args[0].asObject(rt);
|
|
115
|
+
std::string url = options.getProperty(rt, "url").asString(rt).utf8(rt);
|
|
116
|
+
std::string auth_token =
|
|
117
|
+
options.getProperty(rt, "authToken").asString(rt).utf8(rt);
|
|
118
|
+
|
|
119
|
+
std::shared_ptr<DBHostObject> db =
|
|
120
|
+
std::make_shared<DBHostObject>(rt, url, auth_token, invoker);
|
|
121
|
+
return jsi::Object::createFromHostObject(rt, db);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
auto open_sync = HOST_STATIC_FN("openSync") {
|
|
125
|
+
jsi::Object options = args[0].asObject(rt);
|
|
126
|
+
std::string name =
|
|
127
|
+
options.getProperty(rt, "name").asString(rt).utf8(rt);
|
|
128
|
+
std::string path = std::string(_base_path);
|
|
129
|
+
std::string url = options.getProperty(rt, "url").asString(rt).utf8(rt);
|
|
130
|
+
std::string auth_token =
|
|
131
|
+
options.getProperty(rt, "authToken").asString(rt).utf8(rt);
|
|
132
|
+
int sync_interval = 0;
|
|
133
|
+
if (options.hasProperty(rt, "syncInterval")) {
|
|
134
|
+
sync_interval = static_cast<int>(
|
|
135
|
+
options.getProperty(rt, "syncInterval").asNumber());
|
|
136
|
+
}
|
|
137
|
+
std::string location;
|
|
138
|
+
|
|
139
|
+
if (options.hasProperty(rt, "location")) {
|
|
140
|
+
location =
|
|
141
|
+
options.getProperty(rt, "location").asString(rt).utf8(rt);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!location.empty()) {
|
|
145
|
+
if (location == ":memory:") {
|
|
146
|
+
path = ":memory:";
|
|
147
|
+
} else if (location.rfind("/", 0) == 0) {
|
|
148
|
+
path = location;
|
|
149
|
+
} else {
|
|
150
|
+
path = path + "/" + location;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
std::shared_ptr<DBHostObject> db = std::make_shared<DBHostObject>(
|
|
155
|
+
rt, invoker, name, path, url, auth_token, sync_interval);
|
|
156
|
+
return jsi::Object::createFromHostObject(rt, db);
|
|
157
|
+
});
|
|
154
158
|
#endif
|
|
155
159
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
jsi::Object module = jsi::Object(rt);
|
|
161
|
+
module.setProperty(rt, "open", std::move(open));
|
|
162
|
+
module.setProperty(rt, "isSQLCipher", std::move(is_sqlcipher));
|
|
163
|
+
module.setProperty(rt, "isLibsql", std::move(is_libsql));
|
|
164
|
+
module.setProperty(rt, "isIOSEmbedded", std::move(is_ios_embedded));
|
|
161
165
|
#ifdef OP_SQLITE_USE_LIBSQL
|
|
162
|
-
|
|
163
|
-
|
|
166
|
+
module.setProperty(rt, "openRemote", std::move(open_remote));
|
|
167
|
+
module.setProperty(rt, "openSync", std::move(open_sync));
|
|
164
168
|
#endif
|
|
165
169
|
|
|
166
|
-
|
|
170
|
+
rt.global().setProperty(rt, "__OPSQLiteProxy", std::move(module));
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
} // namespace opsqlite
|