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

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