@op-engineering/op-sqlite 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +274 -0
  3. package/android/.project +17 -0
  4. package/android/.settings/org.eclipse.buildship.core.prefs +13 -0
  5. package/android/CMakeLists.txt +57 -0
  6. package/android/build.gradle +127 -0
  7. package/android/cpp-adapter.cpp +42 -0
  8. package/android/gradle.properties +4 -0
  9. package/android/src/main/AndroidManifest.xml +4 -0
  10. package/android/src/main/java/com/op/sqlite/OPSQLiteBridge.java +29 -0
  11. package/android/src/main/java/com/op/sqlite/OPSQLiteModule.java +46 -0
  12. package/android/src/main/java/com/op/sqlite/OPSQLitePackage.java +26 -0
  13. package/cpp/DynamicHostObject.cpp +30 -0
  14. package/cpp/DynamicHostObject.h +30 -0
  15. package/cpp/ThreadPool.cpp +95 -0
  16. package/cpp/ThreadPool.h +46 -0
  17. package/cpp/bindings.cpp +430 -0
  18. package/cpp/bindings.h +13 -0
  19. package/cpp/bridge.cpp +502 -0
  20. package/cpp/bridge.h +34 -0
  21. package/cpp/logs.h +38 -0
  22. package/cpp/macros.h +16 -0
  23. package/cpp/sqlbatchexecutor.cpp +94 -0
  24. package/cpp/sqlbatchexecutor.h +28 -0
  25. package/cpp/sqlite3.c +252611 -0
  26. package/cpp/sqlite3.h +13257 -0
  27. package/cpp/utils.cpp +218 -0
  28. package/cpp/utils.h +55 -0
  29. package/ios/OPSQLite.h +8 -0
  30. package/ios/OPSQLite.mm +63 -0
  31. package/ios/OPSQLite.xcodeproj/project.pbxproj +275 -0
  32. package/lib/commonjs/index.js +190 -0
  33. package/lib/commonjs/index.js.map +1 -0
  34. package/lib/module/index.js +183 -0
  35. package/lib/module/index.js.map +1 -0
  36. package/lib/typescript/index.d.ts +108 -0
  37. package/op-sqlite.podspec +39 -0
  38. package/package.json +79 -0
  39. package/src/index.ts +374 -0
package/cpp/bridge.cpp ADDED
@@ -0,0 +1,502 @@
1
+ #include "bridge.h"
2
+ #include <sstream>
3
+ #include <iostream>
4
+ #include <sqlite3.h>
5
+ #include <ctime>
6
+ #include <unistd.h>
7
+ #include <sys/stat.h>
8
+ #include <map>
9
+ #include "logs.h"
10
+ #include "DynamicHostObject.h"
11
+
12
+ namespace osp {
13
+
14
+ std::unordered_map<std::string, sqlite3 *> dbMap = std::unordered_map<std::string, sqlite3 *>();
15
+
16
+ bool folder_exists(const std::string &foldername)
17
+ {
18
+ struct stat buffer;
19
+ return (stat(foldername.c_str(), &buffer) == 0);
20
+ }
21
+
22
+ /**
23
+ * Portable wrapper for mkdir. Internally used by mkdir()
24
+ * @param[in] path the full path of the directory to create.
25
+ * @return zero on success, otherwise -1.
26
+ */
27
+ int _mkdir(const char *path)
28
+ {
29
+ #if _POSIX_C_SOURCE
30
+ return mkdir(path);
31
+ #else
32
+ return mkdir(path, 0755); // not sure if this works on mac
33
+ #endif
34
+ }
35
+
36
+ /**
37
+ * Recursive, portable wrapper for mkdir.
38
+ * @param[in] path the full path of the directory to create.
39
+ * @return zero on success, otherwise -1.
40
+ */
41
+ int mkdir(const char *path)
42
+ {
43
+ std::string current_level = "/";
44
+ std::string level;
45
+ std::stringstream ss(path);
46
+ // First line is empty because it starts with /User
47
+ getline(ss, level, '/');
48
+ // split path using slash as a separator
49
+ while (getline(ss, level, '/'))
50
+ {
51
+ current_level += level; // append folder to the current level
52
+ // create current level
53
+ if (!folder_exists(current_level) && _mkdir(current_level.c_str()) != 0)
54
+ return -1;
55
+
56
+ current_level += "/"; // don't forget to append a slash
57
+ }
58
+
59
+ return 0;
60
+ }
61
+
62
+ inline bool file_exists(const std::string &path)
63
+ {
64
+ struct stat buffer;
65
+ return (stat(path.c_str(), &buffer) == 0);
66
+ }
67
+
68
+ std::string get_db_path(std::string const dbName, std::string const docPath)
69
+ {
70
+ mkdir(docPath.c_str());
71
+ return docPath + "/" + dbName;
72
+ }
73
+
74
+ BridgeResult sqliteOpenDb(std::string const dbName, std::string const docPath)
75
+ {
76
+ std::string dbPath = get_db_path(dbName, docPath);
77
+
78
+ int sqlOpenFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
79
+
80
+ sqlite3 *db;
81
+ int exit = 0;
82
+ exit = sqlite3_open_v2(dbPath.c_str(), &db, sqlOpenFlags, nullptr);
83
+
84
+ if (exit != SQLITE_OK)
85
+ {
86
+ return {
87
+ .type = SQLiteError,
88
+ .message = sqlite3_errmsg(db)
89
+ };
90
+ }
91
+ else
92
+ {
93
+ dbMap[dbName] = db;
94
+ }
95
+
96
+ return BridgeResult{
97
+ .type = SQLiteOk,
98
+ .affectedRows = 0
99
+ };
100
+ }
101
+
102
+ BridgeResult sqliteCloseDb(std::string const dbName)
103
+ {
104
+
105
+ if (dbMap.count(dbName) == 0)
106
+ {
107
+ return {
108
+ .type = SQLiteError,
109
+ .message = dbName + " is not open",
110
+ };
111
+ }
112
+
113
+ sqlite3 *db = dbMap[dbName];
114
+
115
+ sqlite3_close_v2(db);
116
+
117
+ dbMap.erase(dbName);
118
+
119
+ return BridgeResult{
120
+ .type = SQLiteOk,
121
+ };
122
+ }
123
+
124
+ void sqliteCloseAll() {
125
+ for (auto const& x : dbMap) {
126
+ // In certain cases, this will return SQLITE_OK, mark the database connection as an unusable "zombie",
127
+ // and deallocate the connection later.
128
+ sqlite3_close_v2(x.second);
129
+ }
130
+ dbMap.clear();
131
+ }
132
+
133
+ BridgeResult sqliteAttachDb(std::string const mainDBName, std::string const docPath, std::string const databaseToAttach, std::string const alias)
134
+ {
135
+ /**
136
+ * There is no need to check if mainDBName is opened because sqliteExecuteLiteral will do that.
137
+ * */
138
+ std::string dbPath = get_db_path(databaseToAttach, docPath);
139
+ std::string statement = "ATTACH DATABASE '" + dbPath + "' AS " + alias;
140
+
141
+ BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
142
+
143
+ if (result.type == SQLiteError)
144
+ {
145
+ return {
146
+ .type = SQLiteError,
147
+ .message = mainDBName + " was unable to attach another database: " + std::string(result.message),
148
+ };
149
+ }
150
+ return {
151
+ .type = SQLiteOk,
152
+ };
153
+ }
154
+
155
+ BridgeResult sqliteDetachDb(std::string const mainDBName, std::string const alias)
156
+ {
157
+ /**
158
+ * There is no need to check if mainDBName is opened because sqliteExecuteLiteral will do that.
159
+ * */
160
+ std::string statement = "DETACH DATABASE " + alias;
161
+ BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
162
+ if (result.type == SQLiteError)
163
+ {
164
+ return BridgeResult{
165
+ .type = SQLiteError,
166
+ .message = mainDBName + "was unable to detach database: " + std::string(result.message),
167
+ };
168
+ }
169
+ return BridgeResult{
170
+ .type = SQLiteOk,
171
+ };
172
+ }
173
+
174
+ BridgeResult sqliteRemoveDb(std::string const dbName, std::string const docPath)
175
+ {
176
+ if (dbMap.count(dbName) == 1)
177
+ {
178
+ BridgeResult closeResult = sqliteCloseDb(dbName);
179
+ if (closeResult.type == SQLiteError)
180
+ {
181
+ return closeResult;
182
+ }
183
+ }
184
+
185
+ std::string dbPath = get_db_path(dbName, docPath);
186
+
187
+ if (!file_exists(dbPath))
188
+ {
189
+ return {
190
+ .type = SQLiteError,
191
+ .message = "[op-sqlite]: Database file not found" + dbPath
192
+ };
193
+ }
194
+
195
+ remove(dbPath.c_str());
196
+
197
+ return {
198
+ .type = SQLiteOk,
199
+ };
200
+ }
201
+
202
+ void bindStatement(sqlite3_stmt *statement, std::vector<std::any> *values)
203
+ {
204
+ size_t size = values->size();
205
+ if (size <= 0)
206
+ {
207
+ return;
208
+ }
209
+
210
+ for (int ii = 0; ii < size; ii++)
211
+ {
212
+ int sqIndex = ii + 1;
213
+ std::any value = values->at(ii);
214
+
215
+ if (value.type() == typeid(NULL) || value.type() == typeid(nullptr))
216
+ {
217
+ sqlite3_bind_null(statement, sqIndex);
218
+ }
219
+ else if (value.type() == typeid(bool))
220
+ {
221
+ sqlite3_bind_int(statement, sqIndex, std::any_cast<bool>(value));
222
+ }
223
+ else if (value.type() == typeid(int))
224
+ {
225
+ sqlite3_bind_int(statement, sqIndex, std::any_cast<int>(value));
226
+ }
227
+ else if (value.type() == typeid(long long))
228
+ {
229
+ sqlite3_bind_double(statement, sqIndex, std::any_cast<long long>(value));
230
+ }
231
+ else if (value.type() == typeid(double))
232
+ {
233
+ sqlite3_bind_double(statement, sqIndex, std::any_cast<double>(value));
234
+ }
235
+ else if (value.type() == typeid(std::string))
236
+ {
237
+ std::string str = std::any_cast<std::string>(value);
238
+ sqlite3_bind_text(statement, sqIndex, str.c_str(), str.length(), SQLITE_TRANSIENT);
239
+ } else {
240
+ throw std::invalid_argument("Unsupported scalar type, cannot convert to JSI Value");
241
+ }
242
+ // else if (value.type() == typeid(const char*))
243
+ // {
244
+ // sqlite3_bind_text(statement, sqIndex, str.c_str(), str.length(), SQLITE_TRANSIENT);
245
+ // return jsi::String::createFromAscii(rt, std::any_cast<const char*>(value));
246
+ // }
247
+ // TODO Add support for array buffers
248
+ // else if (value.isObject())
249
+ // {
250
+ // auto obj = value.asObject(rt);
251
+ // if(obj.isArrayBuffer(rt)) {
252
+ // auto buf = obj.getArrayBuffer(rt);
253
+ // sqlite3_bind_blob(statement, sqIndex, buf.data(rt), buf.size(rt), SQLITE_STATIC);
254
+ // }
255
+ //
256
+ // else if (dataType == ARRAY_BUFFER)
257
+ // {
258
+ //
259
+ // }
260
+ // sqlite3_bind_blob(statement, sqIndex, value.arrayBufferValue.get(), value.arrayBufferSize, SQLITE_STATIC);
261
+ // }
262
+
263
+
264
+
265
+ }
266
+ }
267
+
268
+ BridgeResult sqliteExecute(std::string const dbName,
269
+ std::string const &query,
270
+ std::vector<std::any> *params,
271
+ std::vector<std::shared_ptr<DynamicHostObject>> *results,
272
+ std::vector<std::shared_ptr<DynamicHostObject>> *metadatas)
273
+ {
274
+
275
+ if (dbMap.count(dbName) == 0)
276
+ {
277
+ return {
278
+ .type = SQLiteError,
279
+ .message = "[op-sqlite]: Database " + dbName + " is not open",
280
+ .affectedRows = 0
281
+ };
282
+ }
283
+
284
+ sqlite3 *db = dbMap[dbName];
285
+
286
+ sqlite3_stmt *statement;
287
+
288
+ int statementStatus = sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
289
+
290
+ if (statementStatus == SQLITE_OK) // statemnet is correct, bind the passed parameters
291
+ {
292
+ bindStatement(statement, params);
293
+ }
294
+ else
295
+ {
296
+ const char *message = sqlite3_errmsg(db);
297
+ return {
298
+ .type = SQLiteError,
299
+ .message = "[op-sqlite] SQL execution error: " + std::string(message),
300
+ .affectedRows = 0};
301
+ }
302
+
303
+ bool isConsuming = true;
304
+ bool isFailed = false;
305
+
306
+ int result, i, count, column_type;
307
+ std::string column_name, column_declared_type;
308
+ std::shared_ptr<DynamicHostObject> row;
309
+
310
+ while (isConsuming)
311
+ {
312
+ result = sqlite3_step(statement);
313
+
314
+ switch (result)
315
+ {
316
+ case SQLITE_ROW:
317
+ if(results == NULL)
318
+ {
319
+ break;
320
+ }
321
+
322
+ i = 0;
323
+ row = std::make_shared<DynamicHostObject>();
324
+ count = sqlite3_column_count(statement);
325
+
326
+ while (i < count)
327
+ {
328
+ column_type = sqlite3_column_type(statement, i);
329
+ column_name = sqlite3_column_name(statement, i);
330
+
331
+ switch (column_type)
332
+ {
333
+
334
+ case SQLITE_INTEGER:
335
+ {
336
+ /**
337
+ * Warning this will loose precision because JS can
338
+ * only represent Integers up to 53 bits
339
+ */
340
+ double column_value = sqlite3_column_double(statement, i);
341
+ row->fields[column_name] = std::any(column_value);
342
+ break;
343
+ }
344
+
345
+ case SQLITE_FLOAT:
346
+ {
347
+ double column_value = sqlite3_column_double(statement, i);
348
+ row->fields[column_name] = std::any(column_value);
349
+ break;
350
+ }
351
+
352
+ case SQLITE_TEXT:
353
+ {
354
+ const char *column_value = reinterpret_cast<const char *>(sqlite3_column_text(statement, i));
355
+ int byteLen = sqlite3_column_bytes(statement, i);
356
+ // Specify length too; in case string contains NULL in the middle (which SQLite supports!)
357
+ row->fields[column_name] = std::any(std::string(column_value, byteLen));
358
+ break;
359
+ }
360
+
361
+ case SQLITE_BLOB:
362
+ {
363
+ int blob_size = sqlite3_column_bytes(statement, i);
364
+ const void *blob = sqlite3_column_blob(statement, i);
365
+ uint8_t *data = new uint8_t[blob_size];
366
+ memcpy(data, blob, blob_size);
367
+ // TODO array buffer support
368
+ // row[column_name] = createArrayBufferQuickValue(data, blob_size);
369
+ break;
370
+ }
371
+
372
+ case SQLITE_NULL:
373
+ // Intentionally left blank
374
+
375
+ default:
376
+ row->fields[column_name] = std::any(NULL);
377
+ break;
378
+ }
379
+ i++;
380
+ }
381
+ results->push_back(row);
382
+ break;
383
+
384
+ case SQLITE_DONE:
385
+ if(metadatas != nullptr)
386
+ {
387
+ i = 0;
388
+ count = sqlite3_column_count(statement);
389
+ while (i < count)
390
+ {
391
+ column_name = sqlite3_column_name(statement, i);
392
+ const char *type = sqlite3_column_decltype(statement, i);
393
+ auto metadata = std::make_shared<DynamicHostObject>();
394
+ metadata->fields["name"] = column_name;
395
+ metadata->fields["index"] = i;
396
+ metadata->fields["type"] = type;
397
+
398
+ i++;
399
+ }
400
+ }
401
+ isConsuming = false;
402
+ break;
403
+
404
+ default:
405
+ isFailed = true;
406
+ isConsuming = false;
407
+ }
408
+ }
409
+
410
+ sqlite3_finalize(statement);
411
+
412
+ if (isFailed)
413
+ {
414
+ const char *message = sqlite3_errmsg(db);
415
+ return {
416
+ .type = SQLiteError,
417
+ .message = "[op-sqlite] SQL execution error: " + std::string(message),
418
+ .affectedRows = 0,
419
+ .insertId = 0
420
+ };
421
+ }
422
+
423
+ int changedRowCount = sqlite3_changes(db);
424
+ long long latestInsertRowId = sqlite3_last_insert_rowid(db);
425
+
426
+ return {
427
+ .type = SQLiteOk,
428
+ .affectedRows = changedRowCount,
429
+ .insertId = static_cast<double>(latestInsertRowId)};
430
+ }
431
+
432
+ BridgeResult sqliteExecuteLiteral(std::string const dbName, std::string const &query)
433
+ {
434
+ // Check if db connection is opened
435
+ if (dbMap.count(dbName) == 0)
436
+ {
437
+ return {
438
+ SQLiteError,
439
+ "[op-sqlite] Database not opened: " + dbName
440
+ };
441
+ }
442
+
443
+ sqlite3 *db = dbMap[dbName];
444
+ sqlite3_stmt *statement;
445
+
446
+ // Compile and move result into statement memory spot
447
+ int statementStatus = sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
448
+
449
+ if (statementStatus != SQLITE_OK)
450
+ {
451
+ const char *message = sqlite3_errmsg(db);
452
+ return {
453
+ SQLiteError,
454
+ "[op-sqlite] SQL execution error: " + std::string(message),
455
+ 0};
456
+ }
457
+
458
+ bool isConsuming = true;
459
+ bool isFailed = false;
460
+
461
+ int result;
462
+ std::string column_name;
463
+
464
+ while (isConsuming)
465
+ {
466
+ result = sqlite3_step(statement);
467
+
468
+ switch (result)
469
+ {
470
+ case SQLITE_ROW:
471
+ isConsuming = true;
472
+ break;
473
+
474
+ case SQLITE_DONE:
475
+ isConsuming = false;
476
+ break;
477
+
478
+ default:
479
+ isFailed = true;
480
+ isConsuming = false;
481
+ }
482
+ }
483
+
484
+ sqlite3_finalize(statement);
485
+
486
+ if (isFailed)
487
+ {
488
+ const char *message = sqlite3_errmsg(db);
489
+ return {
490
+ SQLiteError,
491
+ "[op-sqlite] SQL execution error: " + std::string(message),
492
+ 0};
493
+ }
494
+
495
+ int changedRowCount = sqlite3_changes(db);
496
+ return {
497
+ SQLiteOk,
498
+ "",
499
+ changedRowCount};
500
+ }
501
+
502
+ }
package/cpp/bridge.h ADDED
@@ -0,0 +1,34 @@
1
+ #ifndef bridge_h
2
+ #define bridge_h
3
+
4
+ #include "utils.h"
5
+ #include <vector>
6
+ #include "DynamicHostObject.h"
7
+
8
+ namespace osp {
9
+
10
+ namespace jsi = facebook::jsi;
11
+
12
+ BridgeResult sqliteOpenDb(std::string const dbName, std::string const docPath);
13
+
14
+ BridgeResult sqliteCloseDb(std::string const dbName);
15
+
16
+ BridgeResult sqliteRemoveDb(std::string const dbName, std::string const docPath);
17
+
18
+ BridgeResult sqliteAttachDb(std::string const mainDBName, std::string const docPath, std::string const databaseToAttach, std::string const alias);
19
+
20
+ BridgeResult sqliteDetachDb(std::string const mainDBName, std::string const alias);
21
+
22
+ BridgeResult sqliteExecute(std::string const dbName,
23
+ std::string const &query,
24
+ std::vector<std::any> *values,
25
+ std::vector<std::shared_ptr<DynamicHostObject>> *result,
26
+ std::vector<std::shared_ptr<DynamicHostObject>> *metadata);
27
+
28
+ BridgeResult sqliteExecuteLiteral(std::string const dbName, std::string const &query);
29
+
30
+ void sqliteCloseAll();
31
+
32
+ }
33
+
34
+ #endif /* bridge_h */
package/cpp/logs.h ADDED
@@ -0,0 +1,38 @@
1
+ #ifdef ANDROID
2
+ // LOGS ANDROID
3
+ #include <android/log.h>
4
+ #define LOG_TAG "op-sqlite"
5
+ #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
6
+ #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
7
+ #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
8
+ #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
9
+ #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
10
+ #define LOGSIMPLE(...)
11
+ #else
12
+ // LOGS NO ANDROID
13
+ #include <stdio.h>
14
+ #define LOG_TAG "op-sqlite"
15
+ #define LOGV(...) \
16
+ printf(" "); \
17
+ printf(__VA_ARGS__); \
18
+ printf("\t - <%s> \n", LOG_TAG);
19
+ #define LOGD(...) \
20
+ printf(" "); \
21
+ printf(__VA_ARGS__); \
22
+ printf("\t - <%s> \n", LOG_TAG);
23
+ #define LOGI(...) \
24
+ printf(" "); \
25
+ printf(__VA_ARGS__); \
26
+ printf("\t - <%s> \n", LOG_TAG);
27
+ #define LOGW(...) \
28
+ printf(" * Warning: "); \
29
+ printf(__VA_ARGS__); \
30
+ printf("\t - <%s> \n", LOG_TAG);
31
+ #define LOGE(...) \
32
+ printf(" *** Error: "); \
33
+ printf(__VA_ARGS__); \
34
+ printf("\t - <%s> \n", LOG_TAG);
35
+ #define LOGSIMPLE(...) \
36
+ printf(" "); \
37
+ printf(__VA_ARGS__);
38
+ #endif // ANDROID
package/cpp/macros.h ADDED
@@ -0,0 +1,16 @@
1
+ #ifndef macros_h
2
+ #define macros_h
3
+
4
+ #define HOSTFN(name, basecount) \
5
+ jsi::Function::createFromHostFunction( \
6
+ rt, \
7
+ jsi::PropNameID::forAscii(rt, name), \
8
+ basecount, \
9
+ [=](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value
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
+ #endif /* macros_h */
@@ -0,0 +1,94 @@
1
+ #include "sqlbatchexecutor.h"
2
+
3
+ namespace osp {
4
+
5
+ /**
6
+ * Batch execution implementation
7
+ */
8
+
9
+ void toBatchArguments(jsi::Runtime &rt, jsi::Array const &batchParams, std::vector<BatchArguments> *commands)
10
+ {
11
+ for (int i = 0; i < batchParams.length(rt); i++)
12
+ {
13
+ const jsi::Array &command = batchParams.getValueAtIndex(rt, i).asObject(rt).asArray(rt);
14
+ if (command.length(rt) == 0)
15
+ {
16
+ continue;
17
+ }
18
+
19
+ const std::string query = command.getValueAtIndex(rt, 0).asString(rt).utf8(rt);
20
+ const jsi::Value &commandParams = command.length(rt) > 1 ? command.getValueAtIndex(rt, 1) : jsi::Value::undefined();
21
+ if (!commandParams.isUndefined() && commandParams.asObject(rt).isArray(rt) && commandParams.asObject(rt).asArray(rt).length(rt) > 0 && commandParams.asObject(rt).asArray(rt).getValueAtIndex(rt, 0).isObject())
22
+ {
23
+ // This arguments is an array of arrays, like a batch update of a single sql command.
24
+ const jsi::Array &batchUpdateParams = commandParams.asObject(rt).asArray(rt);
25
+ for (int x = 0; x < batchUpdateParams.length(rt); x++)
26
+ {
27
+ const jsi::Value &p = batchUpdateParams.getValueAtIndex(rt, x);
28
+ auto params = std::make_shared<std::vector<std::any>>(toAnyVec(rt, p));
29
+ commands->push_back({
30
+ query,
31
+ params
32
+ });
33
+ }
34
+ }
35
+ else
36
+ {
37
+ auto params = std::make_shared<std::vector<std::any>>(toAnyVec(rt, commandParams));
38
+ commands->push_back({
39
+ query,
40
+ params
41
+ });
42
+ }
43
+ }
44
+ }
45
+
46
+ BatchResult sqliteExecuteBatch(std::string dbName, std::vector<BatchArguments> *commands)
47
+ {
48
+ size_t commandCount = commands->size();
49
+ if(commandCount <= 0)
50
+ {
51
+ return BatchResult {
52
+ .type = SQLiteError,
53
+ .message = "No SQL commands provided",
54
+ };
55
+ }
56
+
57
+ try
58
+ {
59
+ int affectedRows = 0;
60
+ sqliteExecuteLiteral(dbName, "BEGIN EXCLUSIVE TRANSACTION");
61
+ for(int i = 0; i < commandCount; i++) {
62
+ auto command = commands->at(i);
63
+ // We do not provide a datastructure to receive query data because we don't need/want to handle this results in a batch execution
64
+ auto result = sqliteExecute(dbName, command.sql, command.params.get(), nullptr, nullptr);
65
+ if(result.type == SQLiteError)
66
+ {
67
+ sqliteExecuteLiteral(dbName, "ROLLBACK");
68
+ return BatchResult {
69
+ .type = SQLiteError,
70
+ .message = result.message,
71
+ };
72
+ } else
73
+ {
74
+ affectedRows += result.affectedRows;
75
+ }
76
+ }
77
+ sqliteExecuteLiteral(dbName, "COMMIT");
78
+ return BatchResult {
79
+ .type = SQLiteOk,
80
+ .affectedRows = affectedRows,
81
+ .commands = static_cast<int>(commandCount),
82
+ };
83
+ } catch(std::exception &exc)
84
+ {
85
+ sqliteExecuteLiteral(dbName, "ROLLBACK");
86
+ return BatchResult {
87
+ .type = SQLiteError,
88
+ .message = exc.what(),
89
+ };
90
+ }
91
+ }
92
+
93
+
94
+ }