@op-engineering/op-sqlite 6.0.1 → 6.0.2-beta3

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 (38) hide show
  1. package/android/CMakeLists.txt +26 -15
  2. package/android/build.gradle +10 -1
  3. package/android/cpp-adapter.cpp +4 -0
  4. package/android/jniLibs/arm64-v8a/libsql_experimental.a +0 -0
  5. package/android/jniLibs/armeabi-v7a/libsql_experimental.a +0 -0
  6. package/android/jniLibs/x86/libsql_experimental.a +0 -0
  7. package/android/jniLibs/x86_64/libsql_experimental.a +0 -0
  8. package/cpp/DBHostObject.cpp +882 -0
  9. package/cpp/DBHostObject.h +61 -0
  10. package/cpp/PreparedStatementHostObject.cpp +29 -15
  11. package/cpp/PreparedStatementHostObject.h +18 -14
  12. package/cpp/bindings.cpp +24 -616
  13. package/cpp/bindings.h +5 -2
  14. package/cpp/bridge.cpp +55 -16
  15. package/cpp/bridge.h +5 -1
  16. package/cpp/libsql/bridge.cpp +629 -0
  17. package/cpp/libsql/bridge.h +88 -0
  18. package/cpp/libsql/libsql.h +133 -0
  19. package/cpp/sqlite3.h +0 -1
  20. package/cpp/types.h +5 -0
  21. package/cpp/utils.cpp +68 -6
  22. package/cpp/utils.h +5 -5
  23. package/ios/libsql.xcframework/Info.plist +48 -0
  24. package/ios/libsql.xcframework/ios-arm64/Headers/libsql.h +133 -0
  25. package/ios/libsql.xcframework/ios-arm64/libsql_experimental.a +0 -0
  26. package/ios/libsql.xcframework/ios-arm64_x86_64-simulator/Headers/libsql.h +133 -0
  27. package/ios/libsql.xcframework/ios-arm64_x86_64-simulator/libsql_experimental.a +0 -0
  28. package/lib/commonjs/index.js +143 -153
  29. package/lib/commonjs/index.js.map +1 -1
  30. package/lib/module/index.js +141 -152
  31. package/lib/module/index.js.map +1 -1
  32. package/lib/typescript/src/index.d.ts +28 -39
  33. package/lib/typescript/src/index.d.ts.map +1 -1
  34. package/op-sqlite.podspec +20 -3
  35. package/package.json +1 -1
  36. package/src/index.ts +203 -272
  37. package/cpp/sqlbatchexecutor.cpp +0 -93
  38. package/cpp/sqlbatchexecutor.h +0 -30
@@ -0,0 +1,882 @@
1
+ #include "DBHostObject.h"
2
+ #include "PreparedStatementHostObject.h"
3
+ #if OP_SQLITE_USE_LIBSQL
4
+ #include "libsql/bridge.h"
5
+ #else
6
+ #include "bridge.h"
7
+ #endif
8
+ #include "macros.h"
9
+ #include "utils.h"
10
+ #include <iostream>
11
+
12
+ namespace opsqlite {
13
+
14
+ namespace jsi = facebook::jsi;
15
+ namespace react = facebook::react;
16
+
17
+ #ifndef OP_SQLITE_USE_LIBSQL
18
+ void DBHostObject::auto_register_update_hook() {
19
+ if (update_hook_callback == nullptr && reactive_queries.size() == 0 &&
20
+ has_update_hook_registered) {
21
+ opsqlite_deregister_update_hook(db_name);
22
+ return;
23
+ }
24
+
25
+ if (has_update_hook_registered) {
26
+ return;
27
+ }
28
+
29
+ auto hook = [this](std::string dbName, std::string table_name,
30
+ std::string operation, int rowId) {
31
+ if (update_hook_callback != nullptr) {
32
+ std::vector<JSVariant> params;
33
+ std::vector<DumbHostObject> results;
34
+ std::shared_ptr<std::vector<SmartHostObject>> metadata =
35
+ std::make_shared<std::vector<SmartHostObject>>();
36
+
37
+ if (operation != "DELETE") {
38
+ std::string query = "SELECT * FROM " + table_name +
39
+ " where rowid = " + std::to_string(rowId) + ";";
40
+ opsqlite_execute(dbName, query, &params, &results, metadata);
41
+ }
42
+
43
+ jsCallInvoker->invokeAsync(
44
+ [this,
45
+ results = std::make_shared<std::vector<DumbHostObject>>(results),
46
+ callback = update_hook_callback, tableName = std::move(table_name),
47
+ operation = std::move(operation), &rowId] {
48
+ auto res = jsi::Object(rt);
49
+ res.setProperty(rt, "table",
50
+ jsi::String::createFromUtf8(rt, tableName));
51
+ res.setProperty(rt, "operation",
52
+ jsi::String::createFromUtf8(rt, operation));
53
+ res.setProperty(rt, "rowId", jsi::Value(rowId));
54
+ if (results->size() != 0) {
55
+ res.setProperty(
56
+ rt, "row",
57
+ jsi::Object::createFromHostObject(
58
+ rt, std::make_shared<DumbHostObject>(results->at(0))));
59
+ }
60
+
61
+ callback->asObject(rt).asFunction(rt).call(rt, res);
62
+ });
63
+ }
64
+
65
+ for (const auto &query_ptr : reactive_queries) {
66
+
67
+ auto query = query_ptr.get();
68
+ if (query->discriminators.size() == 0) {
69
+ continue;
70
+ }
71
+
72
+ bool shouldFire = false;
73
+
74
+ for (const auto &discriminator : query->discriminators) {
75
+ if (discriminator.table != table_name) {
76
+ continue;
77
+ }
78
+
79
+ // Table has matched
80
+
81
+ // If no ids are specified, then we should fire
82
+ if (discriminator.ids.size() == 0) {
83
+ shouldFire = true;
84
+ break;
85
+ } else { // If ids are specified, then we should check if the rowId
86
+ // matches
87
+ for (const auto &discrimator_id : discriminator.ids) {
88
+ if (rowId == discrimator_id) {
89
+ shouldFire = true;
90
+ break;
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ if (!shouldFire) {
97
+ continue;
98
+ }
99
+
100
+ std::vector<DumbHostObject> results;
101
+ std::shared_ptr<std::vector<SmartHostObject>> metadata =
102
+ std::make_shared<std::vector<SmartHostObject>>();
103
+
104
+ auto status = opsqlite_execute_prepared_statement(db_name, query->stmt,
105
+ &results, metadata);
106
+
107
+ if (status.type == SQLiteError) {
108
+ jsCallInvoker->invokeAsync(
109
+ [this, callback = query->callback, status = std::move(status)] {
110
+ auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
111
+ auto error = errorCtr.callAsConstructor(
112
+ rt, jsi::String::createFromUtf8(rt, status.message));
113
+ callback->asObject(rt).asFunction(rt).call(rt, error);
114
+ });
115
+ } else {
116
+ jsCallInvoker->invokeAsync(
117
+ [this,
118
+ results = std::make_shared<std::vector<DumbHostObject>>(results),
119
+ callback = query->callback, metadata, status = std::move(status)] {
120
+ if (status.type == SQLiteOk) {
121
+ auto jsiResult =
122
+ createResult(rt, status, results.get(), metadata);
123
+ callback->asObject(rt).asFunction(rt).call(rt, jsiResult);
124
+ } else {
125
+ auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
126
+ auto error = errorCtr.callAsConstructor(
127
+ rt, jsi::String::createFromUtf8(rt, status.message));
128
+ callback->asObject(rt).asFunction(rt).call(rt, error);
129
+ }
130
+ });
131
+ }
132
+ }
133
+ };
134
+
135
+ opsqlite_register_update_hook(db_name, std::move(hook));
136
+ has_update_hook_registered = true;
137
+ }
138
+ #endif
139
+
140
+ DBHostObject::DBHostObject(jsi::Runtime &rt, std::string &base_path,
141
+ std::shared_ptr<react::CallInvoker> jsCallInvoker,
142
+ std::shared_ptr<ThreadPool> thread_pool,
143
+ std::string &db_name, std::string &path,
144
+ std::string &crsqlite_path,
145
+ std::string &encryption_key)
146
+ : base_path(base_path), jsCallInvoker(jsCallInvoker),
147
+ thread_pool(thread_pool), db_name(db_name), rt(rt) {
148
+
149
+ #ifdef OP_SQLITE_USE_SQLCIPHER
150
+ BridgeResult result =
151
+ opsqlite_open(db_name, path, crsqlite_path, encryption_key);
152
+ #elif OP_SQLITE_USE_LIBSQL
153
+ BridgeResult result = opsqlite_libsql_open(db_name, path);
154
+ #else
155
+ BridgeResult result = opsqlite_open(db_name, path, crsqlite_path);
156
+ #endif
157
+
158
+ if (result.type == SQLiteError) {
159
+ throw std::runtime_error(result.message);
160
+ }
161
+
162
+ auto attach = HOSTFN("attach", 4) {
163
+ if (count < 3) {
164
+ throw jsi::JSError(rt,
165
+ "[op-sqlite][attach] Incorrect number of arguments");
166
+ }
167
+ if (!args[0].isString() || !args[1].isString() || !args[2].isString()) {
168
+ throw jsi::JSError(
169
+ rt, "dbName, databaseToAttach and alias must be a strings");
170
+ return {};
171
+ }
172
+
173
+ std::string tempDocPath = std::string(base_path);
174
+ if (count > 3 && !args[3].isUndefined() && !args[3].isNull()) {
175
+ if (!args[3].isString()) {
176
+ throw std::runtime_error(
177
+ "[op-sqlite][attach] database location must be a string");
178
+ }
179
+
180
+ tempDocPath = tempDocPath + "/" + args[3].asString(rt).utf8(rt);
181
+ }
182
+
183
+ std::string dbName = args[0].asString(rt).utf8(rt);
184
+ std::string databaseToAttach = args[1].asString(rt).utf8(rt);
185
+ std::string alias = args[2].asString(rt).utf8(rt);
186
+ #ifdef OP_SQLITE_USE_LIBSQL
187
+ BridgeResult result =
188
+ opsqlite_libsql_attach(dbName, tempDocPath, databaseToAttach, alias);
189
+ #else
190
+ BridgeResult result =
191
+ opsqlite_attach(dbName, tempDocPath, databaseToAttach, alias);
192
+ #endif
193
+ if (result.type == SQLiteError) {
194
+ throw std::runtime_error(result.message);
195
+ }
196
+
197
+ return {};
198
+ });
199
+
200
+ auto detach = HOSTFN("detach", 2) {
201
+ if (count < 2) {
202
+ throw std::runtime_error(
203
+ "[op-sqlite][detach] Incorrect number of arguments");
204
+ }
205
+ if (!args[0].isString() || !args[1].isString()) {
206
+ throw std::runtime_error(
207
+ "dbName, databaseToAttach and alias must be a strings");
208
+ return {};
209
+ }
210
+
211
+ std::string dbName = args[0].asString(rt).utf8(rt);
212
+ std::string alias = args[1].asString(rt).utf8(rt);
213
+ #ifdef OP_SQLITE_USE_LIBSQL
214
+ BridgeResult result = opsqlite_libsql_detach(dbName, alias);
215
+ #else
216
+ BridgeResult result = opsqlite_detach(dbName, alias);
217
+ #endif
218
+
219
+ if (result.type == SQLiteError) {
220
+ throw jsi::JSError(rt, result.message.c_str());
221
+ }
222
+
223
+ return {};
224
+ });
225
+
226
+ auto close = HOSTFN("close", 0) {
227
+ #ifdef OP_SQLITE_USE_LIBSQL
228
+ BridgeResult result = opsqlite_libsql_close(db_name);
229
+ #else
230
+ BridgeResult result = opsqlite_close(db_name);
231
+ #endif
232
+
233
+ if (result.type == SQLiteError) {
234
+ throw jsi::JSError(rt, result.message.c_str());
235
+ }
236
+
237
+ return {};
238
+ });
239
+
240
+ auto remove = HOSTFN("delete", 1) {
241
+
242
+ std::string path = std::string(base_path);
243
+
244
+ if (count == 1 && !args[0].isUndefined() && !args[0].isNull()) {
245
+ if (!args[1].isString()) {
246
+ throw std::runtime_error(
247
+ "[op-sqlite][open] database location must be a string");
248
+ }
249
+
250
+ std::string location = args[1].asString(rt).utf8(rt);
251
+
252
+ if (!location.empty()) {
253
+ if (location == ":memory:") {
254
+ path = ":memory:";
255
+ } else if (location.rfind("/", 0) == 0) {
256
+ path = location;
257
+ } else {
258
+ path = path + "/" + location;
259
+ }
260
+ }
261
+ }
262
+
263
+ #ifdef OP_SQLITE_USE_LIBSQL
264
+ BridgeResult result = opsqlite_libsql_remove(db_name, path);
265
+ #else
266
+ BridgeResult result = opsqlite_remove(db_name, path);
267
+ #endif
268
+
269
+ if (result.type == SQLiteError) {
270
+ throw std::runtime_error(result.message);
271
+ }
272
+
273
+ return {};
274
+ });
275
+
276
+ auto execute = HOSTFN("execute", 2) {
277
+ const std::string query = args[0].asString(rt).utf8(rt);
278
+ std::vector<JSVariant> params;
279
+
280
+ if (count == 2) {
281
+ const jsi::Value &originalParams = args[1];
282
+ params = to_variant_vec(rt, originalParams);
283
+ }
284
+
285
+ std::vector<DumbHostObject> results;
286
+ std::shared_ptr<std::vector<SmartHostObject>> metadata =
287
+ std::make_shared<std::vector<SmartHostObject>>();
288
+
289
+ #ifdef OP_SQLITE_USE_LIBSQL
290
+ auto status =
291
+ opsqlite_libsql_execute(db_name, query, &params, &results, metadata);
292
+ #else
293
+ auto status = opsqlite_execute(db_name, query, &params, &results, metadata);
294
+ #endif
295
+
296
+ if (status.type == SQLiteError) {
297
+ throw std::runtime_error(status.message);
298
+ }
299
+
300
+ auto jsiResult = createResult(rt, status, &results, metadata);
301
+ return jsiResult;
302
+ });
303
+
304
+ auto execute_raw_async = HOSTFN("executeRawAsync", 2) {
305
+ const std::string query = args[0].asString(rt).utf8(rt);
306
+ std::vector<JSVariant> params;
307
+
308
+ if (count == 2) {
309
+ const jsi::Value &originalParams = args[1];
310
+ params = to_variant_vec(rt, originalParams);
311
+ }
312
+
313
+ auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
314
+ auto promise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2) {
315
+ auto resolve = std::make_shared<jsi::Value>(rt, args[0]);
316
+ auto reject = std::make_shared<jsi::Value>(rt, args[1]);
317
+
318
+ auto task = [&rt, db_name, query, params = std::move(params), resolve,
319
+ reject, invoker = this->jsCallInvoker]() {
320
+ try {
321
+ std::vector<std::vector<JSVariant>> results;
322
+
323
+ #ifdef OP_SQLITE_USE_LIBSQL
324
+ auto status =
325
+ opsqlite_libsql_execute_raw(db_name, query, &params, &results);
326
+ #else
327
+ auto status = opsqlite_execute_raw(db_name, query, &params, &results);
328
+ #endif
329
+ //
330
+ // if (invalidated) {
331
+ // return;
332
+ // }
333
+
334
+ invoker->invokeAsync([&rt, results = std::move(results),
335
+ status = std::move(status), resolve, reject] {
336
+ if (status.type == SQLiteOk) {
337
+ auto jsiResult = create_raw_result(rt, status, &results);
338
+ resolve->asObject(rt).asFunction(rt).call(rt,
339
+ std::move(jsiResult));
340
+ } else {
341
+ auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
342
+ auto error = errorCtr.callAsConstructor(
343
+ rt, jsi::String::createFromUtf8(rt, status.message));
344
+ reject->asObject(rt).asFunction(rt).call(rt, error);
345
+ }
346
+ });
347
+
348
+ } catch (std::exception &exc) {
349
+ invoker->invokeAsync([&rt, exc = std::move(exc), reject] {
350
+ auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
351
+ auto error = errorCtr.callAsConstructor(
352
+ rt, jsi::String::createFromAscii(rt, exc.what()));
353
+ reject->asObject(rt).asFunction(rt).call(rt, error);
354
+ });
355
+ }
356
+ };
357
+
358
+ thread_pool->queueWork(task);
359
+
360
+ return {};
361
+ }));
362
+
363
+ return promise;
364
+ });
365
+
366
+ auto execute_async = HOSTFN("executeAsync", 2) {
367
+ const std::string query = args[0].asString(rt).utf8(rt);
368
+ std::vector<JSVariant> params;
369
+
370
+ if (count == 2) {
371
+ const jsi::Value &originalParams = args[1];
372
+ params = to_variant_vec(rt, originalParams);
373
+ }
374
+
375
+ auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
376
+ auto promise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2) {
377
+ auto resolve = std::make_shared<jsi::Value>(rt, args[0]);
378
+ auto reject = std::make_shared<jsi::Value>(rt, args[1]);
379
+
380
+ auto task = [&rt, &db_name, query, params = std::move(params), resolve,
381
+ reject, invoker = this->jsCallInvoker]() {
382
+ try {
383
+ std::vector<DumbHostObject> results;
384
+ std::shared_ptr<std::vector<SmartHostObject>> metadata =
385
+ std::make_shared<std::vector<SmartHostObject>>();
386
+ #ifdef OP_SQLITE_USE_LIBSQL
387
+ auto status = opsqlite_libsql_execute(db_name, query, &params,
388
+ &results, metadata);
389
+ #else
390
+ auto status =
391
+ opsqlite_execute(db_name, query, &params, &results, metadata);
392
+ #endif
393
+
394
+ // if (invalidated) {
395
+ // return;
396
+ // }
397
+
398
+ invoker->invokeAsync(
399
+ [&rt,
400
+ results = std::make_shared<std::vector<DumbHostObject>>(results),
401
+ metadata, status = std::move(status), resolve, reject] {
402
+ if (status.type == SQLiteOk) {
403
+ auto jsiResult =
404
+ createResult(rt, status, results.get(), metadata);
405
+ resolve->asObject(rt).asFunction(rt).call(
406
+ rt, std::move(jsiResult));
407
+ } else {
408
+ auto errorCtr =
409
+ rt.global().getPropertyAsFunction(rt, "Error");
410
+ auto error = errorCtr.callAsConstructor(
411
+ rt, jsi::String::createFromUtf8(rt, status.message));
412
+ reject->asObject(rt).asFunction(rt).call(rt, error);
413
+ }
414
+ });
415
+
416
+ } catch (std::exception &exc) {
417
+ invoker->invokeAsync([&rt, exc = std::move(exc), reject] {
418
+ auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
419
+ auto error = errorCtr.callAsConstructor(
420
+ rt, jsi::String::createFromAscii(rt, exc.what()));
421
+ reject->asObject(rt).asFunction(rt).call(rt, error);
422
+ });
423
+ }
424
+ };
425
+
426
+ thread_pool->queueWork(task);
427
+
428
+ return {};
429
+ }));
430
+
431
+ return promise;
432
+ });
433
+
434
+ auto execute_batch = HOSTFN("executeBatch", 1) {
435
+ if (sizeof(args) < 1) {
436
+ throw std::runtime_error(
437
+ "[op-sqlite][executeBatch] - Incorrect parameter count");
438
+ }
439
+
440
+ const jsi::Value &params = args[0];
441
+ if (params.isNull() || params.isUndefined()) {
442
+ throw std::runtime_error("[op-sqlite][executeBatch] - An array of SQL "
443
+ "commands or parameters is needed");
444
+ }
445
+ const jsi::Array &batchParams = params.asObject(rt).asArray(rt);
446
+ std::vector<BatchArguments> commands;
447
+ to_batch_arguments(rt, batchParams, &commands);
448
+
449
+ #ifdef OP_SQLITE_USE_LIBSQL
450
+ auto batchResult = opsqlite_libsql_execute_batch(db_name, &commands);
451
+ #else
452
+ auto batchResult = opsqlite_execute_batch(db_name, &commands);
453
+ #endif
454
+ if (batchResult.type == SQLiteOk) {
455
+ auto res = jsi::Object(rt);
456
+ res.setProperty(rt, "rowsAffected", jsi::Value(batchResult.affectedRows));
457
+ return std::move(res);
458
+ } else {
459
+ throw std::runtime_error(batchResult.message);
460
+ }
461
+ });
462
+
463
+ auto execute_batch_async = HOSTFN("executeBatchAsync", 1) {
464
+ if (sizeof(args) < 1) {
465
+ throw std::runtime_error(
466
+ "[op-sqlite][executeAsyncBatch] Incorrect parameter count");
467
+ return {};
468
+ }
469
+
470
+ const jsi::Value &params = args[0];
471
+
472
+ if (params.isNull() || params.isUndefined()) {
473
+ throw std::runtime_error(
474
+ "[op-sqlite][executeAsyncBatch] - An array of SQL "
475
+ "commands or parameters is needed");
476
+ return {};
477
+ }
478
+
479
+ const jsi::Array &batchParams = params.asObject(rt).asArray(rt);
480
+
481
+ std::vector<BatchArguments> commands;
482
+ to_batch_arguments(rt, batchParams, &commands);
483
+
484
+ auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
485
+ auto promise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2) {
486
+ auto resolve = std::make_shared<jsi::Value>(rt, args[0]);
487
+ auto reject = std::make_shared<jsi::Value>(rt, args[1]);
488
+
489
+ auto task = [&rt, &db_name, &jsCallInvoker,
490
+ commands =
491
+ std::make_shared<std::vector<BatchArguments>>(commands),
492
+ resolve, reject]() {
493
+ try {
494
+ #ifdef OP_SQLITE_USE_LIBSQL
495
+ auto batchResult =
496
+ opsqlite_libsql_execute_batch(db_name, commands.get());
497
+ #else
498
+ auto batchResult = opsqlite_execute_batch(db_name, commands.get());
499
+ #endif
500
+ jsCallInvoker->invokeAsync([&rt, batchResult = std::move(batchResult),
501
+ resolve, reject] {
502
+ if (batchResult.type == SQLiteOk) {
503
+ auto res = jsi::Object(rt);
504
+ res.setProperty(rt, "rowsAffected",
505
+ jsi::Value(batchResult.affectedRows));
506
+ resolve->asObject(rt).asFunction(rt).call(rt, std::move(res));
507
+ } else {
508
+ auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
509
+ auto error = errorCtr.callAsConstructor(
510
+ rt, jsi::String::createFromUtf8(rt, batchResult.message));
511
+ reject->asObject(rt).asFunction(rt).call(rt, error);
512
+ }
513
+ });
514
+ } catch (std::exception &exc) {
515
+ jsCallInvoker->invokeAsync(
516
+ [&rt, reject, &exc] { throw jsi::JSError(rt, exc.what()); });
517
+ }
518
+ };
519
+ thread_pool->queueWork(task);
520
+
521
+ return {};
522
+ }));
523
+
524
+ return promise;
525
+ });
526
+
527
+ #ifndef OP_SQLITE_USE_LIBSQL
528
+ auto load_file = HOSTFN("loadFile", 1) {
529
+ if (sizeof(args) < 1) {
530
+ throw std::runtime_error(
531
+ "[op-sqlite][loadFile] Incorrect parameter count");
532
+ return {};
533
+ }
534
+
535
+ const std::string sqlFileName = args[0].asString(rt).utf8(rt);
536
+
537
+ auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
538
+ auto promise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2)
539
+ {
540
+ auto resolve = std::make_shared<jsi::Value>(rt, args[0]);
541
+ auto reject = std::make_shared<jsi::Value>(rt, args[1]);
542
+
543
+ auto task = [&rt, &db_name, &jsCallInvoker, sqlFileName, resolve,
544
+ reject]() {
545
+ try {
546
+ const auto importResult = importSQLFile(db_name, sqlFileName);
547
+
548
+ jsCallInvoker->invokeAsync([&rt, result = std::move(importResult),
549
+ resolve, reject] {
550
+ if (result.type == SQLiteOk) {
551
+ auto res = jsi::Object(rt);
552
+ res.setProperty(rt, "rowsAffected",
553
+ jsi::Value(result.affectedRows));
554
+ res.setProperty(rt, "commands", jsi::Value(result.commands));
555
+ resolve->asObject(rt).asFunction(rt).call(rt, std::move(res));
556
+ } else {
557
+ auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
558
+ auto error = errorCtr.callAsConstructor(
559
+ rt, jsi::String::createFromUtf8(rt, result.message));
560
+ reject->asObject(rt).asFunction(rt).call(rt, error);
561
+ }
562
+ });
563
+ } catch (std::exception &exc) {
564
+ jsCallInvoker->invokeAsync(
565
+ [&rt, err = exc.what(), reject] { throw jsi::JSError(rt, err); });
566
+ }
567
+ };
568
+ thread_pool->queueWork(task);
569
+ return {};
570
+ }));
571
+
572
+ return promise;
573
+ });
574
+
575
+ auto update_hook = HOSTFN("updateHook", 1) {
576
+ auto callback = std::make_shared<jsi::Value>(rt, args[0]);
577
+
578
+ if (callback->isUndefined() || callback->isNull()) {
579
+ update_hook_callback = nullptr;
580
+ } else {
581
+ update_hook_callback = callback;
582
+ }
583
+ auto_register_update_hook();
584
+ return {};
585
+ });
586
+
587
+ auto commit_hook = HOSTFN("commitHook", 1) {
588
+ if (sizeof(args) < 1) {
589
+ throw std::runtime_error("[op-sqlite][commitHook] callback needed");
590
+ return {};
591
+ }
592
+
593
+ auto callback = std::make_shared<jsi::Value>(rt, args[0]);
594
+ if (callback->isUndefined() || callback->isNull()) {
595
+ opsqlite_deregister_commit_hook(db_name);
596
+ return {};
597
+ }
598
+ commit_hook_callback = callback;
599
+
600
+ auto hook = [&rt, jsCallInvoker, callback](std::string dbName) {
601
+ jsCallInvoker->invokeAsync(
602
+ [&rt, callback] { callback->asObject(rt).asFunction(rt).call(rt); });
603
+ };
604
+
605
+ opsqlite_register_commit_hook(db_name, std::move(hook));
606
+
607
+ return {};
608
+ });
609
+
610
+ auto rollback_hook = HOSTFN("rollbackHook", 1) {
611
+ if (sizeof(args) < 1) {
612
+ throw std::runtime_error("[op-sqlite][rollbackHook] callback needed");
613
+ return {};
614
+ }
615
+
616
+ auto callback = std::make_shared<jsi::Value>(rt, args[0]);
617
+
618
+ if (callback->isUndefined() || callback->isNull()) {
619
+ opsqlite_deregister_rollback_hook(db_name);
620
+ return {};
621
+ }
622
+ rollback_hook_callback = callback;
623
+
624
+ auto hook = [&rt, jsCallInvoker, callback](std::string db_name) {
625
+ jsCallInvoker->invokeAsync(
626
+ [&rt, callback] { callback->asObject(rt).asFunction(rt).call(rt); });
627
+ };
628
+
629
+ opsqlite_register_rollback_hook(db_name, std::move(hook));
630
+ return {};
631
+ });
632
+
633
+ auto load_extension = HOSTFN("loadExtension", 1) {
634
+ auto path = args[0].asString(rt).utf8(rt);
635
+ std::string entry_point = "";
636
+ if (count > 1 && args[1].isString()) {
637
+ entry_point = args[1].asString(rt).utf8(rt);
638
+ }
639
+
640
+ auto result = opsqlite_load_extension(db_name, path, entry_point);
641
+ if (result.type == SQLiteError) {
642
+ throw std::runtime_error(result.message);
643
+ }
644
+ return {};
645
+ });
646
+
647
+ auto reactive_execute = HOSTFN("reactiveExecute", 0) {
648
+ auto query = args[0].asObject(rt);
649
+ // if (!query.hasProperty(rt, "query") || !query.hasProperty(rt, "args")
650
+ // ||
651
+ // !query.hasProperty(rt, "tables") || !query.hasProperty(rt,
652
+ // "rowIds")
653
+ // || !query.hasProperty(rt, "callback")) {
654
+ // throw std::runtime_error(
655
+ // "[op-sqlite][reactiveExecute] Query object must have query, args,
656
+ // " "tables, rowIds and callback properties");
657
+ // }
658
+
659
+ const std::string query_str =
660
+ query.getProperty(rt, "query").asString(rt).utf8(rt);
661
+ auto js_args = query.getProperty(rt, "args");
662
+ auto js_discriminators =
663
+ query.getProperty(rt, "fireOn").asObject(rt).asArray(rt);
664
+ auto variant_args = to_variant_vec(rt, js_args);
665
+
666
+ sqlite3_stmt *stmt = opsqlite_prepare_statement(db_name, query_str);
667
+ opsqlite_bind_statement(stmt, &variant_args);
668
+
669
+ auto callback =
670
+ std::make_shared<jsi::Value>(query.getProperty(rt, "callback"));
671
+
672
+ // std::vector<JSVariant> query_args = to_variant_vec(rt, argsArray);
673
+ // std::vector<std::string> tables = to_string_vec(rt, tablesArray);
674
+ // std::vector<int> rowIds;
675
+ // if (query.hasProperty(rt, "rowIds")) {
676
+ // auto rowIdsArray = query.getProperty(rt, "rowIds");
677
+ // rowIds = to_int_vec(rt, rowIdsArray);
678
+ // }
679
+
680
+ std::vector<TableRowDiscriminator> discriminators;
681
+
682
+ for (size_t i = 0; i < js_discriminators.length(rt); i++) {
683
+ auto js_discriminator =
684
+ js_discriminators.getValueAtIndex(rt, i).asObject(rt);
685
+ std::string table =
686
+ js_discriminator.getProperty(rt, "table").asString(rt).utf8(rt);
687
+ std::vector<int> ids;
688
+ if (js_discriminator.hasProperty(rt, "ids")) {
689
+ auto js_ids =
690
+ js_discriminator.getProperty(rt, "ids").asObject(rt).asArray(rt);
691
+ for (size_t j = 0; j < js_ids.length(rt); j++) {
692
+ ids.push_back(
693
+ static_cast<int>(js_ids.getValueAtIndex(rt, j).asNumber()));
694
+ }
695
+ }
696
+ discriminators.push_back({table, ids});
697
+ }
698
+
699
+ std::shared_ptr<ReactiveQuery> reactiveQuery =
700
+ std::make_shared<ReactiveQuery>(
701
+ ReactiveQuery{stmt, discriminators, callback});
702
+
703
+ reactive_queries.push_back(reactiveQuery);
704
+
705
+ auto_register_update_hook();
706
+
707
+ auto unsubscribe = HOSTFN("unsubscribe", 0) {
708
+ auto it = std::find(reactive_queries.begin(), reactive_queries.end(),
709
+ reactiveQuery);
710
+ if (it != reactive_queries.end()) {
711
+ reactive_queries.erase(it);
712
+ }
713
+ auto_register_update_hook();
714
+ return {};
715
+ });
716
+
717
+ return unsubscribe;
718
+ });
719
+
720
+ #endif
721
+
722
+ auto prepare_statement = HOSTFN("prepareStatement", 1) {
723
+ auto query = args[0].asString(rt).utf8(rt);
724
+ #ifdef OP_SQLITE_USE_LIBSQL
725
+ libsql_stmt_t statement = opsqlite_libsql_prepare_statement(db_name, query);
726
+ #else
727
+ sqlite3_stmt *statement = opsqlite_prepare_statement(db_name, query);
728
+ #endif
729
+ auto preparedStatementHostObject =
730
+ std::make_shared<PreparedStatementHostObject>(db_name, statement);
731
+
732
+ return jsi::Object::createFromHostObject(rt, preparedStatementHostObject);
733
+ });
734
+
735
+ auto get_db_path = HOSTFN("getDbPath", 1) {
736
+ std::string path = std::string(base_path);
737
+ if (count == 1 && !args[0].isUndefined() && !args[0].isNull()) {
738
+ if (!args[0].isString()) {
739
+ throw std::runtime_error(
740
+ "[op-sqlite][open] database location must be a string");
741
+ }
742
+
743
+ std::string lastPath = args[0].asString(rt).utf8(rt);
744
+
745
+ if (lastPath == ":memory:") {
746
+ path = ":memory:";
747
+ } else if (lastPath.rfind("/", 0) == 0) {
748
+ path = lastPath;
749
+ } else {
750
+ path = path + "/" + lastPath;
751
+ }
752
+ }
753
+
754
+ auto result = opsqlite_get_db_path(db_name, path);
755
+ return jsi::String::createFromUtf8(rt, result);
756
+ });
757
+
758
+ function_map["attach"] = std::move(attach);
759
+ function_map["detach"] = std::move(detach);
760
+ function_map["close"] = std::move(close);
761
+ function_map["executeRawAsync"] = std::move(execute_raw_async);
762
+ function_map["execute"] = std::move(execute);
763
+ function_map["executeAsync"] = std::move(execute_async);
764
+ function_map["delete"] = std::move(remove);
765
+ function_map["executeBatch"] = std::move(execute_batch);
766
+ function_map["executeBatchAsync"] = std::move(execute_batch_async);
767
+ #ifndef OP_SQLITE_USE_LIBSQL
768
+ function_map["loadFile"] = std::move(load_file);
769
+ function_map["updateHook"] = std::move(update_hook);
770
+ function_map["commitHook"] = std::move(commit_hook);
771
+ function_map["rollbackHook"] = std::move(rollback_hook);
772
+ function_map["loadExtension"] = std::move(load_extension);
773
+ function_map["reactiveExecute"] = std::move(reactive_execute);
774
+ #endif
775
+ function_map["prepareStatement"] = std::move(prepare_statement);
776
+ function_map["getDbPath"] = std::move(get_db_path);
777
+ };
778
+
779
+ std::vector<jsi::PropNameID> DBHostObject::getPropertyNames(jsi::Runtime &rt) {
780
+ std::vector<jsi::PropNameID> keys;
781
+
782
+ return keys;
783
+ }
784
+
785
+ jsi::Value DBHostObject::get(jsi::Runtime &rt,
786
+ const jsi::PropNameID &propNameID) {
787
+
788
+ auto name = propNameID.utf8(rt);
789
+ if (name == "attach") {
790
+ return jsi::Value(rt, function_map["attach"]);
791
+ }
792
+ if (name == "detach") {
793
+ return jsi::Value(rt, function_map["detach"]);
794
+ }
795
+ if (name == "close") {
796
+ return jsi::Value(rt, function_map["close"]);
797
+ }
798
+ if (name == "executeRawAsync") {
799
+ return jsi::Value(rt, function_map["executeRawAsync"]);
800
+ }
801
+ if (name == "execute") {
802
+ return jsi::Value(rt, function_map["execute"]);
803
+ }
804
+ if (name == "executeAsync") {
805
+ return jsi::Value(rt, function_map["executeAsync"]);
806
+ }
807
+ if (name == "delete") {
808
+ return jsi::Value(rt, function_map["delete"]);
809
+ }
810
+ if (name == "executeBatch") {
811
+ return jsi::Value(rt, function_map["executeBatch"]);
812
+ }
813
+ if (name == "executeBatchAsync") {
814
+ return jsi::Value(rt, function_map["executeBatchAsync"]);
815
+ }
816
+ if (name == "prepareStatement") {
817
+ return jsi::Value(rt, function_map["prepareStatement"]);
818
+ }
819
+ if (name == "getDbPath") {
820
+ return jsi::Value(rt, function_map["getDbPath"]);
821
+ }
822
+ #ifdef OP_SQLITE_USE_LIBSQL
823
+ if (name == "loadFile") {
824
+ return HOSTFN("loadFile", 0) {
825
+ throw std::runtime_error("[op-sqlite] Load file not implemented");
826
+ });
827
+ }
828
+ if (name == "updateHook") {
829
+ return HOSTFN("updateHook", 0) {
830
+ throw std::runtime_error("[op-sqlite] Hooks not supported in libsql");
831
+ });
832
+ }
833
+ if (name == "commitHook") {
834
+ return HOSTFN("commitHook", 0) {
835
+ throw std::runtime_error("[op-sqlite] Hooks not supported in libsql");
836
+ });
837
+ }
838
+ if (name == "rollbackHook") {
839
+ return HOSTFN("rollbackHook", 0) {
840
+ throw std::runtime_error("[op-sqlite] Hooks not supported in libsql");
841
+ });
842
+ }
843
+ if (name == "loadExtension") {
844
+ return HOSTFN("loadExtension", 0) {
845
+ throw std::runtime_error("[op-sqlite] Hooks not supported in libsql");
846
+ });
847
+ }
848
+ if (name == "reactiveExecute") {
849
+ return HOSTFN("reactiveExecute", 0) {
850
+ throw std::runtime_error("[op-sqlite] Hooks not supported in libsql");
851
+ });
852
+ }
853
+ #else
854
+ if (name == "loadFile") {
855
+ return jsi::Value(rt, function_map["loadFile"]);
856
+ }
857
+ if (name == "updateHook") {
858
+ return jsi::Value(rt, function_map["updateHook"]);
859
+ }
860
+ if (name == "commitHook") {
861
+ return jsi::Value(rt, function_map["commitHook"]);
862
+ }
863
+ if (name == "rollbackHook") {
864
+ return jsi::Value(rt, function_map["rollbackHook"]);
865
+ }
866
+ if (name == "loadExtension") {
867
+ return jsi::Value(rt, function_map["loadExtension"]);
868
+ }
869
+ if (name == "reactiveExecute") {
870
+ return jsi::Value(rt, function_map["reactiveExecute"]);
871
+ }
872
+ #endif
873
+
874
+ return {};
875
+ }
876
+
877
+ void DBHostObject::set(jsi::Runtime &rt, const jsi::PropNameID &name,
878
+ const jsi::Value &value) {
879
+ throw std::runtime_error("You cannot write to this object!");
880
+ }
881
+
882
+ } // namespace opsqlite