@op-engineering/op-sqlite 0.0.0-resolution-test

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 (99) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +31 -0
  3. package/android/.project +17 -0
  4. package/android/.settings/org.eclipse.buildship.core.prefs +13 -0
  5. package/android/CMakeLists.txt +141 -0
  6. package/android/build.gradle +266 -0
  7. package/android/c_sources/tokenizers.cpp +88 -0
  8. package/android/c_sources/tokenizers.h +15 -0
  9. package/android/cpp-adapter.cpp +46 -0
  10. package/android/gradle.properties +1 -0
  11. package/android/jniLibs/arm64-v8a/libsql_experimental.a +0 -0
  12. package/android/jniLibs/armeabi-v7a/libsql_experimental.a +0 -0
  13. package/android/jniLibs/x86/libsql_experimental.a +0 -0
  14. package/android/jniLibs/x86_64/libsql_experimental.a +0 -0
  15. package/android/src/main/AndroidManifest.xml +1 -0
  16. package/android/src/main/java/com/op/sqlite/OPSQLiteBridge.kt +37 -0
  17. package/android/src/main/java/com/op/sqlite/OPSQLiteModule.kt +119 -0
  18. package/android/src/main/java/com/op/sqlite/OPSQLitePackage.kt +18 -0
  19. package/android/src/main/jniLibs/arm64-v8a/libcrsqlite.so +0 -0
  20. package/android/src/main/jniLibs/arm64-v8a/libsqlite_vec.so +0 -0
  21. package/android/src/main/jniLibs/armeabi-v7a/libcrsqlite.so +0 -0
  22. package/android/src/main/jniLibs/armeabi-v7a/libsqlite_vec.so +0 -0
  23. package/android/src/main/jniLibs/x86/libcrsqlite.so +0 -0
  24. package/android/src/main/jniLibs/x86/libsqlite_vec.so +0 -0
  25. package/android/src/main/jniLibs/x86_64/libcrsqlite.so +0 -0
  26. package/android/src/main/jniLibs/x86_64/libsqlite_vec.so +0 -0
  27. package/android/src/paper/java/com/op/sqlite/NativeOPSQLiteSpec.java +77 -0
  28. package/cpp/DBHostObject.cpp +852 -0
  29. package/cpp/DBHostObject.h +99 -0
  30. package/cpp/DumbHostObject.cpp +72 -0
  31. package/cpp/DumbHostObject.h +36 -0
  32. package/cpp/OPThreadPool.cpp +120 -0
  33. package/cpp/OPThreadPool.h +44 -0
  34. package/cpp/PreparedStatementHostObject.cpp +151 -0
  35. package/cpp/PreparedStatementHostObject.h +59 -0
  36. package/cpp/SmartHostObject.cpp +34 -0
  37. package/cpp/SmartHostObject.h +24 -0
  38. package/cpp/bindings.cpp +182 -0
  39. package/cpp/bindings.h +19 -0
  40. package/cpp/bridge.cpp +873 -0
  41. package/cpp/bridge.h +80 -0
  42. package/cpp/libsql/bridge.cpp +738 -0
  43. package/cpp/libsql/bridge.h +85 -0
  44. package/cpp/libsql/libsql.h +172 -0
  45. package/cpp/logs.h +40 -0
  46. package/cpp/macros.h +15 -0
  47. package/cpp/sqlcipher/sqlite3.c +262970 -0
  48. package/cpp/sqlcipher/sqlite3.h +13485 -0
  49. package/cpp/sqlite3.c +261454 -0
  50. package/cpp/sqlite3.h +13715 -0
  51. package/cpp/types.h +33 -0
  52. package/cpp/utils.cpp +327 -0
  53. package/cpp/utils.h +47 -0
  54. package/generate_tokenizers_header_file.rb +29 -0
  55. package/ios/OPSQLite.h +7 -0
  56. package/ios/OPSQLite.mm +157 -0
  57. package/ios/OPSQLite.xcodeproj/project.pbxproj +275 -0
  58. package/ios/crsqlite.xcframework/Info.plist +46 -0
  59. package/ios/crsqlite.xcframework/ios-arm64/crsqlite.framework/Info.plist +24 -0
  60. package/ios/crsqlite.xcframework/ios-arm64/crsqlite.framework/crsqlite +0 -0
  61. package/ios/crsqlite.xcframework/ios-arm64_x86_64-simulator/crsqlite.framework/Info.plist +24 -0
  62. package/ios/crsqlite.xcframework/ios-arm64_x86_64-simulator/crsqlite.framework/crsqlite +0 -0
  63. package/ios/libsql.xcframework/Info.plist +48 -0
  64. package/ios/libsql.xcframework/ios-arm64/Headers/libsql.h +172 -0
  65. package/ios/libsql.xcframework/ios-arm64/libsql_experimental.a +0 -0
  66. package/ios/libsql.xcframework/ios-arm64_x86_64-simulator/Headers/libsql.h +172 -0
  67. package/ios/libsql.xcframework/ios-arm64_x86_64-simulator/libsql_experimental.a +0 -0
  68. package/ios/sqlitevec.xcframework/Info.plist +71 -0
  69. package/ios/sqlitevec.xcframework/ios-arm64/sqlitevec.framework/Info.plist +24 -0
  70. package/ios/sqlitevec.xcframework/ios-arm64/sqlitevec.framework/sqlitevec +0 -0
  71. package/ios/sqlitevec.xcframework/ios-arm64_x86_64-simulator/sqlitevec.framework/Info.plist +24 -0
  72. package/ios/sqlitevec.xcframework/ios-arm64_x86_64-simulator/sqlitevec.framework/sqlitevec +0 -0
  73. package/ios/sqlitevec.xcframework/tvos-arm64/sqlitevec.framework/Info.plist +24 -0
  74. package/ios/sqlitevec.xcframework/tvos-arm64/sqlitevec.framework/sqlitevec +0 -0
  75. package/ios/sqlitevec.xcframework/tvos-arm64_x86_64-simulator/sqlitevec.framework/Info.plist +24 -0
  76. package/ios/sqlitevec.xcframework/tvos-arm64_x86_64-simulator/sqlitevec.framework/sqlitevec +0 -0
  77. package/lib/commonjs/NativeOPSQLite.js +9 -0
  78. package/lib/commonjs/NativeOPSQLite.js.map +1 -0
  79. package/lib/commonjs/Storage.js +60 -0
  80. package/lib/commonjs/Storage.js.map +1 -0
  81. package/lib/commonjs/index.js +365 -0
  82. package/lib/commonjs/index.js.map +1 -0
  83. package/lib/module/NativeOPSQLite.js +3 -0
  84. package/lib/module/NativeOPSQLite.js.map +1 -0
  85. package/lib/module/Storage.js +53 -0
  86. package/lib/module/Storage.js.map +1 -0
  87. package/lib/module/index.js +340 -0
  88. package/lib/module/index.js.map +1 -0
  89. package/lib/typescript/src/NativeOPSQLite.d.ts +15 -0
  90. package/lib/typescript/src/NativeOPSQLite.d.ts.map +1 -0
  91. package/lib/typescript/src/Storage.d.ts +23 -0
  92. package/lib/typescript/src/Storage.d.ts.map +1 -0
  93. package/lib/typescript/src/index.d.ts +319 -0
  94. package/lib/typescript/src/index.d.ts.map +1 -0
  95. package/op-sqlite.podspec +212 -0
  96. package/package.json +85 -0
  97. package/src/NativeOPSQLite.ts +17 -0
  98. package/src/Storage.ts +85 -0
  99. package/src/index.ts +722 -0
package/cpp/bridge.cpp ADDED
@@ -0,0 +1,873 @@
1
+ // This file contains pure sqlite operations without JSI interaction
2
+ // Allows a clear defined boundary between the JSI and the SQLite operations
3
+ // so that threading operations are safe and contained within DBHostObject
4
+
5
+ #include "bridge.h"
6
+ #include "DBHostObject.h"
7
+ #include "DumbHostObject.h"
8
+ #include "SmartHostObject.h"
9
+ #include "logs.h"
10
+ #include "utils.h"
11
+ #include <filesystem>
12
+ #include <iostream>
13
+ #include <sstream>
14
+ #include <stdexcept>
15
+ #include <unordered_map>
16
+ #include <variant>
17
+
18
+ #ifdef TOKENIZERS_HEADER_PATH
19
+ #include TOKENIZERS_HEADER_PATH
20
+ #else
21
+ #define TOKENIZER_LIST
22
+ #endif
23
+
24
+ namespace opsqlite {
25
+
26
+ inline void opsqlite_bind_statement(sqlite3_stmt *statement,
27
+ const std::vector<JSVariant> *values) {
28
+ sqlite3_clear_bindings(statement);
29
+
30
+ size_t size = values->size();
31
+
32
+ for (int ii = 0; ii < size; ii++) {
33
+ int stmt_index = ii + 1;
34
+ JSVariant value = values->at(ii);
35
+
36
+ std::visit(
37
+ [&](auto &&v) {
38
+ using T = std::decay_t<decltype(v)>;
39
+
40
+ if constexpr (std::is_same_v<T, bool>) {
41
+ sqlite3_bind_int(statement, stmt_index,
42
+ static_cast<int>(v));
43
+ } else if constexpr (std::is_same_v<T, int>) {
44
+ sqlite3_bind_int(statement, stmt_index, v);
45
+ } else if constexpr (std::is_same_v<T, long long>) {
46
+ sqlite3_bind_double(statement, stmt_index,
47
+ static_cast<double>(v));
48
+ } else if constexpr (std::is_same_v<T, double>) {
49
+ sqlite3_bind_double(statement, stmt_index, v);
50
+ } else if constexpr (std::is_same_v<T, std::string>) {
51
+ sqlite3_bind_text(statement, stmt_index, v.c_str(),
52
+ static_cast<int>(v.length()),
53
+ SQLITE_TRANSIENT);
54
+ } else if constexpr (std::is_same_v<T, ArrayBuffer>) {
55
+ sqlite3_bind_blob(statement, stmt_index, v.data.get(),
56
+ static_cast<int>(v.size),
57
+ SQLITE_TRANSIENT);
58
+ } else {
59
+ sqlite3_bind_null(statement, stmt_index);
60
+ }
61
+ },
62
+ value);
63
+ }
64
+ }
65
+
66
+ /// Returns the completely formed db path, but it also creates any sub-folders
67
+ /// along the way
68
+ std::string opsqlite_get_db_path(std::string const &db_name,
69
+ std::string const &location) {
70
+
71
+ if (location == ":memory:") {
72
+ return location;
73
+ }
74
+
75
+ // Will return false if the directory already exists, no need to check
76
+ std::filesystem::create_directories(location);
77
+
78
+ if (!location.empty() && location.back() != '/') {
79
+ return location + "/" + db_name;
80
+ }
81
+
82
+ return location + db_name;
83
+ }
84
+
85
+ #ifdef OP_SQLITE_USE_SQLCIPHER
86
+ sqlite3 *opsqlite_open(std::string const &name, std::string const &path,
87
+ std::string const &crsqlite_path,
88
+ std::string const &sqlite_vec_path,
89
+ std::string const &encryption_key) {
90
+ #else
91
+ sqlite3 *opsqlite_open(std::string const &name, std::string const &path,
92
+ [[maybe_unused]] std::string const &crsqlite_path,
93
+ [[maybe_unused]] std::string const &sqlite_vec_path) {
94
+ #endif
95
+ std::string final_path = opsqlite_get_db_path(name, path);
96
+ char *errMsg;
97
+ sqlite3 *db;
98
+
99
+ int flags =
100
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
101
+
102
+ int status = sqlite3_open_v2(final_path.c_str(), &db, flags, nullptr);
103
+
104
+ if (status != SQLITE_OK) {
105
+ throw std::runtime_error(sqlite3_errmsg(db));
106
+ }
107
+
108
+ #ifdef OP_SQLITE_USE_SQLCIPHER
109
+ if (!encryption_key.empty()) {
110
+ opsqlite_execute(db, "PRAGMA key = '" + encryption_key + "'", nullptr);
111
+ }
112
+ #endif
113
+
114
+ #ifndef OP_SQLITE_USE_PHONE_VERSION
115
+ sqlite3_enable_load_extension(db, 1);
116
+ #endif
117
+
118
+ #ifdef OP_SQLITE_USE_CRSQLITE
119
+ const char *crsqliteEntryPoint = "sqlite3_crsqlite_init";
120
+
121
+ sqlite3_load_extension(db, crsqlite_path.c_str(), crsqliteEntryPoint,
122
+ &errMsg);
123
+
124
+ if (errMsg != nullptr) {
125
+ throw std::runtime_error(errMsg);
126
+ }
127
+ #endif
128
+
129
+ #ifdef OP_SQLITE_USE_SQLITE_VEC
130
+ const char *vec_entry_point = "sqlite3_vec_init";
131
+
132
+ sqlite3_load_extension(db, sqlite_vec_path.c_str(), vec_entry_point,
133
+ &errMsg);
134
+
135
+ if (errMsg != nullptr) {
136
+ throw std::runtime_error(errMsg);
137
+ }
138
+ #endif
139
+
140
+ TOKENIZER_LIST
141
+
142
+ return db;
143
+ }
144
+
145
+ void opsqlite_close(sqlite3 *db) {
146
+ #ifdef OP_SQLITE_USE_CRSQLITE
147
+ opsqlite_execute(db, "select crsql_finalize();", nullptr);
148
+ #endif
149
+
150
+ sqlite3_close_v2(db);
151
+ }
152
+
153
+ void opsqlite_attach(sqlite3 *db, std::string const &doc_path,
154
+ std::string const &secondary_db_name,
155
+ std::string const &alias) {
156
+ auto secondary_db_path = opsqlite_get_db_path(secondary_db_name, doc_path);
157
+ auto statement = "ATTACH DATABASE '" + secondary_db_path + "' AS " + alias;
158
+
159
+ opsqlite_execute(db, statement, nullptr);
160
+ }
161
+
162
+ void opsqlite_detach(sqlite3 *db, std::string const &alias) {
163
+ std::string statement = "DETACH DATABASE " + alias;
164
+ opsqlite_execute(db, statement, nullptr);
165
+ }
166
+
167
+ void opsqlite_remove(sqlite3 *db, std::string const &name,
168
+ std::string const &doc_path) {
169
+ opsqlite_close(db);
170
+
171
+ std::string db_path = opsqlite_get_db_path(name, doc_path);
172
+
173
+ if (!file_exists(db_path)) {
174
+ throw std::runtime_error("op-sqlite: db file not found:" + db_path);
175
+ }
176
+
177
+ remove(db_path.c_str());
178
+ }
179
+
180
+ BridgeResult opsqlite_execute_prepared_statement(
181
+ sqlite3 *db, sqlite3_stmt *statement, std::vector<DumbHostObject> *results,
182
+ std::shared_ptr<std::vector<SmartHostObject>> &metadatas) {
183
+
184
+ const char *errorMessage;
185
+
186
+ bool isConsuming = true;
187
+ bool isFailed = false;
188
+
189
+ int result = SQLITE_OK;
190
+
191
+ int i, count, column_type;
192
+ std::string column_name, column_declared_type;
193
+
194
+ while (isConsuming) {
195
+ result = sqlite3_step(statement);
196
+
197
+ switch (result) {
198
+ case SQLITE_ROW: {
199
+ i = 0;
200
+ DumbHostObject row = DumbHostObject(metadatas);
201
+
202
+ count = sqlite3_column_count(statement);
203
+
204
+ while (i < count) {
205
+ column_type = sqlite3_column_type(statement, i);
206
+
207
+ switch (column_type) {
208
+ case SQLITE_INTEGER: {
209
+ /**
210
+ * Warning this will loose precision because JS can
211
+ * only represent Integers up to 53 bits
212
+ */
213
+ double column_value = sqlite3_column_double(statement, i);
214
+ row.values.emplace_back(column_value);
215
+ break;
216
+ }
217
+
218
+ case SQLITE_FLOAT: {
219
+ double column_value = sqlite3_column_double(statement, i);
220
+ row.values.emplace_back(column_value);
221
+ break;
222
+ }
223
+
224
+ case SQLITE_TEXT: {
225
+ const char *column_value = reinterpret_cast<const char *>(
226
+ sqlite3_column_text(statement, i));
227
+ int byteLen = sqlite3_column_bytes(statement, i);
228
+ // Specify length too; in case string contains NULL in the
229
+ // middle
230
+ row.values.emplace_back(std::string(column_value, byteLen));
231
+ break;
232
+ }
233
+
234
+ case SQLITE_BLOB: {
235
+ int blob_size = sqlite3_column_bytes(statement, i);
236
+ const void *blob = sqlite3_column_blob(statement, i);
237
+ auto *data = new uint8_t[blob_size];
238
+ // You cannot share raw memory between native and JS
239
+ // always copy the data
240
+ memcpy(data, blob, blob_size);
241
+ row.values.emplace_back(
242
+ ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
243
+ .size = static_cast<size_t>(blob_size)});
244
+ break;
245
+ }
246
+
247
+ case SQLITE_NULL:
248
+ // Intentionally left blank
249
+
250
+ default:
251
+ row.values.emplace_back(nullptr);
252
+ break;
253
+ }
254
+ i++;
255
+ }
256
+
257
+ results->emplace_back(row);
258
+
259
+ break;
260
+ }
261
+
262
+ case SQLITE_DONE:
263
+ if (metadatas != nullptr) {
264
+ i = 0;
265
+ count = sqlite3_column_count(statement);
266
+
267
+ while (i < count) {
268
+ column_name = sqlite3_column_name(statement, i);
269
+ const char *type = sqlite3_column_decltype(statement, i);
270
+ auto metadata = SmartHostObject();
271
+ metadata.fields.emplace_back("name", column_name);
272
+ metadata.fields.emplace_back("index", i);
273
+ metadata.fields.emplace_back(
274
+ "type", type == nullptr ? "UNKNOWN" : type);
275
+
276
+ metadatas->emplace_back(metadata);
277
+ i++;
278
+ }
279
+ }
280
+ isConsuming = false;
281
+ break;
282
+
283
+ default:
284
+ errorMessage = sqlite3_errmsg(db);
285
+ isFailed = true;
286
+ isConsuming = false;
287
+ }
288
+ }
289
+
290
+ sqlite3_reset(statement);
291
+
292
+ if (isFailed) {
293
+ throw std::runtime_error(
294
+ "[op-sqlite] SQLite code: " + std::to_string(result) +
295
+ " execution error: " + std::string(errorMessage));
296
+ }
297
+
298
+ int changedRowCount = sqlite3_changes(db);
299
+ long long latestInsertRowId = sqlite3_last_insert_rowid(db);
300
+
301
+ return {.affectedRows = changedRowCount,
302
+ .insertId = static_cast<double>(latestInsertRowId)};
303
+ }
304
+
305
+ sqlite3_stmt *opsqlite_prepare_statement(sqlite3 *db,
306
+ std::string const &query) {
307
+ sqlite3_stmt *statement;
308
+
309
+ const char *queryStr = query.c_str();
310
+
311
+ int statementStatus =
312
+ sqlite3_prepare_v2(db, queryStr, -1, &statement, nullptr);
313
+
314
+ if (statementStatus == SQLITE_ERROR) {
315
+ const char *message = sqlite3_errmsg(db);
316
+ throw std::runtime_error("[op-sqlite] SQL prepare statement error: " +
317
+ std::string(message));
318
+ }
319
+
320
+ return statement;
321
+ }
322
+
323
+ BridgeResult opsqlite_execute(sqlite3 *db, std::string const &query,
324
+ const std::vector<JSVariant> *params) {
325
+ sqlite3_stmt *statement;
326
+ const char *errorMessage = nullptr;
327
+ const char *remainingStatement = nullptr;
328
+ bool has_failed = false;
329
+ int status, current_column, column_count, column_type;
330
+ std::string column_name, column_declared_type;
331
+ std::vector<std::string> column_names;
332
+ std::vector<std::vector<JSVariant>> rows;
333
+ rows.reserve(20);
334
+ std::vector<JSVariant> row;
335
+
336
+ do {
337
+ const char *query_str =
338
+ remainingStatement == nullptr ? query.c_str() : remainingStatement;
339
+
340
+ status = sqlite3_prepare_v2(db, query_str, -1, &statement,
341
+ &remainingStatement);
342
+
343
+ if (status != SQLITE_OK) {
344
+ errorMessage = sqlite3_errmsg(db);
345
+ throw std::runtime_error("[op-sqlite] sqlite query error: " +
346
+ std::string(errorMessage));
347
+ }
348
+
349
+ // The statement did not fail to parse but there is nothing to do, just
350
+ // skip to the end
351
+ if (statement == nullptr) {
352
+ continue;
353
+ }
354
+
355
+ if (params != nullptr && !params->empty()) {
356
+ opsqlite_bind_statement(statement, params);
357
+ }
358
+
359
+ column_count = sqlite3_column_count(statement);
360
+ column_names.reserve(column_count);
361
+ bool is_consuming_rows = true;
362
+ double double_value;
363
+ const char *string_value;
364
+
365
+ // Do a first pass to get the column names
366
+ for (int i = 0; i < column_count; i++) {
367
+ column_name = sqlite3_column_name(statement, i);
368
+ column_names.emplace_back(column_name);
369
+ }
370
+
371
+ while (is_consuming_rows) {
372
+ status = sqlite3_step(statement);
373
+
374
+ switch (status) {
375
+ case SQLITE_ROW:
376
+ current_column = 0;
377
+ row = std::vector<JSVariant>();
378
+ row.reserve(column_count);
379
+
380
+ while (current_column < column_count) {
381
+ column_type =
382
+ sqlite3_column_type(statement, current_column);
383
+
384
+ switch (column_type) {
385
+
386
+ case SQLITE_INTEGER:
387
+ // intentional fallthrough
388
+ case SQLITE_FLOAT: {
389
+ double_value =
390
+ sqlite3_column_double(statement, current_column);
391
+ row.emplace_back(double_value);
392
+ break;
393
+ }
394
+
395
+ case SQLITE_TEXT: {
396
+ string_value = reinterpret_cast<const char *>(
397
+ sqlite3_column_text(statement, current_column));
398
+ int len =
399
+ sqlite3_column_bytes(statement, current_column);
400
+ // Specify length too; in case string contains NULL in
401
+ // the middle
402
+ row.emplace_back(std::string(string_value, len));
403
+ break;
404
+ }
405
+
406
+ case SQLITE_BLOB: {
407
+ int blob_size =
408
+ sqlite3_column_bytes(statement, current_column);
409
+ const void *blob =
410
+ sqlite3_column_blob(statement, current_column);
411
+ auto *data = new uint8_t[blob_size];
412
+ memcpy(data, blob, blob_size);
413
+ row.emplace_back(ArrayBuffer{
414
+ .data = std::shared_ptr<uint8_t>{data},
415
+ .size = static_cast<size_t>(blob_size)});
416
+ break;
417
+ }
418
+
419
+ case SQLITE_NULL:
420
+ // Intentionally left blank to switch to default case
421
+ default:
422
+ row.emplace_back(nullptr);
423
+ break;
424
+ }
425
+
426
+ current_column++;
427
+ }
428
+
429
+ rows.emplace_back(std::move(row));
430
+ break;
431
+
432
+ case SQLITE_DONE:
433
+ is_consuming_rows = false;
434
+ break;
435
+
436
+ default:
437
+ has_failed = true;
438
+ is_consuming_rows = false;
439
+ }
440
+ }
441
+
442
+ sqlite3_finalize(statement);
443
+ } while (remainingStatement != nullptr &&
444
+ strcmp(remainingStatement, "") != 0 && !has_failed);
445
+
446
+ if (has_failed) {
447
+ const char *message = sqlite3_errmsg(db);
448
+ throw std::runtime_error("[op-sqlite] statement execution error: " +
449
+ std::string(message));
450
+ }
451
+
452
+ int changedRowCount = sqlite3_changes(db);
453
+ long long latestInsertRowId = sqlite3_last_insert_rowid(db);
454
+ return {.affectedRows = changedRowCount,
455
+ .insertId = static_cast<double>(latestInsertRowId),
456
+ .rows = std::move(rows),
457
+ .column_names = std::move(column_names)};
458
+ }
459
+
460
+ BridgeResult opsqlite_execute_host_objects(
461
+ sqlite3 *db, std::string const &query, const std::vector<JSVariant> *params,
462
+ std::vector<DumbHostObject> *results,
463
+ std::shared_ptr<std::vector<SmartHostObject>> &metadatas) {
464
+
465
+ sqlite3_stmt *statement;
466
+ const char *errorMessage;
467
+ const char *remainingStatement = nullptr;
468
+
469
+ bool isConsuming = true;
470
+ bool isFailed = false;
471
+
472
+ int result = SQLITE_OK;
473
+
474
+ do {
475
+ const char *queryStr =
476
+ remainingStatement == nullptr ? query.c_str() : remainingStatement;
477
+
478
+ int statementStatus = sqlite3_prepare_v2(db, queryStr, -1, &statement,
479
+ &remainingStatement);
480
+
481
+ if (statementStatus != SQLITE_OK) {
482
+ const char *message = sqlite3_errmsg(db);
483
+ throw std::runtime_error(
484
+ "[op-sqlite] SQL statement error on opsqlite_execute:\n" +
485
+ std::to_string(statementStatus) + " description:\n" +
486
+ std::string(message));
487
+ }
488
+
489
+ // The statement did not fail to parse but there is nothing to do, just
490
+ // skip to the end
491
+ if (statement == nullptr) {
492
+ continue;
493
+ }
494
+
495
+ if (params != nullptr && !params->empty()) {
496
+ opsqlite_bind_statement(statement, params);
497
+ }
498
+
499
+ int i, count, column_type;
500
+ std::string column_name, column_declared_type;
501
+
502
+ while (isConsuming) {
503
+ result = sqlite3_step(statement);
504
+
505
+ switch (result) {
506
+ case SQLITE_ROW: {
507
+ if (results == nullptr) {
508
+ break;
509
+ }
510
+
511
+ i = 0;
512
+ DumbHostObject row = DumbHostObject(metadatas);
513
+
514
+ count = sqlite3_column_count(statement);
515
+
516
+ while (i < count) {
517
+ column_type = sqlite3_column_type(statement, i);
518
+
519
+ switch (column_type) {
520
+ case SQLITE_INTEGER: {
521
+ /**
522
+ * Warning this will loose precision because JS can
523
+ * only represent Integers up to 53 bits
524
+ */
525
+ double column_value =
526
+ sqlite3_column_double(statement, i);
527
+ row.values.emplace_back(column_value);
528
+ break;
529
+ }
530
+
531
+ case SQLITE_FLOAT: {
532
+ double column_value =
533
+ sqlite3_column_double(statement, i);
534
+ row.values.emplace_back(column_value);
535
+ break;
536
+ }
537
+
538
+ case SQLITE_TEXT: {
539
+ const char *column_value =
540
+ reinterpret_cast<const char *>(
541
+ sqlite3_column_text(statement, i));
542
+ int byteLen = sqlite3_column_bytes(statement, i);
543
+ // Specify length too; in case string contains NULL in
544
+ // the middle
545
+ row.values.emplace_back(
546
+ std::string(column_value, byteLen));
547
+ break;
548
+ }
549
+
550
+ case SQLITE_BLOB: {
551
+ int blob_size = sqlite3_column_bytes(statement, i);
552
+ const void *blob = sqlite3_column_blob(statement, i);
553
+ auto *data = new uint8_t[blob_size];
554
+ // You cannot share raw memory between native and JS
555
+ // always copy the data
556
+ memcpy(data, blob, blob_size);
557
+ row.values.emplace_back(ArrayBuffer{
558
+ .data = std::shared_ptr<uint8_t>{data},
559
+ .size = static_cast<size_t>(blob_size)});
560
+ break;
561
+ }
562
+
563
+ case SQLITE_NULL:
564
+ // Intentionally left blank
565
+
566
+ default:
567
+ row.values.emplace_back(nullptr);
568
+ break;
569
+ }
570
+ i++;
571
+ }
572
+
573
+ results->emplace_back(row);
574
+ break;
575
+ }
576
+
577
+ case SQLITE_DONE:
578
+ if (metadatas != nullptr) {
579
+ i = 0;
580
+ count = sqlite3_column_count(statement);
581
+
582
+ while (i < count) {
583
+ column_name = sqlite3_column_name(statement, i);
584
+ const char *type =
585
+ sqlite3_column_decltype(statement, i);
586
+ auto metadata = SmartHostObject();
587
+ metadata.fields.emplace_back("name", column_name);
588
+ metadata.fields.emplace_back("index", i);
589
+ metadata.fields.emplace_back(
590
+ "type", type == nullptr ? "UNKNOWN" : type);
591
+
592
+ metadatas->push_back(metadata);
593
+ i++;
594
+ }
595
+ }
596
+ isConsuming = false;
597
+ break;
598
+
599
+ default:
600
+ errorMessage = sqlite3_errmsg(db);
601
+ isFailed = true;
602
+ isConsuming = false;
603
+ }
604
+ }
605
+
606
+ sqlite3_finalize(statement);
607
+ } while (remainingStatement != nullptr &&
608
+ strcmp(remainingStatement, "") != 0 && !isFailed);
609
+
610
+ if (isFailed) {
611
+ throw std::runtime_error(
612
+ "[op-sqlite] SQLite error code: " + std::to_string(result) +
613
+ ", description: " + std::string(errorMessage));
614
+ }
615
+
616
+ int changedRowCount = sqlite3_changes(db);
617
+ long long latestInsertRowId = sqlite3_last_insert_rowid(db);
618
+
619
+ return {.affectedRows = changedRowCount,
620
+ .insertId = static_cast<double>(latestInsertRowId)};
621
+ }
622
+
623
+ /// Executes returning data in raw arrays, a small performance optimization
624
+ /// for certain use cases
625
+ BridgeResult
626
+ opsqlite_execute_raw(sqlite3 *db, std::string const &query,
627
+ const std::vector<JSVariant> *params,
628
+ std::vector<std::vector<JSVariant>> *results) {
629
+ sqlite3_stmt *statement;
630
+ const char *errorMessage;
631
+ const char *remainingStatement = nullptr;
632
+
633
+ bool isConsuming = true;
634
+ bool isFailed = false;
635
+
636
+ int step = SQLITE_OK;
637
+
638
+ do {
639
+ const char *queryStr =
640
+ remainingStatement == nullptr ? query.c_str() : remainingStatement;
641
+
642
+ int statementStatus = sqlite3_prepare_v2(db, queryStr, -1, &statement,
643
+ &remainingStatement);
644
+
645
+ if (statementStatus != SQLITE_OK) {
646
+ const char *message = sqlite3_errmsg(db);
647
+ throw std::runtime_error("[op-sqlite] SQL statement error:" +
648
+ std::to_string(statementStatus) +
649
+ " description:" + std::string(message));
650
+ }
651
+
652
+ // The statement did not fail to parse but there is nothing to do, just
653
+ // skip to the end
654
+ if (statement == nullptr) {
655
+ continue;
656
+ }
657
+
658
+ if (params != nullptr && !params->empty()) {
659
+ opsqlite_bind_statement(statement, params);
660
+ }
661
+
662
+ int i, column_type;
663
+ std::string column_name, column_declared_type;
664
+
665
+ int column_count = sqlite3_column_count(statement);
666
+
667
+ while (isConsuming) {
668
+ step = sqlite3_step(statement);
669
+
670
+ switch (step) {
671
+ case SQLITE_ROW: {
672
+ if (results == nullptr) {
673
+ break;
674
+ }
675
+
676
+ std::vector<JSVariant> row;
677
+ row.reserve(column_count);
678
+
679
+ i = 0;
680
+
681
+ while (i < column_count) {
682
+ column_type = sqlite3_column_type(statement, i);
683
+
684
+ switch (column_type) {
685
+ case SQLITE_INTEGER:
686
+ case SQLITE_FLOAT: {
687
+ double column_value =
688
+ sqlite3_column_double(statement, i);
689
+ row.emplace_back(column_value);
690
+ break;
691
+ }
692
+
693
+ case SQLITE_TEXT: {
694
+ const char *column_value =
695
+ reinterpret_cast<const char *>(
696
+ sqlite3_column_text(statement, i));
697
+ int byteLen = sqlite3_column_bytes(statement, i);
698
+ // Specify length too; in case string contains NULL in
699
+ // the middle
700
+ row.emplace_back(std::string(column_value, byteLen));
701
+ break;
702
+ }
703
+
704
+ case SQLITE_BLOB: {
705
+ int blob_size = sqlite3_column_bytes(statement, i);
706
+ const void *blob = sqlite3_column_blob(statement, i);
707
+ auto *data = new uint8_t[blob_size];
708
+ memcpy(data, blob, blob_size);
709
+ row.emplace_back(ArrayBuffer{
710
+ .data = std::shared_ptr<uint8_t>{data},
711
+ .size = static_cast<size_t>(blob_size)});
712
+ break;
713
+ }
714
+
715
+ case SQLITE_NULL:
716
+ // intentional fallthrough
717
+ default:
718
+ row.emplace_back(nullptr);
719
+ break;
720
+ }
721
+ i++;
722
+ }
723
+
724
+ results->emplace_back(row);
725
+
726
+ break;
727
+ }
728
+
729
+ case SQLITE_DONE:
730
+ isConsuming = false;
731
+ break;
732
+
733
+ default:
734
+ errorMessage = sqlite3_errmsg(db);
735
+ isFailed = true;
736
+ isConsuming = false;
737
+ }
738
+ }
739
+
740
+ sqlite3_finalize(statement);
741
+ } while (remainingStatement != nullptr &&
742
+ strcmp(remainingStatement, "") != 0 && !isFailed);
743
+
744
+ if (isFailed) {
745
+ throw std::runtime_error(
746
+ "[op-sqlite] SQLite error code: " + std::to_string(step) +
747
+ ", description: " + std::string(errorMessage));
748
+ }
749
+
750
+ int changedRowCount = sqlite3_changes(db);
751
+ long long latestInsertRowId = sqlite3_last_insert_rowid(db);
752
+
753
+ return {.affectedRows = changedRowCount,
754
+ .insertId = static_cast<double>(latestInsertRowId)};
755
+ }
756
+
757
+ std::string operation_to_string(int operation_type) {
758
+ switch (operation_type) {
759
+ case SQLITE_INSERT:
760
+ return "INSERT";
761
+
762
+ case SQLITE_DELETE:
763
+ return "DELETE";
764
+
765
+ case SQLITE_UPDATE:
766
+ return "UPDATE";
767
+
768
+ default:
769
+ throw std::runtime_error("Unknown SQLite operation on hook");
770
+ }
771
+ }
772
+
773
+ void update_callback(void *db_host_object_ptr, int operation_type,
774
+ [[maybe_unused]] char const *database, char const *table,
775
+ sqlite3_int64 row_id) {
776
+ auto db_host_object = reinterpret_cast<DBHostObject *>(db_host_object_ptr);
777
+ db_host_object->on_update(std::string(table),
778
+ operation_to_string(operation_type), row_id);
779
+ }
780
+
781
+ void opsqlite_register_update_hook(sqlite3 *db, void *db_host_object) {
782
+ sqlite3_update_hook(db, &update_callback, (void *)db_host_object);
783
+ }
784
+
785
+ void opsqlite_deregister_update_hook(sqlite3 *db) {
786
+ sqlite3_update_hook(db, nullptr, nullptr);
787
+ }
788
+
789
+ int commit_callback(void *db_host_object_ptr) {
790
+ auto db_host_object = reinterpret_cast<DBHostObject *>(db_host_object_ptr);
791
+ db_host_object->on_commit();
792
+ return 0;
793
+ }
794
+
795
+ void opsqlite_register_commit_hook(sqlite3 *db, void *db_host_object_ptr) {
796
+ sqlite3_commit_hook(db, &commit_callback, db_host_object_ptr);
797
+ }
798
+
799
+ void opsqlite_deregister_commit_hook(sqlite3 *db) {
800
+ sqlite3_commit_hook(db, nullptr, nullptr);
801
+ }
802
+
803
+ void rollback_callback(void *db_host_object_ptr) {
804
+ auto db_host_object = reinterpret_cast<DBHostObject *>(db_host_object_ptr);
805
+ db_host_object->on_rollback();
806
+ }
807
+
808
+ void opsqlite_register_rollback_hook(sqlite3 *db, void *db_host_object_ptr) {
809
+ sqlite3_rollback_hook(db, &rollback_callback, db_host_object_ptr);
810
+ }
811
+
812
+ void opsqlite_deregister_rollback_hook(sqlite3 *db) {
813
+ sqlite3_rollback_hook(db, nullptr, nullptr);
814
+ }
815
+
816
+ void opsqlite_load_extension(sqlite3 *db, std::string &path,
817
+ std::string &entry_point) {
818
+ #ifdef OP_SQLITE_USE_PHONE_VERSION
819
+ throw std::runtime_error("[op-sqlite] Embedded version of SQLite does not "
820
+ "support loading extensions");
821
+ #else
822
+ int status = 0;
823
+ status = sqlite3_enable_load_extension(db, 1);
824
+
825
+ if (status != SQLITE_OK) {
826
+ throw std::runtime_error("Could not enable extension loading");
827
+ }
828
+
829
+ const char *entry_point_cstr = nullptr;
830
+ if (!entry_point.empty()) {
831
+ entry_point_cstr = entry_point.c_str();
832
+ }
833
+
834
+ char *error_message;
835
+
836
+ status = sqlite3_load_extension(db, path.c_str(), entry_point_cstr,
837
+ &error_message);
838
+ if (status != SQLITE_OK) {
839
+ throw std::runtime_error(error_message);
840
+ }
841
+ #endif
842
+ }
843
+
844
+ BatchResult
845
+ opsqlite_execute_batch(sqlite3 *db,
846
+ const std::vector<BatchArguments> *commands) {
847
+ size_t commandCount = commands->size();
848
+ if (commandCount <= 0) {
849
+ throw std::runtime_error("No SQL commands provided");
850
+ }
851
+
852
+ int affectedRows = 0;
853
+ opsqlite_execute(db, "BEGIN EXCLUSIVE TRANSACTION", nullptr);
854
+ for (int i = 0; i < commandCount; i++) {
855
+ const auto &command = commands->at(i);
856
+ // We do not provide a datastructure to receive query data because we
857
+ // don't need/want to handle this results in a batch execution
858
+ try {
859
+ auto result = opsqlite_execute(db, command.sql, &command.params);
860
+ affectedRows += result.affectedRows;
861
+ } catch (std::exception &exc) {
862
+ opsqlite_execute(db, "ROLLBACK", nullptr);
863
+ throw exc;
864
+ }
865
+ }
866
+ opsqlite_execute(db, "COMMIT", nullptr);
867
+ return BatchResult{
868
+ .affectedRows = affectedRows,
869
+ .commands = static_cast<int>(commandCount),
870
+ };
871
+ }
872
+
873
+ } // namespace opsqlite