@op-engineering/op-sqlite 1.0.10 → 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.
@@ -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
- thread.join();
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
- // Create a scope, so we don't lock the queue for longer than necessary
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
- 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
- }
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
- task = workQueue.front();
81
- workQueue.pop();
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
- void ThreadPool::waitFinished() {
90
- std::unique_lock<std::mutex> g(workQueueMutex);
91
- workQueueConditionVariable.wait(g, [&]{ return workQueue.empty() && (busy == 0); });
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
- ThreadPool();
17
- ~ThreadPool();
18
- void queueWork(std::function<void(void)> task);
19
- void waitFinished();
20
-
16
+ ThreadPool();
17
+ ~ThreadPool();
18
+ void queueWork(std::function<void(void)> task);
19
+ void waitFinished();
20
+ void restartPool();
21
+
21
22
  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();
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
- std::shared_ptr<ThreadPool> pool;
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::make_shared<std::vector<JSVariant>>(params), resolve, reject]()
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.get(), &results, metadata);
247
+ auto status = sqliteExecute(dbName, query, &params, &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->queueWork(task);
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->queueWork(task);
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->queueWork(task);
424
+ pool.queueWork(task);
418
425
  return {};
419
426
  }));
420
427
 
package/cpp/bridge.cpp CHANGED
@@ -13,279 +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
- 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
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
- BridgeResult sqliteOpenDb(std::string const dbName, std::string const docPath, bool memoryStorage)
77
- {
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)
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
- return {
89
- .type = SQLiteError,
90
- .message = sqlite3_errmsg(db)
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
- else
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
- dbMap[dbName] = db;
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
- BridgeResult sqliteCloseDb(std::string const dbName)
105
- {
106
-
107
- if (dbMap.count(dbName) == 0)
64
+ inline bool file_exists(const std::string &path)
108
65
  {
109
- return {
110
- .type = SQLiteError,
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
- void sqliteCloseAll() {
127
- for (auto const& x : dbMap) {
128
- // In certain cases, this will return SQLITE_OK, mark the database connection as an unusable "zombie",
129
- // and deallocate the connection later.
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 sqliteAttachDb(std::string const mainDBName, std::string const docPath, std::string const databaseToAttach, std::string const alias)
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
- return {
148
- .type = SQLiteError,
149
- .message = mainDBName + " was unable to attach another database: " + std::string(result.message),
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 sqliteDetachDb(std::string const mainDBName, std::string const alias)
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 = SQLiteError,
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 sqliteRemoveDb(std::string const dbName, std::string const docPath)
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
- BridgeResult closeResult = sqliteCloseDb(dbName);
181
- if (closeResult.type == SQLiteError)
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 closeResult;
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 = SQLiteError,
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
- inline void bindStatement(sqlite3_stmt *statement, std::vector<JSVariant> *values)
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
- int sqIndex = ii + 1;
211
- JSVariant value = values->at(ii);
212
-
213
- if (std::holds_alternative<bool>(value))
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
- sqlite3_bind_int(statement, sqIndex, std::get<bool>(value));
157
+ return BridgeResult{
158
+ .type = SQLiteError,
159
+ .message = mainDBName + "was unable to detach database: " + std::string(result.message),
160
+ };
216
161
  }
217
- else if (std::holds_alternative<int>(value))
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
- sqlite3_bind_int(statement, sqIndex, std::get<int>(value));
171
+ BridgeResult closeResult = sqliteCloseDb(dbName);
172
+ if (closeResult.type == SQLiteError)
173
+ {
174
+ return closeResult;
175
+ }
220
176
  }
221
- else if (std::holds_alternative<long long>(value))
177
+
178
+ std::string dbPath = get_db_path(dbName, docPath);
179
+
180
+ if (!file_exists(dbPath))
222
181
  {
223
- sqlite3_bind_double(statement, sqIndex, std::get<long long>(value));
182
+ return {
183
+ .type = SQLiteError,
184
+ .message = "[op-sqlite]: Database file not found" + dbPath
185
+ };
224
186
  }
225
- else if (std::holds_alternative<double>(value))
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
- sqlite3_bind_double(statement, sqIndex, std::get<double>(value));
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
- else if (std::holds_alternative<std::string>(value))
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
- std::string str = std::get<std::string>(value);
232
- sqlite3_bind_text(statement, sqIndex, str.c_str(), str.length(), SQLITE_TRANSIENT);
244
+ return {
245
+ .type = SQLiteError,
246
+ .message = "[op-sqlite]: Database " + dbName + " is not open"
247
+ };
233
248
  }
234
- else if(std::holds_alternative<ArrayBuffer>(value))
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
- ArrayBuffer buffer = std::get<ArrayBuffer>(value);
237
- sqlite3_bind_blob(statement, sqIndex, buffer.data.get(), buffer.size, SQLITE_STATIC);
238
- } else {
239
- sqlite3_bind_null(statement, sqIndex);
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
- BridgeResult sqliteExecute(std::string const dbName,
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 = SQLiteError,
255
- .message = "[op-sqlite]: Database " + dbName + " is not open"
402
+ .type = SQLiteOk,
403
+ .affectedRows = changedRowCount,
404
+ .insertId = static_cast<double>(latestInsertRowId)
256
405
  };
257
406
  }
258
-
259
- sqlite3 *db = dbMap[dbName];
260
-
261
- sqlite3_stmt *statement;
262
- const char *remainingStatement = nullptr;
263
-
264
- bool isConsuming = true;
265
- bool isFailed = false;
266
-
267
- int result = SQLITE_OK;
268
-
269
- do {
270
- const char *queryStr = remainingStatement == nullptr ? query.c_str() : remainingStatement;
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
+ }
271
417
 
272
- int statementStatus = sqlite3_prepare_v2(db, queryStr, -1, &statement, &remainingStatement);
418
+ sqlite3 *db = dbMap[dbName];
419
+ sqlite3_stmt *statement;
273
420
 
274
- if (statementStatus == SQLITE_ERROR)
421
+ int statementStatus = sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
422
+
423
+ if (statementStatus != SQLITE_OK)
275
424
  {
276
425
  const char *message = sqlite3_errmsg(db);
277
426
  return {
278
- .type = SQLiteError,
279
- .message = "[op-sqlite] SQL statement error: " + std::string(message),
280
- };
427
+ SQLiteError,
428
+ "[op-sqlite] SQL statement error: " + std::string(message),
429
+ 0};
281
430
  }
282
431
 
283
- bindStatement(statement, params);
432
+ bool isConsuming = true;
433
+ bool isFailed = false;
284
434
 
285
- isConsuming = true;
286
-
287
- int i, count, column_type;
288
- std::string column_name, column_declared_type;
435
+ int result;
436
+ std::string column_name;
289
437
 
290
438
  while (isConsuming)
291
439
  {
@@ -293,95 +441,11 @@ BridgeResult sqliteExecute(std::string const dbName,
293
441
 
294
442
  switch (result)
295
443
  {
296
- case SQLITE_ROW: {
297
- if(results == NULL)
298
- {
299
- break;
300
- }
301
-
302
- i = 0;
303
- DumbHostObject row = DumbHostObject(metadatas);
304
-
305
- count = sqlite3_column_count(statement);
306
-
307
- while (i < count)
308
- {
309
- column_type = sqlite3_column_type(statement, i);
310
-
311
- switch (column_type)
312
- {
313
- case SQLITE_INTEGER:
314
- {
315
- /**
316
- * Warning this will loose precision because JS can
317
- * only represent Integers up to 53 bits
318
- */
319
- double column_value = sqlite3_column_double(statement, i);
320
- row.values.push_back(JSVariant(column_value));
321
- break;
322
- }
323
-
324
- case SQLITE_FLOAT:
325
- {
326
- double column_value = sqlite3_column_double(statement, i);
327
- row.values.push_back(JSVariant(column_value));
328
- break;
329
- }
330
-
331
- case SQLITE_TEXT:
332
- {
333
- const char *column_value = reinterpret_cast<const char *>(sqlite3_column_text(statement, i));
334
- int byteLen = sqlite3_column_bytes(statement, i);
335
- // Specify length too; in case string contains NULL in the middle
336
- row.values.push_back(JSVariant(std::string(column_value, byteLen)));
337
- break;
338
- }
339
-
340
- case SQLITE_BLOB:
341
- {
342
- int blob_size = sqlite3_column_bytes(statement, i);
343
- const void *blob = sqlite3_column_blob(statement, i);
344
- uint8_t *data = new uint8_t[blob_size];
345
- // You cannot share raw memory between native and JS
346
- // always copy the data
347
- memcpy(data, blob, blob_size);
348
- row.values.push_back(JSVariant(ArrayBuffer {
349
- .data = std::shared_ptr<uint8_t>{data},
350
- .size = static_cast<size_t>(blob_size)
351
- }));
352
- break;
353
- }
354
-
355
- case SQLITE_NULL:
356
- // Intentionally left blank
357
-
358
- default:
359
- row.values.push_back(JSVariant(NULL));
360
- break;
361
- }
362
- i++;
363
- }
364
- results->push_back(row);
444
+ case SQLITE_ROW:
445
+ isConsuming = true;
365
446
  break;
366
- }
367
447
 
368
448
  case SQLITE_DONE:
369
- i = 0;
370
- count = sqlite3_column_count(statement);
371
-
372
- while (i < count)
373
- {
374
- column_name = sqlite3_column_name(statement, i);
375
- const char *type = sqlite3_column_decltype(statement, i);
376
- auto metadata = DynamicHostObject();
377
- metadata.fields.push_back(std::make_pair("name", column_name));
378
- metadata.fields.push_back(std::make_pair("index", i));
379
- metadata.fields.push_back(std::make_pair("type", type));
380
-
381
- metadatas->push_back(metadata);
382
- i++;
383
- }
384
-
385
449
  isConsuming = false;
386
450
  break;
387
451
 
@@ -392,96 +456,31 @@ BridgeResult sqliteExecute(std::string const dbName,
392
456
  }
393
457
 
394
458
  sqlite3_finalize(statement);
395
- } while (remainingStatement != NULL && strcmp(remainingStatement, "") != 0 && !isFailed);
396
-
397
-
398
- if (isFailed)
399
- {
400
- const char *message = sqlite3_errmsg(db);
401
- return {
402
- .type = SQLiteError,
403
- .message = "[op-sqlite] SQLite code: " + std::to_string(result) + " execution error: " + std::string(message)
404
- };
405
- }
406
-
407
- int changedRowCount = sqlite3_changes(db);
408
- long long latestInsertRowId = sqlite3_last_insert_rowid(db);
409
-
410
- return {
411
- .type = SQLiteOk,
412
- .affectedRows = changedRowCount,
413
- .insertId = static_cast<double>(latestInsertRowId)
414
- };
415
- }
416
-
417
- BridgeResult sqliteExecuteLiteral(std::string const dbName, std::string const &query)
418
- {
419
- // Check if db connection is opened
420
- if (dbMap.count(dbName) == 0)
421
- {
422
- return {
423
- SQLiteError,
424
- "[op-sqlite] Database not opened: " + dbName
425
- };
426
- }
427
-
428
- sqlite3 *db = dbMap[dbName];
429
- sqlite3_stmt *statement;
430
-
431
- // Compile and move result into statement memory spot
432
- int statementStatus = sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
433
-
434
- if (statementStatus != SQLITE_OK)
435
- {
436
- const char *message = sqlite3_errmsg(db);
437
- return {
438
- SQLiteError,
439
- "[op-sqlite] SQL statement error: " + std::string(message),
440
- 0};
441
- }
442
-
443
- bool isConsuming = true;
444
- bool isFailed = false;
445
-
446
- int result;
447
- std::string column_name;
448
-
449
- while (isConsuming)
450
- {
451
- result = sqlite3_step(statement);
452
459
 
453
- switch (result)
460
+ if (isFailed)
454
461
  {
455
- case SQLITE_ROW:
456
- isConsuming = true;
457
- break;
458
-
459
- case SQLITE_DONE:
460
- isConsuming = false;
461
- break;
462
-
463
- default:
464
- isFailed = true;
465
- isConsuming = false;
462
+ const char *message = sqlite3_errmsg(db);
463
+ return {
464
+ SQLiteError,
465
+ "[op-sqlite] SQL execution error: " + std::string(message),
466
+ 0};
466
467
  }
467
- }
468
-
469
- sqlite3_finalize(statement);
470
-
471
- if (isFailed)
472
- {
473
- const char *message = sqlite3_errmsg(db);
468
+
469
+ int changedRowCount = sqlite3_changes(db);
474
470
  return {
475
- SQLiteError,
476
- "[op-sqlite] SQL execution error: " + std::string(message),
477
- 0};
471
+ SQLiteOk,
472
+ "",
473
+ changedRowCount};
478
474
  }
479
-
480
- int changedRowCount = sqlite3_changes(db);
481
- return {
482
- SQLiteOk,
483
- "",
484
- changedRowCount};
485
- }
486
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
+
487
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> *values,
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 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@op-engineering/op-sqlite",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "Next generation SQLite for React Native",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",