1 #include <ejdb2.h>
2 #define NAPI_EXPERIMENTAL
3 #include "node_api.h"
4 #include <stdlib.h>
5 #include <string.h>
6 #include <pthread.h>
7 #include <errno.h>
8 #include <assert.h>
9 #include <inttypes.h>
10
11 #define STR_HELPER(x) #x
12 #define STR(x) STR_HELPER(x)
13
14 static void jn_resultset_tsf(
15 napi_env env,
16 napi_value js_add_stream,
17 void *context,
18 void *data);
19 static bool jn_throw_error(napi_env env, iwrc rc, const char *location, const char *msg);
20 static napi_value jn_create_error(napi_env env, iwrc rc, const char *location, const char *msg);
21
jn_is_exception_pending(napi_env env)22 IW_INLINE bool jn_is_exception_pending(napi_env env) {
23 bool rv = false;
24 napi_is_exception_pending(env, &rv);
25 return rv;
26 }
27
28 #define JNTHROW(env, rc, message) \
29 jn_throw_error(env, rc, "ejdb2_node.c" ":" STR(__LINE__), message)
30
31 #define JNTHROW_LAST(env) do { \
32 const napi_extended_error_info *info = 0; \
33 napi_get_last_error_info((env), &info); \
34 if (info) JNTHROW((env), 0, info->error_message); \
35 } while (0)
36
37 #define JNCHECK(ns, env) do { \
38 if (ns && ns != napi_pending_exception) { \
39 JNTHROW_LAST(env); \
40 } \
41 } while (0)
42
43 #define JNRC(env, rc) do { \
44 if (rc && !jn_is_exception_pending(env)) { \
45 JNTHROW(env, rc, 0); \
46 } \
47 } while (0)
48
49 #define JNRET(ns, env, call, res) do { \
50 ns = (call); \
51 if (ns) { \
52 if (ns != napi_pending_exception) { \
53 JNTHROW_LAST(env); \
54 } \
55 return (res); \
56 } \
57 } while (0)
58
59 #define JNGO(ns, env, call, label) do { \
60 ns = (call); \
61 if (ns) { \
62 if (ns != napi_pending_exception) { \
63 JNTHROW_LAST(env); \
64 } \
65 goto label; \
66 } \
67 } while (0)
68
jn_create_error(napi_env env,iwrc rc,const char * location,const char * msg)69 static napi_value jn_create_error(napi_env env, iwrc rc, const char *location, const char *msg) {
70 // Eg:
71 // Error [@ejdb IWRC:70002 open]: IO error with expected errno status set. (IW_ERROR_IO_ERRNO)
72 char codebuf[255];
73 const char *code = codebuf;
74 if (!msg) {
75 if (rc) {
76 msg = iwlog_ecode_explained(rc);
77 } else {
78 msg = "";
79 }
80 }
81 if (rc) {
82 iwrc_strip_code(&rc);
83 if (location) {
84 snprintf(codebuf, sizeof(codebuf), "@ejdb IWRC:%" PRId64 " %s", rc, location);
85 } else {
86 snprintf(codebuf, sizeof(codebuf), "@ejdb IWRC:%" PRId64, rc);
87 }
88 } else {
89 code = "@ejdb";
90 }
91 napi_status ns;
92 napi_value vcode, vmsg, verr = 0;
93 JNGO(ns, env, napi_create_string_utf8(env, code, NAPI_AUTO_LENGTH, &vcode), finish);
94 JNGO(ns, env, napi_create_string_utf8(env, msg, NAPI_AUTO_LENGTH, &vmsg), finish);
95 JNGO(ns, env, napi_create_error(env, vcode, vmsg, &verr), finish);
96
97 finish:
98 return verr;
99 }
100
jn_throw_error(napi_env env,iwrc rc,const char * location,const char * msg)101 IW_INLINE bool jn_throw_error(napi_env env, iwrc rc, const char *location, const char *msg) {
102 napi_value verr = jn_create_error(env, rc, location, msg);
103 return verr && napi_throw(env, verr) == napi_ok;
104 }
105
106 typedef enum {
107 _JN_ERROR_START = (IW_ERROR_START + 15000UL + 6000),
108 JN_ERROR_INVALID_NATIVE_CALL_ARGS, /**< Invalid native function call args (JN_ERROR_INVALID_NATIVE_CALL_ARGS) */
109 JN_ERROR_INVALID_STATE, /**< Invalid native extension state (JN_ERROR_INVALID_STATE) */
110 JN_ERROR_QUERY_IN_USE,
111 /**< Query object is in use by active async iteration, and cannot be changed
112 (JN_ERROR_QUERY_IN_USE) */
113 JN_ERROR_NAPI, /*< N-API Error (JN_ERROR_NAPI) */
114 _JN_ERROR_END,
115 } jn_ecode_t;
116
117 typedef struct JBN {
118 EJDB db;
119 IWPOOL *pool;
120 EJDB_OPTS opts;
121 napi_threadsafe_function resultset_tsf;
122 } *JBN;
123
124 typedef struct JNWORK {
125 iwrc rc; // RC error
126 napi_status ns;
127 napi_deferred deferred;
128 napi_async_work async_work;
129 const char *async_resource;
130 void *unwrapped;
131 void *data;
132 void (*release_data)(napi_env env, struct JNWORK *w);
133 IWPOOL *pool;
134 } *JNWORK;
135
136 // Globals
137
138 static napi_ref k_vadd_streamfn_ref;
139
jn_get_ref(napi_env env,napi_ref ref)140 IW_INLINE napi_value jn_get_ref(napi_env env, napi_ref ref) {
141 napi_value ret;
142 napi_status ns;
143 JNRET(ns, env, napi_get_reference_value(env, ref, &ret), 0);
144 return ret;
145 }
146
jn_null(napi_env env)147 IW_INLINE napi_value jn_null(napi_env env) {
148 napi_value ret;
149 napi_status ns;
150 JNRET(ns, env, napi_get_null(env, &ret), 0);
151 return ret;
152 }
153
jn_global(napi_env env)154 IW_INLINE napi_value jn_global(napi_env env) {
155 napi_value ret;
156 napi_status ns;
157 JNRET(ns, env, napi_get_global(env, &ret), 0);
158 return ret;
159 }
160
jn_undefined(napi_env env)161 IW_INLINE napi_value jn_undefined(napi_env env) {
162 napi_value ret;
163 napi_status ns;
164 JNRET(ns, env, napi_get_undefined(env, &ret), 0);
165 return ret;
166 }
167
jn_is_null(napi_env env,napi_value val)168 IW_INLINE bool jn_is_null(napi_env env, napi_value val) {
169 bool bv = false;
170 napi_value rv = jn_null(env);
171 if (!rv) {
172 return false;
173 }
174 napi_strict_equals(env, val, rv, &bv);
175 return bv;
176 }
177
jn_is_undefined(napi_env env,napi_value val)178 IW_INLINE bool jn_is_undefined(napi_env env, napi_value val) {
179 bool bv = false;
180 napi_value rv = jn_undefined(env);
181 if (!rv) {
182 return false;
183 }
184 napi_strict_equals(env, val, rv, &bv);
185 return bv;
186 }
187
jn_is_null_or_undefined(napi_env env,napi_value val)188 IW_INLINE bool jn_is_null_or_undefined(napi_env env, napi_value val) {
189 return jn_is_null(env, val) || jn_is_undefined(env, val);
190 }
191
jn_create_string(napi_env env,const char * str)192 IW_INLINE napi_value jn_create_string(napi_env env, const char *str) {
193 napi_value ret;
194 napi_status ns;
195 JNRET(ns, env, napi_create_string_utf8(env, str, NAPI_AUTO_LENGTH, &ret), 0);
196 return ret;
197 }
198
jn_create_int64(napi_env env,int64_t val)199 IW_INLINE napi_value jn_create_int64(napi_env env, int64_t val) {
200 napi_value ret;
201 napi_status ns;
202 JNRET(ns, env, napi_create_int64(env, val, &ret), 0);
203 return ret;
204 }
205
jn_string(napi_env env,napi_value val_,IWPOOL * pool,bool nulls,bool coerce,iwrc * rcp)206 static char *jn_string(napi_env env, napi_value val_, IWPOOL *pool, bool nulls, bool coerce, iwrc *rcp) {
207 *rcp = 0;
208 size_t len = 0;
209 napi_status ns = 0;
210 napi_value val = val_;
211 if (nulls && jn_is_null_or_undefined(env, val)) {
212 return 0;
213 }
214 if (coerce) {
215 ns = napi_coerce_to_string(env, val, &val);
216 if (ns) {
217 *rcp = JN_ERROR_NAPI;
218 JNCHECK(ns, env);
219 return 0;
220 }
221 }
222 ns = napi_get_value_string_utf8(env, val, 0, 0, &len);
223 if (ns) {
224 *rcp = JN_ERROR_NAPI;
225 JNCHECK(ns, env);
226 return 0;
227 }
228 if (!len) {
229 return 0;
230 }
231 char *buf = iwpool_alloc(len + 1, pool);
232 if (!buf) {
233 *rcp = iwrc_set_errno(IW_ERROR_ALLOC, errno);
234 return 0;
235 }
236 ns = napi_get_value_string_utf8(env, val, buf, len + 1, &len);
237 if (ns) {
238 *rcp = JN_ERROR_NAPI;
239 JNCHECK(ns, env);
240 return 0;
241 }
242 return buf;
243 }
244
jn_string_at(napi_env env,IWPOOL * pool,napi_value arr,bool nulls,bool coerce,uint32_t idx,iwrc * rcp)245 static char *jn_string_at(
246 napi_env env, IWPOOL *pool, napi_value arr,
247 bool nulls, bool coerce, uint32_t idx, iwrc *rcp) {
248 *rcp = 0;
249 napi_value el;
250 napi_status ns = napi_get_element(env, arr, idx, &el);
251 if (ns) {
252 *rcp = JN_ERROR_NAPI;
253 JNCHECK(ns, env);
254 return 0;
255 }
256 return jn_string(env, el, pool, nulls, coerce, rcp);
257 }
258
jn_int(napi_env env,napi_value val_,bool nulls,bool coerce,iwrc * rcp)259 static int64_t jn_int(napi_env env, napi_value val_, bool nulls, bool coerce, iwrc *rcp) {
260 *rcp = 0;
261 napi_status ns = 0;
262 napi_value val = val_;
263 if (nulls && jn_is_null_or_undefined(env, val)) {
264 return 0;
265 }
266 if (coerce) {
267 ns = napi_coerce_to_number(env, val, &val);
268 if (ns) {
269 *rcp = JN_ERROR_NAPI;
270 JNCHECK(ns, env);
271 return 0;
272 }
273 }
274 int64_t rv = 0;
275 ns = napi_get_value_int64(env, val, &rv);
276 if (ns) {
277 *rcp = JN_ERROR_NAPI;
278 JNCHECK(ns, env);
279 return 0;
280 }
281 return rv;
282 }
283
jn_int_at(napi_env env,napi_value arr,bool nulls,bool coerce,uint32_t idx,iwrc * rcp)284 static int64_t jn_int_at(napi_env env, napi_value arr, bool nulls, bool coerce, uint32_t idx, iwrc *rcp) {
285 *rcp = 0;
286 napi_value el;
287 napi_status ns = napi_get_element(env, arr, idx, &el);
288 if (ns) {
289 *rcp = JN_ERROR_NAPI;
290 JNCHECK(ns, env);
291 return 0;
292 }
293 return jn_int(env, el, nulls, coerce, rcp);
294 }
295
jn_double(napi_env env,napi_value val_,bool nulls,bool coerce,iwrc * rcp)296 static double jn_double(napi_env env, napi_value val_, bool nulls, bool coerce, iwrc *rcp) {
297 *rcp = 0;
298 napi_status ns = 0;
299 napi_value val = val_;
300 if (nulls && jn_is_null_or_undefined(env, val)) {
301 return 0;
302 }
303 if (coerce) {
304 ns = napi_coerce_to_number(env, val, &val);
305 if (ns) {
306 *rcp = JN_ERROR_NAPI;
307 JNCHECK(ns, env);
308 return 0;
309 }
310 }
311 double rv = 0;
312 ns = napi_get_value_double(env, val, &rv);
313 if (ns) {
314 *rcp = JN_ERROR_NAPI;
315 JNCHECK(ns, env);
316 return 0;
317 }
318 return rv;
319 }
320
jn_bool(napi_env env,napi_value val_,bool nulls,bool coerce,iwrc * rcp)321 static bool jn_bool(napi_env env, napi_value val_, bool nulls, bool coerce, iwrc *rcp) {
322 *rcp = 0;
323 napi_status ns = 0;
324 napi_value val = val_;
325 if (nulls && jn_is_null_or_undefined(env, val)) {
326 return false;
327 }
328 if (coerce) {
329 ns = napi_coerce_to_bool(env, val, &val);
330 if (ns) {
331 *rcp = JN_ERROR_NAPI;
332 JNCHECK(ns, env);
333 return false;
334 }
335 }
336 bool rv = false;
337 ns = napi_get_value_bool(env, val, &rv);
338 if (ns) {
339 *rcp = JN_ERROR_NAPI;
340 JNCHECK(ns, env);
341 return false;
342 }
343 return rv;
344 }
345
jn_bool_at(napi_env env,napi_value arr,bool nulls,bool coerce,uint32_t idx,iwrc * rcp)346 static bool jn_bool_at(napi_env env, napi_value arr, bool nulls, bool coerce, uint32_t idx, iwrc *rcp) {
347 *rcp = 0;
348 napi_value el;
349 napi_status ns = napi_get_element(env, arr, idx, &el);
350 if (ns) {
351 *rcp = JN_ERROR_NAPI;
352 JNCHECK(ns, env);
353 return false;
354 }
355 return jn_bool(env, el, nulls, coerce, rcp);
356 }
357
jb_jbn_alloc(JBN * vp)358 static iwrc jb_jbn_alloc(JBN *vp) {
359 *vp = 0;
360 IWPOOL *pool = iwpool_create(255);
361 if (!pool) {
362 return iwrc_set_errno(IW_ERROR_ALLOC, errno);
363 }
364 JBN v = iwpool_calloc(sizeof(*v), pool);
365 if (!v) {
366 iwrc rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
367 iwpool_destroy(pool);
368 return rc;
369 }
370 v->pool = pool;
371 *vp = v;
372 return 0;
373 }
374
jn_jbn_destroy(napi_env env,JBN * vp)375 static void jn_jbn_destroy(napi_env env, JBN *vp) {
376 if (!vp || !*vp) {
377 return;
378 }
379 JBN v = *vp;
380 if (v->db) {
381 iwrc rc = ejdb_close(&v->db);
382 if (rc) {
383 iwlog_ecode_error3(rc);
384 }
385 JNRC(env, rc);
386 }
387 if (v->resultset_tsf) {
388 napi_release_threadsafe_function(v->resultset_tsf, napi_tsfn_abort);
389 }
390 if (v->pool) {
391 iwpool_destroy(v->pool);
392 }
393 *vp = 0;
394 }
395
jn_work_create(iwrc * rcp)396 static JNWORK jn_work_create(iwrc *rcp) {
397 *rcp = 0;
398 IWPOOL *pool = iwpool_create(255);
399 if (!pool) {
400 *rcp = iwrc_set_errno(IW_ERROR_ALLOC, errno);
401 return 0;
402 }
403 JNWORK w = iwpool_calloc(sizeof(*w), pool);
404 if (!w) {
405 *rcp = iwrc_set_errno(IW_ERROR_ALLOC, errno);
406 iwpool_destroy(pool);
407 return 0;
408 }
409 w->pool = pool;
410 return w;
411 }
412
jn_work_alloc_data(size_t siz,JNWORK work,iwrc * rcp)413 static void *jn_work_alloc_data(size_t siz, JNWORK work, iwrc *rcp) {
414 *rcp = 0;
415 work->data = iwpool_calloc(siz, work->pool);
416 if (!work->data) {
417 *rcp = iwrc_set_errno(IW_ERROR_ALLOC, errno);
418 }
419 return work->data;
420 }
421
jn_work_destroy(napi_env env,JNWORK * wp)422 void jn_work_destroy(napi_env env, JNWORK *wp) {
423 if (!wp || !*wp) {
424 return;
425 }
426 JNWORK w = *wp;
427 if (w->deferred) {
428 // Reject promise with undefined value
429 napi_reject_deferred(env, w->deferred, jn_undefined(env));
430 }
431 if (w->async_work) {
432 napi_delete_async_work(env, w->async_work);
433 }
434 if (w->release_data) {
435 w->release_data(env, w);
436 }
437 if (w->pool) {
438 iwpool_destroy(w->pool);
439 }
440 *wp = 0;
441 }
442
jn_ejdb2impl_finalize(napi_env env,void * data,void * hint)443 static void jn_ejdb2impl_finalize(napi_env env, void *data, void *hint) {
444 JBN jbn = data;
445 jn_jbn_destroy(env, &jbn);
446 }
447
448 // string array opts
jn_ejdb2impl_ctor(napi_env env,napi_callback_info info)449 static napi_value jn_ejdb2impl_ctor(napi_env env, napi_callback_info info) {
450 iwrc rc = 0;
451 napi_status ns = 0;
452
453 size_t argc = 1;
454 bool bv = false;
455 uint32_t ulv = 0;
456
457 JBN jbn = 0;
458 napi_value varr, this;
459 void *data;
460
461 JNGO(ns, env, napi_get_cb_info(env, info, &argc, &varr, &this, &data), finish);
462 if (argc != 1) {
463 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
464 JNRC(env, rc);
465 goto finish;
466 }
467
468 napi_is_array(env, varr, &bv);
469 if (!bv) {
470 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
471 JNRC(env, rc);
472 goto finish;
473 }
474 JNGO(ns, env, napi_get_array_length(env, varr, &ulv), finish);
475 if (ulv != 16) {
476 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
477 JNRC(env, rc);
478 goto finish;
479 }
480
481 rc = jb_jbn_alloc(&jbn);
482 RCGO(rc, finish);
483
484 // opts.kv.path // non null
485 // opts.kv.oflags // non null
486 // opts.kv.wal.enabled // non null
487 // opts.kv.wal.check_crc_on_checkpoint
488 // opts.kv.wal.checkpoint_buffer_sz
489 // opts.kv.wal.checkpoint_timeout_sec
490 // opts.kv.wal.savepoint_timeout_sec
491 // opts.kv.wal.wal_buffer_sz
492 // opts.document_buffer_sz
493 // opts.sort_buffer_sz
494 // opts.http.enabled
495 // opts.http.access_token
496 // opts.http.bind
497 // opts.http.max_body_size
498 // opts.http.port
499 // opts.http.read_anon
500
501 argc = 0;
502 jbn->opts.kv.path = jn_string_at(env, jbn->pool, varr, false, false, argc++, &rc);
503 RCGO(rc, finish);
504 jbn->opts.kv.oflags = jn_int_at(env, varr, false, false, argc++, &rc);
505 RCGO(rc, finish);
506 jbn->opts.kv.wal.enabled = jn_bool_at(env, varr, false, false, argc++, &rc);
507 RCGO(rc, finish);
508 jbn->opts.kv.wal.check_crc_on_checkpoint = jn_bool_at(env, varr, true, true, argc++, &rc);
509 RCGO(rc, finish);
510 jbn->opts.kv.wal.checkpoint_buffer_sz = jn_int_at(env, varr, true, false, argc++, &rc);
511 RCGO(rc, finish);
512 jbn->opts.kv.wal.checkpoint_timeout_sec = jn_int_at(env, varr, true, false, argc++, &rc);
513 RCGO(rc, finish);
514 jbn->opts.kv.wal.savepoint_timeout_sec = jn_int_at(env, varr, true, false, argc++, &rc);
515 RCGO(rc, finish);
516 jbn->opts.kv.wal.wal_buffer_sz = jn_int_at(env, varr, true, false, argc++, &rc);
517 RCGO(rc, finish);
518 jbn->opts.document_buffer_sz = jn_int_at(env, varr, true, false, argc++, &rc);
519 RCGO(rc, finish);
520 jbn->opts.sort_buffer_sz = jn_int_at(env, varr, true, false, argc++, &rc);
521 RCGO(rc, finish);
522 jbn->opts.http.enabled = jn_bool_at(env, varr, true, true, argc++, &rc);
523 RCGO(rc, finish);
524 jbn->opts.http.access_token = jn_string_at(env, jbn->pool, varr, true, false, argc++, &rc);
525 RCGO(rc, finish);
526 jbn->opts.http.bind = jn_string_at(env, jbn->pool, varr, true, false, argc++, &rc);
527 RCGO(rc, finish);
528 jbn->opts.http.max_body_size = jn_int_at(env, varr, true, false, argc++, &rc);
529 RCGO(rc, finish);
530 jbn->opts.http.port = jn_int_at(env, varr, true, false, argc++, &rc);
531 RCGO(rc, finish);
532 jbn->opts.http.read_anon = jn_bool_at(env, varr, true, true, argc++, &rc);
533 RCGO(rc, finish);
534
535 jbn->opts.kv.file_lock_fail_fast = true;
536 jbn->opts.no_wal = !jbn->opts.kv.wal.enabled;
537 jbn->opts.http.blocking = false;
538 jbn->opts.http.access_token_len = jbn->opts.http.access_token ? strlen(jbn->opts.http.access_token) : 0;
539
540 // Result-set thread-save function initialization
541 napi_value vadd_streamfn;
542 JNGO(ns, env, napi_get_reference_value(env, k_vadd_streamfn_ref, &vadd_streamfn), finish);
543 JNGO(ns, env, napi_create_threadsafe_function(
544 env, // napi_env env,
545 vadd_streamfn, // napi_value func,
546 0, // napi_value async_resource,
547 jn_create_string(env, "jn_add_stream_result"), // napi_value async_resource_name,
548 1, // size_t max_queue_size,
549 1, // size_t initial_thread_count,
550 0, // void* thread_finalize_data,
551 0, // napi_finalize thread_finalize_cb,
552 0, // void* context,
553 jn_resultset_tsf, // napi_threadsafe_function_call_js call_js_cb,
554 &jbn->resultset_tsf // napi_threadsafe_function* result
555 ), finish);
556
557 // Wrap class instance
558 JNGO(ns, env, napi_wrap(env, this, jbn, jn_ejdb2impl_finalize, 0, 0), finish);
559
560 finish:
561 if (jn_is_exception_pending(env) || rc) {
562 JNRC(env, rc);
563 if (jbn) {
564 jn_jbn_destroy(env, &jbn);
565 }
566 return 0;
567 }
568 return this;
569 }
570
571 #define JNFUNC(func) {#func, 0, jn_ ## func, 0, 0, 0, napi_default, 0 }
572 #define JNVAL(name, value) {#name, 0, 0, 0, 0, value, napi_default, 0 }
573
jn_resolve_pending_errors(napi_env env,napi_status ns,JNWORK work)574 bool jn_resolve_pending_errors(napi_env env, napi_status ns, JNWORK work) {
575 assert(work);
576 if (!work->deferred) {
577 return true;
578 }
579 bool pending = jn_is_exception_pending(env);
580 if (!(work->rc || ns || pending)) {
581 return false;
582 }
583 napi_value ex;
584 if (pending && (!work->rc || (work->rc == JN_ERROR_NAPI))) {
585 ns = napi_get_and_clear_last_exception(env, &ex);
586 if (ns == napi_ok) {
587 napi_reject_deferred(env, work->deferred, ex);
588 }
589 } else {
590 napi_value verr;
591 const napi_extended_error_info *info = 0;
592 if (ns) {
593 napi_get_last_error_info(env, &info);
594 }
595 if (pending) {
596 napi_get_and_clear_last_exception(env, &ex);
597 }
598 verr = jn_create_error(env, work->rc, work->async_resource, info ? info->error_message : 0);
599 if (verr) {
600 napi_reject_deferred(env, work->deferred, verr);
601 }
602 }
603 work->deferred = 0;
604 return true;
605 }
606
jn_launch_promise(napi_env env,napi_callback_info info,const char * async_resource_name,napi_async_execute_callback execute,napi_async_complete_callback complete,JNWORK work)607 static napi_value jn_launch_promise(
608 napi_env env,
609 napi_callback_info info,
610 const char *async_resource_name,
611 napi_async_execute_callback execute,
612 napi_async_complete_callback complete,
613 JNWORK work) {
614 size_t argc = 0;
615 napi_status ns = 0;
616 napi_value promise = 0, this, awork;
617 void *data;
618
619 work->async_resource = async_resource_name;
620 JNGO(ns, env, napi_get_cb_info(env, info, &argc, 0, &this, &data), finish);
621 JNGO(ns, env, napi_unwrap(env, this, &work->unwrapped), finish);
622 JNGO(ns, env, napi_create_promise(env, &work->deferred, &promise), finish);
623 JNGO(ns, env, napi_create_string_utf8(env, async_resource_name, NAPI_AUTO_LENGTH, &awork), finish);
624 JNGO(ns, env, napi_create_async_work(env, 0, awork, execute, complete, work, &work->async_work), finish);
625 JNGO(ns, env, napi_queue_async_work(env, work->async_work), finish);
626
627 finish:
628 if (ns || jn_is_exception_pending(env)) {
629 if (work) {
630 jn_resolve_pending_errors(env, ns, work);
631 jn_work_destroy(env, &work);
632 }
633 }
634 return promise;
635 }
636
637 // ---------------- EJDB2.open()
638
jn_open_execute(napi_env env,void * data)639 static void jn_open_execute(napi_env env, void *data) {
640 JNWORK work = data;
641 JBN jbn = work->unwrapped;
642 if (jbn->db) {
643 return; // Database is already opened
644 }
645 work->rc = ejdb_open(&jbn->opts, &jbn->db);
646 }
647
jn_open_complete(napi_env env,napi_status ns,void * data)648 static void jn_open_complete(napi_env env, napi_status ns, void *data) {
649 JNWORK work = data;
650 if (jn_resolve_pending_errors(env, ns, work)) {
651 goto finish;
652 }
653 JNGO(ns, env, napi_resolve_deferred(env, work->deferred, jn_undefined(env)), finish);
654 work->deferred = 0;
655
656 finish:
657 jn_work_destroy(env, &work);
658 }
659
jn_open(napi_env env,napi_callback_info info)660 static napi_value jn_open(napi_env env, napi_callback_info info) {
661 iwrc rc;
662 JNWORK work = jn_work_create(&rc);
663 if (rc) {
664 JNRC(env, rc);
665 return jn_undefined(env);
666 }
667 napi_value ret = jn_launch_promise(env, info, "open", jn_open_execute, jn_open_complete, work);
668 return ret ? ret : jn_undefined(env);
669 }
670
671 // ---------------- EJDB2.close()
672
jn_close_execute(napi_env env,void * data)673 static void jn_close_execute(napi_env env, void *data) {
674 JNWORK work = data;
675 JBN jbn = work->unwrapped;
676 if (!jbn->db) {
677 return;
678 }
679 work->rc = ejdb_close(&jbn->db);
680 }
681
jn_close_complete(napi_env env,napi_status ns,void * data)682 static void jn_close_complete(napi_env env, napi_status ns, void *data) {
683 JNWORK work = data;
684 JBN jbn = work->unwrapped;
685 if (jn_resolve_pending_errors(env, ns, work)) {
686 goto finish;
687 }
688 JNGO(ns, env, napi_resolve_deferred(env, work->deferred, jn_undefined(env)), finish);
689 work->deferred = 0;
690
691 finish:
692 if (jbn->resultset_tsf) {
693 napi_release_threadsafe_function(jbn->resultset_tsf, napi_tsfn_abort);
694 jbn->resultset_tsf = 0;
695 }
696 jn_work_destroy(env, &work);
697 }
698
jn_close(napi_env env,napi_callback_info info)699 static napi_value jn_close(napi_env env, napi_callback_info info) {
700 iwrc rc;
701 JNWORK work = jn_work_create(&rc);
702 if (rc) {
703 JNRC(env, rc);
704 return jn_undefined(env);
705 }
706 napi_value ret = jn_launch_promise(env, info, "close", jn_close_execute, jn_close_complete, work);
707 return ret ? ret : jn_undefined(env);
708 }
709
710 // ---------------- EJDB2.put/patch()
711
712 struct JNPUT_DATA {
713 int64_t id;
714 const char *coll;
715 const char *json;
716 bool patch;
717 };
718
jn_put_execute(napi_env env,void * data)719 static void jn_put_execute(napi_env env, void *data) {
720 JBL jbl = 0;
721 JNWORK work = data;
722 JBN jbn = work->unwrapped;
723 if (!jbn->db) {
724 work->rc = JN_ERROR_INVALID_STATE;
725 goto finish;
726 }
727 struct JNPUT_DATA *wdata = work->data;
728 if (!wdata->patch) {
729 work->rc = jbl_from_json(&jbl, wdata->json);
730 RCGO(work->rc, finish);
731 }
732 if (wdata->id > 0) {
733 if (wdata->patch) {
734 work->rc = ejdb_patch(jbn->db, wdata->coll, wdata->json, wdata->id);
735 } else {
736 work->rc = ejdb_put(jbn->db, wdata->coll, jbl, wdata->id);
737 }
738 } else {
739 work->rc = ejdb_put_new(jbn->db, wdata->coll, jbl, &wdata->id);
740 }
741 finish:
742 if (jbl) {
743 jbl_destroy(&jbl);
744 }
745 }
746
jn_put_complete(napi_env env,napi_status ns,void * data)747 static void jn_put_complete(napi_env env, napi_status ns, void *data) {
748 napi_value rv;
749 JNWORK work = data;
750 if (jn_resolve_pending_errors(env, ns, work)) {
751 goto finish;
752 }
753 struct JNPUT_DATA *wdata = work->data;
754 ns = napi_create_int64(env, wdata->id, &rv);
755 if (ns) {
756 jn_resolve_pending_errors(env, ns, work);
757 goto finish;
758 }
759 JNGO(ns, env, napi_resolve_deferred(env, work->deferred, rv), finish);
760 work->deferred = 0;
761
762 finish:
763 jn_work_destroy(env, &work);
764 }
765
766 // collection, json, id
jn_put_patch(napi_env env,napi_callback_info info,bool patch,bool upsert)767 static napi_value jn_put_patch(napi_env env, napi_callback_info info, bool patch, bool upsert) {
768 iwrc rc = 0;
769 napi_status ns = 0;
770 napi_value this, argv[3] = { 0 };
771 size_t argc = sizeof(argv) / sizeof(argv[0]);
772 void *data;
773 napi_value ret = 0;
774 JNWORK work = jn_work_create(&rc);
775 RCGO(rc, finish);
776
777 JNGO(ns, env, napi_get_cb_info(env, info, &argc, argv, &this, &data), finish);
778 if (argc != sizeof(argv) / sizeof(argv[0])) {
779 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
780 goto finish;
781 }
782
783 struct JNPUT_DATA *wdata = jn_work_alloc_data(sizeof(*wdata), work, &rc);
784 RCGO(rc, finish);
785 wdata->patch = patch;
786 wdata->coll = jn_string(env, argv[0], work->pool, false, true, &rc);
787 RCGO(rc, finish);
788 wdata->json = jn_string(env, argv[1], work->pool, false, false, &rc);
789 RCGO(rc, finish);
790 wdata->id = jn_int(env, argv[2], true, true, &rc);
791 RCGO(rc, finish);
792
793 if ((wdata->id < 1) && wdata->patch) {
794 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
795 goto finish;
796 }
797 ret = jn_launch_promise(env, info, wdata->patch ? "patch" : "put", jn_put_execute, jn_put_complete, work);
798
799 finish:
800 if (rc) {
801 JNRC(env, rc);
802 if (work) {
803 jn_work_destroy(env, &work);
804 }
805 }
806 return ret ? ret : jn_undefined(env);
807 }
808
jn_put(napi_env env,napi_callback_info info)809 static napi_value jn_put(napi_env env, napi_callback_info info) {
810 return jn_put_patch(env, info, false, false);
811 }
812
jn_patch(napi_env env,napi_callback_info info)813 static napi_value jn_patch(napi_env env, napi_callback_info info) {
814 return jn_put_patch(env, info, true, false);
815 }
816
jn_patch_or_put(napi_env env,napi_callback_info info)817 static napi_value jn_patch_or_put(napi_env env, napi_callback_info info) {
818 return jn_put_patch(env, info, true, true);
819 }
820
821 // ---------------- EJDB2.get()
822
823 struct JNGET_DATA {
824 int64_t id;
825 const char *coll;
826 JBL jbl;
827 };
828
jn_get_data_destroy(napi_env env,JNWORK w)829 static void jn_get_data_destroy(napi_env env, JNWORK w) {
830 struct JNGET_DATA *wdata = w->data;
831 if (wdata && wdata->jbl) {
832 jbl_destroy(&wdata->jbl);
833 }
834 }
835
jn_get_execute(napi_env env,void * data)836 static void jn_get_execute(napi_env env, void *data) {
837 JNWORK work = data;
838 JBN jbn = work->unwrapped;
839 if (!jbn->db) {
840 work->rc = JN_ERROR_INVALID_STATE;
841 return;
842 }
843 struct JNGET_DATA *wdata = work->data;
844 work->rc = ejdb_get(jbn->db, wdata->coll, wdata->id, &wdata->jbl);
845 }
846
jn_get_complete(napi_env env,napi_status ns,void * data)847 static void jn_get_complete(napi_env env, napi_status ns, void *data) {
848 napi_value rv;
849 JNWORK work = data;
850 if (jn_resolve_pending_errors(env, ns, work)) {
851 goto finish;
852 }
853 struct JNGET_DATA *wdata = work->data;
854 IWXSTR *xstr = iwxstr_new2(jbl_size(wdata->jbl) * 2);
855 if (!xstr) {
856 work->rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
857 goto finish0;
858 }
859 work->rc = jbl_as_json(wdata->jbl, jbl_xstr_json_printer, xstr, 0);
860 RCGO(work->rc, finish0);
861 JNGO(ns, env, napi_create_string_utf8(env, iwxstr_ptr(xstr), iwxstr_size(xstr), &rv), finish0);
862 JNGO(ns, env, napi_resolve_deferred(env, work->deferred, rv), finish0);
863 work->deferred = 0;
864
865 finish0:
866 if (xstr) {
867 iwxstr_destroy(xstr);
868 }
869 if (work->rc || ns) {
870 jn_resolve_pending_errors(env, ns, work);
871 }
872 finish:
873 jn_work_destroy(env, &work);
874 }
875
jn_get(napi_env env,napi_callback_info info)876 static napi_value jn_get(napi_env env, napi_callback_info info) {
877 iwrc rc = 0;
878 napi_status ns;
879 napi_value this, argv[2];
880 napi_value ret = 0;
881 size_t argc = sizeof(argv) / sizeof(argv[0]);
882 void *data;
883
884 JNWORK work = jn_work_create(&rc);
885 RCGO(rc, finish);
886
887 JNGO(ns, env, napi_get_cb_info(env, info, &argc, argv, &this, &data), finish);
888 if (argc != sizeof(argv) / sizeof(argv[0])) {
889 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
890 goto finish;
891 }
892
893 struct JNGET_DATA *wdata = jn_work_alloc_data(sizeof(*wdata), work, &rc);
894 RCGO(rc, finish);
895 work->release_data = jn_get_data_destroy;
896 wdata->coll = jn_string(env, argv[0], work->pool, false, false, &rc);
897 RCGO(rc, finish);
898 wdata->id = jn_int(env, argv[1], false, false, &rc);
899 RCGO(rc, finish);
900
901 ret = jn_launch_promise(env, info, "get", jn_get_execute, jn_get_complete, work);
902
903 finish:
904 if (rc) {
905 JNRC(env, rc);
906 if (work) {
907 jn_work_destroy(env, &work);
908 }
909 }
910 return ret ? ret : jn_undefined(env);
911 }
912
913 // ---------------- EJDB2.del()
914
jn_del_execute(napi_env env,void * data)915 static void jn_del_execute(napi_env env, void *data) {
916 JNWORK work = data;
917 JBN jbn = work->unwrapped;
918 if (!jbn->db) {
919 work->rc = JN_ERROR_INVALID_STATE;
920 return;
921 }
922 struct JNGET_DATA *wdata = work->data;
923 work->rc = ejdb_del(jbn->db, wdata->coll, wdata->id);
924 }
925
jn_del_complete(napi_env env,napi_status ns,void * data)926 static void jn_del_complete(napi_env env, napi_status ns, void *data) {
927 JNWORK work = data;
928 if (jn_resolve_pending_errors(env, ns, work)) {
929 goto finish;
930 }
931 JNGO(ns, env, napi_resolve_deferred(env, work->deferred, jn_undefined(env)), finish);
932 work->deferred = 0;
933
934 finish:
935 jn_work_destroy(env, &work);
936 }
937
jn_del(napi_env env,napi_callback_info info)938 static napi_value jn_del(napi_env env, napi_callback_info info) {
939 iwrc rc = 0;
940 napi_status ns;
941 napi_value this, argv[2];
942 napi_value ret = 0;
943 size_t argc = sizeof(argv) / sizeof(argv[0]);
944 void *data;
945
946 JNWORK work = jn_work_create(&rc);
947 RCGO(rc, finish);
948
949 JNGO(ns, env, napi_get_cb_info(env, info, &argc, argv, &this, &data), finish);
950 if (argc != sizeof(argv) / sizeof(argv[0])) {
951 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
952 goto finish;
953 }
954
955 struct JNGET_DATA *wdata = jn_work_alloc_data(sizeof(*wdata), work, &rc);
956 RCGO(rc, finish);
957 wdata->coll = jn_string(env, argv[0], work->pool, false, false, &rc);
958 RCGO(rc, finish);
959 wdata->id = jn_int(env, argv[1], false, false, &rc);
960 RCGO(rc, finish);
961
962 ret = jn_launch_promise(env, info, "del", jn_del_execute, jn_del_complete, work);
963
964 finish:
965 if (rc) {
966 JNRC(env, rc);
967 if (work) {
968 jn_work_destroy(env, &work);
969 }
970 }
971 return ret ? ret : jn_undefined(env);
972 }
973
974 // ---------------- EJDB2.renameCollection()
975
976 struct JNRENAME_DATA {
977 const char *old_name;
978 const char *new_name;
979 };
980
jn_rename_collection_execute(napi_env env,void * data)981 static void jn_rename_collection_execute(napi_env env, void *data) {
982 JNWORK work = data;
983 JBN jbn = work->unwrapped;
984 if (!jbn->db) {
985 work->rc = JN_ERROR_INVALID_STATE;
986 return;
987 }
988 struct JNRENAME_DATA *wdata = work->data;
989 work->rc = ejdb_rename_collection(jbn->db, wdata->old_name, wdata->new_name);
990 }
991
jn_rename_collection_complete(napi_env env,napi_status ns,void * data)992 static void jn_rename_collection_complete(napi_env env, napi_status ns, void *data) {
993 JNWORK work = data;
994 if (jn_resolve_pending_errors(env, ns, work)) {
995 goto finish;
996 }
997 JNGO(ns, env, napi_resolve_deferred(env, work->deferred, jn_undefined(env)), finish);
998 work->deferred = 0;
999
1000 finish:
1001 jn_work_destroy(env, &work);
1002 }
1003
jn_rename_collection(napi_env env,napi_callback_info info)1004 static napi_value jn_rename_collection(napi_env env, napi_callback_info info) {
1005 iwrc rc = 0;
1006 napi_status ns;
1007 napi_value this, argv[2];
1008 napi_value ret = 0;
1009 size_t argc = sizeof(argv) / sizeof(argv[0]);
1010 void *data;
1011
1012 JNWORK work = jn_work_create(&rc);
1013 RCGO(rc, finish);
1014
1015 JNGO(ns, env, napi_get_cb_info(env, info, &argc, argv, &this, &data), finish);
1016 if (argc != sizeof(argv) / sizeof(argv[0])) {
1017 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
1018 goto finish;
1019 }
1020
1021 struct JNRENAME_DATA *wdata = jn_work_alloc_data(sizeof(*wdata), work, &rc);
1022 RCGO(rc, finish);
1023
1024 wdata->old_name = jn_string(env, argv[0], work->pool, false, false, &rc);
1025 RCGO(rc, finish);
1026
1027 wdata->new_name = jn_string(env, argv[1], work->pool, false, false, &rc);
1028 RCGO(rc, finish);
1029
1030 ret = jn_launch_promise(env, info, "rename",
1031 jn_rename_collection_execute, jn_rename_collection_complete,
1032 work);
1033 finish:
1034 if (rc) {
1035 JNRC(env, rc);
1036 if (work) {
1037 jn_work_destroy(env, &work);
1038 }
1039 }
1040 return ret ? ret : jn_undefined(env);
1041 }
1042
1043 // ---------------- EJDB2.info()
1044
jn_info_execute(napi_env env,void * data)1045 static void jn_info_execute(napi_env env, void *data) {
1046 JNWORK work = data;
1047 JBN jbn = work->unwrapped;
1048 if (!jbn->db) {
1049 work->rc = JN_ERROR_INVALID_STATE;
1050 return;
1051 }
1052 struct JNGET_DATA *wdata = work->data;
1053 work->rc = ejdb_get_meta(jbn->db, &wdata->jbl);
1054 }
1055
jn_info_complete(napi_env env,napi_status ns,void * data)1056 static void jn_info_complete(napi_env env, napi_status ns, void *data) {
1057 napi_value rv;
1058 JNWORK work = data;
1059 if (jn_resolve_pending_errors(env, ns, work)) {
1060 goto finish;
1061 }
1062 struct JNGET_DATA *wdata = work->data;
1063 IWXSTR *xstr = iwxstr_new2(jbl_size(wdata->jbl) * 2);
1064 if (!xstr) {
1065 work->rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1066 goto finish0;
1067 }
1068 work->rc = jbl_as_json(wdata->jbl, jbl_xstr_json_printer, xstr, 0);
1069 RCGO(work->rc, finish0);
1070 JNGO(ns, env, napi_create_string_utf8(env, iwxstr_ptr(xstr), iwxstr_size(xstr), &rv), finish0);
1071
1072 JNGO(ns, env, napi_resolve_deferred(env, work->deferred, rv), finish0);
1073 work->deferred = 0;
1074
1075 finish0:
1076 if (xstr) {
1077 iwxstr_destroy(xstr);
1078 }
1079 if (work->rc || ns) {
1080 jn_resolve_pending_errors(env, ns, work);
1081 }
1082 finish:
1083 jn_work_destroy(env, &work);
1084 }
1085
jn_info(napi_env env,napi_callback_info info)1086 static napi_value jn_info(napi_env env, napi_callback_info info) {
1087 iwrc rc = 0;
1088 napi_status ns;
1089 napi_value this;
1090 napi_value ret = 0;
1091 size_t argc = 0;
1092 void *data;
1093 JNWORK work = jn_work_create(&rc);
1094 RCGO(rc, finish);
1095
1096 JNGO(ns, env, napi_get_cb_info(env, info, &argc, 0, &this, &data), finish);
1097 jn_work_alloc_data(sizeof(struct JNGET_DATA), work, &rc);
1098 RCGO(rc, finish);
1099 work->release_data = jn_get_data_destroy;
1100 ret = jn_launch_promise(env, info, "info", jn_info_execute, jn_info_complete, work);
1101
1102 finish:
1103 if (rc) {
1104 JNRC(env, rc);
1105 if (work) {
1106 jn_work_destroy(env, &work);
1107 }
1108 }
1109 return ret ? ret : jn_undefined(env);
1110 }
1111
1112 // ---------------- EJDB2.ensureIndex/removeIndex()
1113
1114 struct JNIDX_DATA {
1115 const char *coll;
1116 const char *path;
1117 ejdb_idx_mode_t mode;
1118 bool remove;
1119 };
1120
jn_index_execute(napi_env env,void * data)1121 static void jn_index_execute(napi_env env, void *data) {
1122 JNWORK work = data;
1123 JBN jbn = work->unwrapped;
1124 if (!jbn->db) {
1125 work->rc = JN_ERROR_INVALID_STATE;
1126 return;
1127 }
1128 struct JNIDX_DATA *wdata = work->data;
1129 if (wdata->remove) {
1130 work->rc = ejdb_remove_index(jbn->db, wdata->coll, wdata->path, wdata->mode);
1131 } else {
1132 work->rc = ejdb_ensure_index(jbn->db, wdata->coll, wdata->path, wdata->mode);
1133 }
1134 }
1135
jn_index_complete(napi_env env,napi_status ns,void * data)1136 static void jn_index_complete(napi_env env, napi_status ns, void *data) {
1137 JNWORK work = data;
1138 if (jn_resolve_pending_errors(env, ns, work)) {
1139 goto finish;
1140 }
1141 JNGO(ns, env, napi_resolve_deferred(env, work->deferred, jn_undefined(env)), finish);
1142 work->deferred = 0;
1143
1144 finish:
1145 jn_work_destroy(env, &work);
1146 }
1147
jn_index(napi_env env,napi_callback_info info)1148 static napi_value jn_index(napi_env env, napi_callback_info info) {
1149 iwrc rc = 0;
1150 napi_value ret = 0;
1151 napi_status ns;
1152 napi_value this, argv[4];
1153 size_t argc = sizeof(argv) / sizeof(argv[0]);
1154 void *data;
1155
1156 JNWORK work = jn_work_create(&rc);
1157 RCGO(rc, finish);
1158
1159 JNGO(ns, env, napi_get_cb_info(env, info, &argc, argv, &this, &data), finish);
1160 if (argc != sizeof(argv) / sizeof(argv[0])) {
1161 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
1162 goto finish;
1163 }
1164
1165 struct JNIDX_DATA *wdata = jn_work_alloc_data(sizeof(*wdata), work, &rc);
1166 RCGO(rc, finish);
1167 wdata->coll = jn_string(env, argv[0], work->pool, false, false, &rc);
1168 RCGO(rc, finish);
1169 wdata->path = jn_string(env, argv[1], work->pool, false, false, &rc);
1170 RCGO(rc, finish);
1171 wdata->mode = jn_int(env, argv[2], false, false, &rc);
1172 RCGO(rc, finish);
1173 wdata->remove = jn_bool(env, argv[3], false, false, &rc);
1174 RCGO(rc, finish);
1175
1176 ret = jn_launch_promise(env, info, "index", jn_index_execute, jn_index_complete, work);
1177
1178 finish:
1179 if (rc) {
1180 JNRC(env, rc);
1181 if (work) {
1182 jn_work_destroy(env, &work);
1183 }
1184 }
1185 return ret ? ret : jn_undefined(env);
1186 }
1187
1188 // ---------------- EJDB2.removeCollection()
1189
1190 struct JNRMC_DATA {
1191 const char *coll;
1192 };
1193
jn_rmcoll_execute(napi_env env,void * data)1194 static void jn_rmcoll_execute(napi_env env, void *data) {
1195 JNWORK work = data;
1196 JBN jbn = work->unwrapped;
1197 if (!jbn->db) {
1198 work->rc = JN_ERROR_INVALID_STATE;
1199 return;
1200 }
1201 struct JNRMC_DATA *wdata = work->data;
1202 work->rc = ejdb_remove_collection(jbn->db, wdata->coll);
1203 }
1204
jn_rmcoll_complete(napi_env env,napi_status ns,void * data)1205 static void jn_rmcoll_complete(napi_env env, napi_status ns, void *data) {
1206 JNWORK work = data;
1207 if (jn_resolve_pending_errors(env, ns, work)) {
1208 goto finish;
1209 }
1210 JNGO(ns, env, napi_resolve_deferred(env, work->deferred, jn_undefined(env)), finish);
1211 work->deferred = 0;
1212
1213 finish:
1214 jn_work_destroy(env, &work);
1215 }
1216
jn_rmcoll(napi_env env,napi_callback_info info)1217 static napi_value jn_rmcoll(napi_env env, napi_callback_info info) {
1218 iwrc rc = 0;
1219 napi_status ns;
1220 napi_value this, argv;
1221 napi_value ret = 0;
1222 size_t argc = 1;
1223 void *data;
1224
1225 JNWORK work = jn_work_create(&rc);
1226 RCGO(rc, finish);
1227
1228 JNGO(ns, env, napi_get_cb_info(env, info, &argc, &argv, &this, &data), finish);
1229 if (argc != 1) {
1230 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
1231 goto finish;
1232 }
1233
1234 struct JNRMC_DATA *wdata = jn_work_alloc_data(sizeof(*wdata), work, &rc);
1235 RCGO(rc, finish);
1236 wdata->coll = jn_string(env, argv, work->pool, false, false, &rc);
1237 RCGO(rc, finish);
1238
1239 ret = jn_launch_promise(env, info, "rmcoll", jn_rmcoll_execute, jn_rmcoll_complete, work);
1240
1241 finish:
1242 if (rc) {
1243 JNRC(env, rc);
1244 if (work) {
1245 jn_work_destroy(env, &work);
1246 }
1247 }
1248 return ret ? ret : jn_undefined(env);
1249 }
1250
1251 // ---------------- EJDB2.onlineBackup
1252
1253 struct JNBK_DATA {
1254 uint64_t ts;
1255 const char *file_name;
1256 };
1257
jn_online_backup_execute(napi_env env,void * data)1258 static void jn_online_backup_execute(napi_env env, void *data) {
1259 JNWORK work = data;
1260 JBN jbn = work->unwrapped;
1261 if (!jbn->db) {
1262 work->rc = JN_ERROR_INVALID_STATE;
1263 return;
1264 }
1265 struct JNBK_DATA *wdata = work->data;
1266 work->rc = ejdb_online_backup(jbn->db, &wdata->ts, wdata->file_name);
1267 }
1268
jn_online_backup_complete(napi_env env,napi_status ns,void * data)1269 static void jn_online_backup_complete(napi_env env, napi_status ns, void *data) {
1270 napi_value rv;
1271 JNWORK work = data;
1272 if (jn_resolve_pending_errors(env, ns, work)) {
1273 goto finish;
1274 }
1275 struct JNBK_DATA *wdata = work->data;
1276 ns = napi_create_int64(env, (int64_t) wdata->ts, &rv);
1277 if (ns) {
1278 jn_resolve_pending_errors(env, ns, work);
1279 goto finish;
1280 }
1281 JNGO(ns, env, napi_resolve_deferred(env, work->deferred, rv), finish);
1282 work->deferred = 0;
1283
1284 finish:
1285 jn_work_destroy(env, &work);
1286 }
1287
jn_online_backup(napi_env env,napi_callback_info info)1288 static napi_value jn_online_backup(napi_env env, napi_callback_info info) {
1289 iwrc rc = 0;
1290 napi_status ns = 0;
1291 napi_value this, argv[1] = { 0 };
1292 size_t argc = sizeof(argv) / sizeof(argv[0]);
1293 void *data;
1294 napi_value ret = 0;
1295 JNWORK work = jn_work_create(&rc);
1296 RCGO(rc, finish);
1297
1298 JNGO(ns, env, napi_get_cb_info(env, info, &argc, argv, &this, &data), finish);
1299 if (argc != sizeof(argv) / sizeof(argv[0])) {
1300 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
1301 goto finish;
1302 }
1303
1304 struct JNBK_DATA *wdata = jn_work_alloc_data(sizeof(*wdata), work, &rc);
1305 RCGO(rc, finish);
1306 wdata->file_name = jn_string(env, argv[0], work->pool, false, true, &rc);
1307 ret = jn_launch_promise(env, info, "online_backup", jn_online_backup_execute, jn_online_backup_complete, work);
1308
1309 finish:
1310 if (rc) {
1311 JNRC(env, rc);
1312 if (work) {
1313 jn_work_destroy(env, &work);
1314 }
1315 }
1316 return ret ? ret : jn_undefined(env);
1317 }
1318
1319 // ---------------- jql_init
1320
1321 typedef struct JNQL {
1322 int refs;
1323 JQL jql;
1324 } *JNQL;
1325
jn_jnql_destroy_mt(JNQL * qlp)1326 static void jn_jnql_destroy_mt(JNQL *qlp) {
1327 if (!qlp || !*qlp) {
1328 return;
1329 }
1330 JNQL ql = *qlp;
1331 if (ql->jql) {
1332 jql_destroy(&ql->jql);
1333 }
1334 free(ql);
1335 }
1336
jn_jql_finalize(napi_env env,void * data,void * hint)1337 static void jn_jql_finalize(napi_env env, void *data, void *hint) {
1338 JNQL jnql = data;
1339 if (jnql) {
1340 jn_jnql_destroy_mt(&jnql);
1341 }
1342 }
1343
1344 // JQL._impl.jql_init(this, query, collection);
jn_jql_init(napi_env env,napi_callback_info info)1345 static napi_value jn_jql_init(napi_env env, napi_callback_info info) {
1346 iwrc rc = 0;
1347 napi_status ns;
1348 napi_value argv[3], this;
1349 size_t argc = sizeof(argv) / sizeof(argv[0]);
1350 void *data;
1351 JNQL jnql = 0;
1352
1353 IWPOOL *pool = iwpool_create(255);
1354 if (!pool) {
1355 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1356 goto finish;
1357 }
1358 jnql = calloc(1, sizeof(*jnql));
1359 if (!jnql) {
1360 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1361 goto finish;
1362 }
1363
1364 JNGO(ns, env, napi_get_cb_info(env, info, &argc, argv, &this, &data), finish);
1365 if (argc != sizeof(argv) / sizeof(argv[0])) {
1366 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
1367 goto finish;
1368 }
1369
1370 const char *query = jn_string(env, argv[1], pool, false, false, &rc);
1371 RCGO(rc, finish);
1372
1373 const char *collection = jn_string(env, argv[2], pool, true, false, &rc);
1374 RCGO(rc, finish);
1375
1376 rc = jql_create2(&jnql->jql, collection, query, JQL_KEEP_QUERY_ON_PARSE_ERROR | JQL_SILENT_ON_PARSE_ERROR);
1377 RCGO(rc, finish);
1378
1379 JNGO(ns, env, napi_wrap(env, argv[0], jnql, jn_jql_finalize, 0, 0), finish);
1380
1381 napi_value vcoll = jn_create_string(env, jql_collection(jnql->jql));
1382 if (!vcoll) {
1383 goto finish;
1384 }
1385 JNGO(ns, env, napi_set_named_property(env, argv[0], "collection", vcoll), finish);
1386
1387 finish:
1388 if (rc) {
1389 if ((rc == JQL_ERROR_QUERY_PARSE) && jnql && jnql->jql) {
1390 JNTHROW(env, rc, jql_error(jnql->jql));
1391 } else {
1392 JNRC(env, rc);
1393 }
1394 if (jnql) {
1395 jn_jnql_destroy_mt(&jnql);
1396 }
1397 }
1398 iwpool_destroy(pool);
1399 return jn_undefined(env);
1400 }
1401
1402 // ---------------- jql_stream_attach
1403
1404 typedef struct JNQS { // query stream associated data
1405 volatile bool aborted;
1406 volatile bool paused;
1407 int refs;
1408 JBN jbn;
1409 JNQL jnql;
1410 napi_ref stream_ref; // Reference to the stream object
1411 napi_ref explain_cb_ref; // Reference to the optional explain callback
1412 int64_t limit;
1413 struct JNWORK work;
1414 pthread_mutex_t mtx;
1415 pthread_cond_t cond;
1416 } *JNQS;
1417
1418 typedef struct JNCS { // call data to `k_add_stream_tsfn`
1419 bool has_count;
1420 IWXSTR *log;
1421 IWXSTR *document;
1422 int64_t count;
1423 int64_t document_id;
1424 napi_ref stream_ref; // copied from `JNQS`
1425 } *JNCS;
1426
jn_cs_destroy(JNCS * csp)1427 static void jn_cs_destroy(JNCS *csp) {
1428 if (!csp || !*csp) {
1429 return;
1430 }
1431 JNCS cs = *csp;
1432 if (cs->document) {
1433 iwxstr_destroy(cs->document);
1434 }
1435 if (cs->log) {
1436 iwxstr_destroy(cs->log);
1437 }
1438 free(cs);
1439 *csp = 0;
1440 }
1441
1442 // function addStreamResult(stream, id, jsondoc, log)
jn_resultset_tsf(napi_env env,napi_value js_add_stream,void * context,void * data)1443 static void jn_resultset_tsf(
1444 napi_env env,
1445 napi_value js_add_stream,
1446 void *context,
1447 void *data) {
1448
1449 if (!env) { // shutdown pending
1450 JNCS cs = data;
1451 jn_cs_destroy(&cs);
1452 return;
1453 }
1454
1455 JNCS cs = data;
1456 napi_status ns;
1457 napi_value vstream, vid, vdoc, vlog, vresult;
1458 napi_value vglobal = jn_global(env);
1459 napi_value vnull = jn_null(env);
1460
1461 if (!vglobal || !vnull) {
1462 goto finish;
1463 }
1464 vstream = jn_get_ref(env, cs->stream_ref);
1465 if (!vstream) { // exception pending
1466 goto finish;
1467 }
1468 if (cs->document) {
1469 vdoc = jn_create_string(env, iwxstr_ptr(cs->document));
1470 iwxstr_destroy(cs->document);
1471 cs->document = 0;
1472 } else if (cs->has_count) {
1473 vdoc = jn_create_int64(env, cs->count);
1474 } else {
1475 vdoc = vnull;
1476 }
1477 if (!vdoc) {
1478 goto finish;
1479 }
1480 vid = jn_create_int64(env, cs->document_id);
1481 if (!vid) {
1482 goto finish;
1483 }
1484 if (cs->log) {
1485 vlog = jn_create_string(env, iwxstr_ptr(cs->log));
1486 } else {
1487 vlog = vnull;
1488 }
1489
1490 napi_value argv[] = { vstream, vid, vdoc, vlog };
1491 const int argc = sizeof(argv) / sizeof(argv[0]);
1492 JNGO(ns, env, napi_call_function(
1493 env,
1494 vglobal,
1495 js_add_stream,
1496 argc,
1497 argv,
1498 &vresult
1499 ), finish);
1500
1501 finish:
1502 if (cs->document_id < 0) {
1503 uint32_t refs;
1504 napi_reference_unref(env, cs->stream_ref, &refs);
1505 }
1506 jn_cs_destroy(&cs);
1507 }
1508
jn_jnqs_destroy_mt(napi_env env,JNQS * qsp)1509 static void jn_jnqs_destroy_mt(napi_env env, JNQS *qsp) {
1510 if (!qsp || !*qsp) {
1511 return;
1512 }
1513 JNQS qs = *qsp;
1514 uint32_t rcnt = 0;
1515 if (--qs->refs > 0) {
1516 return;
1517 }
1518 if (qs->jnql) {
1519 qs->jnql->refs--;
1520 }
1521 if (qs->stream_ref) {
1522 napi_reference_unref(env, qs->stream_ref, &rcnt);
1523 if (!rcnt) {
1524 napi_ref ref = qs->stream_ref;
1525 qs->stream_ref = 0;
1526 napi_delete_reference(env, ref);
1527 }
1528 }
1529 if (qs->explain_cb_ref) {
1530 napi_reference_unref(env, qs->explain_cb_ref, &rcnt);
1531 if (!rcnt) {
1532 napi_ref ref = qs->explain_cb_ref;
1533 qs->explain_cb_ref = 0;
1534 napi_delete_reference(env, ref);
1535 }
1536 }
1537 free(qs);
1538 }
1539
jn_stream_pause_guard(JNQS qs)1540 static iwrc jn_stream_pause_guard(JNQS qs) {
1541 iwrc rc = 0;
1542 int rci = pthread_mutex_lock(&qs->mtx);
1543 if (rci) {
1544 return iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
1545 }
1546 while (qs->paused) {
1547 rci = pthread_cond_wait(&qs->cond, &qs->mtx);
1548 if (rci) {
1549 rc = iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
1550 break;
1551 }
1552 }
1553 pthread_mutex_unlock(&qs->mtx);
1554 return rc;
1555 }
1556
jn_jql_stream_visitor(EJDB_EXEC * ux,EJDB_DOC doc,int64_t * step)1557 static iwrc jn_jql_stream_visitor(EJDB_EXEC *ux, EJDB_DOC doc, int64_t *step) {
1558 JNCS cs = 0;
1559 JNQS qs = ux->opaque;
1560 JNWORK work = &qs->work;
1561
1562 if (qs->aborted) {
1563 *step = 0;
1564 return 0;
1565 }
1566 work->rc = jn_stream_pause_guard(qs);
1567 RCRET(work->rc);
1568
1569 IWXSTR *xstr = iwxstr_new();
1570 if (!xstr) {
1571 work->rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1572 return work->rc;
1573 }
1574 if (doc->node) {
1575 work->rc = jbn_as_json(doc->node, jbl_xstr_json_printer, xstr, 0);
1576 } else {
1577 work->rc = jbl_as_json(doc->raw, jbl_xstr_json_printer, xstr, 0);
1578 }
1579 RCGO(work->rc, finish);
1580
1581 cs = malloc(sizeof(*cs));
1582 if (!cs) {
1583 work->rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1584 goto finish;
1585 }
1586 cs->count = 0;
1587 cs->has_count = false;
1588 cs->stream_ref = qs->stream_ref;
1589 cs->log = ux->log;
1590 cs->document_id = doc->id;
1591 cs->document = xstr;
1592
1593 napi_status ns = napi_call_threadsafe_function(qs->jbn->resultset_tsf, cs, napi_tsfn_blocking);
1594 if (ns) {
1595 work->rc = JN_ERROR_NAPI;
1596 work->ns = ns;
1597 goto finish;
1598 }
1599 ux->log = 0;
1600
1601 finish:
1602 if (work->rc) {
1603 iwxstr_destroy(xstr);
1604 if (cs) {
1605 cs->document = 0; // kept in xstr
1606 jn_cs_destroy(&cs);
1607 }
1608 }
1609 return work->rc;
1610 }
1611
jn_jql_stream_execute(napi_env env,void * data)1612 static void jn_jql_stream_execute(napi_env env, void *data) {
1613 napi_status ns;
1614 uint32_t refs = 0;
1615 JNWORK work = data;
1616 JNQS qs = work->data;
1617 JQL q = qs->jnql->jql;
1618 JNCS cs = 0;
1619 EJDB_EXEC ux = { 0 };
1620 bool has_count = jql_has_aggregate_count(q);
1621
1622 // Trying to stop on paused stream
1623 // not in context of query execution
1624 // hence we can avoid unnecessary database locking
1625 // before start reading stream.
1626 work->rc = jn_stream_pause_guard(qs);
1627 RCGO(work->rc, finish);
1628 JNGO(ns, env, napi_reference_ref(env, qs->stream_ref, &refs), finish);
1629
1630 if (qs->explain_cb_ref) {
1631 ux.log = iwxstr_new();
1632 if (!ux.log) {
1633 work->rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1634 goto finish;
1635 }
1636 }
1637
1638 ux.q = q;
1639 ux.db = qs->jbn->db;
1640 ux.opaque = qs;
1641 ux.limit = qs->limit;
1642 if (!has_count) {
1643 ux.visitor = jn_jql_stream_visitor;
1644 }
1645
1646 work->rc = ejdb_exec(&ux);
1647 RCGO(work->rc, finish);
1648
1649 // Stream close event
1650 cs = malloc(sizeof(*cs));
1651 if (!cs) {
1652 work->rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1653 goto finish;
1654 }
1655 cs->has_count = has_count;
1656 cs->count = ux.cnt;
1657 cs->stream_ref = qs->stream_ref;
1658 cs->log = ux.log;
1659 cs->document_id = -1;
1660 cs->document = 0;
1661 ux.log = 0;
1662
1663 ns = napi_call_threadsafe_function(qs->jbn->resultset_tsf, cs, napi_tsfn_blocking);
1664 if (ns) {
1665 work->rc = JN_ERROR_NAPI;
1666 work->ns = ns;
1667 goto finish;
1668 }
1669
1670 refs = 0;
1671
1672 finish:
1673 if (refs) {
1674 napi_reference_unref(env, qs->stream_ref, &refs);
1675 }
1676 if (ux.log) {
1677 iwxstr_destroy(ux.log);
1678 }
1679 if (work->rc) {
1680 jn_cs_destroy(&cs);
1681 }
1682 }
1683
jn_jql_stream_complete(napi_env env,napi_status ns,void * data)1684 static void jn_jql_stream_complete(napi_env env, napi_status ns, void *data) {
1685 JNWORK work = data;
1686 JNQS qs = work->data;
1687 if (!ns) {
1688 ns = work->ns;
1689 }
1690 if (jn_resolve_pending_errors(env, ns, work)) {
1691 goto finish;
1692 }
1693 JNGO(ns, env, napi_resolve_deferred(env, work->deferred, jn_undefined(env)), finish);
1694 work->deferred = 0;
1695
1696 finish:
1697 jn_work_destroy(env, &work);
1698 jn_jnqs_destroy_mt(env, &qs);
1699 }
1700
1701 // this.jql._impl.jql_stream_destroy(this);
jn_jql_stream_destroy(napi_env env,napi_callback_info info)1702 static napi_value jn_jql_stream_destroy(napi_env env, napi_callback_info info) {
1703 void *data;
1704 size_t argc = 1;
1705 napi_value ret = jn_undefined(env), argv, this;
1706
1707 napi_status ns = napi_get_cb_info(env, info, &argc, &argv, &this, &data);
1708 if (ns || (argc < 1)) {
1709 goto finish;
1710 }
1711
1712 ns = napi_remove_wrap(env, argv, &data);
1713 if (ns || !data) {
1714 goto finish;
1715 }
1716
1717 JNQS qs = data;
1718 jn_jnqs_destroy_mt(env, &qs);
1719
1720 finish:
1721 return ret;
1722 }
1723
jn_jql_stream_set_paused(napi_env env,napi_callback_info info,bool paused)1724 static napi_value jn_jql_stream_set_paused(napi_env env, napi_callback_info info, bool paused) {
1725 napi_status ns;
1726 napi_value argv, this;
1727 size_t argc = 1;
1728 void *data;
1729
1730 JNGO(ns, env, napi_get_cb_info(env, info, &argc, &argv, &this, &data), finish);
1731 if (argc != 1) {
1732 iwrc rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
1733 JNRC(env, rc);
1734 goto finish;
1735 }
1736 JNGO(ns, env, napi_unwrap(env, argv, &data), finish);
1737 JNQS qs = data;
1738 assert(qs);
1739
1740 int rci = pthread_mutex_lock(&qs->mtx);
1741 if (rci) {
1742 iwrc rc = iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
1743 JNRC(env, rc);
1744 goto finish;
1745 }
1746 if (qs->paused != paused) {
1747 qs->paused = paused;
1748 pthread_cond_broadcast(&qs->cond);
1749 }
1750 pthread_mutex_unlock(&qs->mtx);
1751
1752 finish:
1753 return jn_undefined(env);
1754 }
1755
jn_jql_stream_pause(napi_env env,napi_callback_info info)1756 static napi_value jn_jql_stream_pause(napi_env env, napi_callback_info info) {
1757 return jn_jql_stream_set_paused(env, info, true);
1758 }
1759
jn_jql_stream_resume(napi_env env,napi_callback_info info)1760 static napi_value jn_jql_stream_resume(napi_env env, napi_callback_info info) {
1761 return jn_jql_stream_set_paused(env, info, false);
1762 }
1763
jn_jql_stream_abort(napi_env env,napi_callback_info info)1764 static napi_value jn_jql_stream_abort(napi_env env, napi_callback_info info) {
1765 napi_status ns;
1766 napi_value argv, this;
1767 size_t argc = 1;
1768 void *data;
1769 JNGO(ns, env, napi_get_cb_info(env, info, &argc, &argv, &this, &data), finish);
1770 if (argc != 1) {
1771 iwrc rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
1772 JNRC(env, rc);
1773 goto finish;
1774 }
1775 JNGO(ns, env, napi_unwrap(env, argv, &data), finish);
1776 JNQS qs = data;
1777 qs->aborted = true;
1778 finish:
1779 return jn_undefined(env);
1780 }
1781
1782 // JQL._impl.jql_stream_attach(this, stream, [opts.limit, opts.explainCallback]);
jn_jql_stream_attach(napi_env env,napi_callback_info info)1783 static napi_value jn_jql_stream_attach(napi_env env, napi_callback_info info) {
1784 iwrc rc = 0;
1785 napi_status ns;
1786 napi_value ret = 0, argv[3], this, vexplain;
1787 size_t argc = sizeof(argv) / sizeof(argv[0]);
1788 void *data;
1789
1790 JBN jbn;
1791 JNQL jnql;
1792 JNQS qs = 0;
1793
1794 JNGO(ns, env, napi_get_cb_info(env, info, &argc, argv, &this, &data), finish);
1795 if (argc != sizeof(argv) / sizeof(argv[0])) {
1796 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
1797 goto finish;
1798 }
1799 JNGO(ns, env, napi_unwrap(env, argv[0], (void**) &jnql), finish); // -V580
1800
1801 qs = calloc(1, sizeof(*qs));
1802 if (!qs) {
1803 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1804 goto finish;
1805 }
1806
1807 pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
1808 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
1809 memcpy(&qs->mtx, &mtx, sizeof(mtx));
1810 memcpy(&qs->cond, &cond, sizeof(cond));
1811
1812 qs->paused = true;
1813 qs->jnql = jnql;
1814 qs->jnql->refs++; // Query in use
1815 qs->limit = jn_int_at(env, argv[2], true, false, 0, &rc);
1816 RCGO(rc, finish);
1817
1818 JNGO(ns, env, napi_get_element(env, argv[2], 1, &vexplain), finish);
1819 if (!jn_is_null_or_undefined(env, vexplain)) {
1820 napi_valuetype vtype;
1821 JNGO(ns, env, napi_typeof(env, vexplain, &vtype), finish);
1822 if (vtype != napi_function) {
1823 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
1824 goto finish;
1825 }
1826 JNGO(ns, env, napi_create_reference(env, vexplain, 1, &qs->explain_cb_ref), finish);
1827 }
1828 JNGO(ns, env, napi_unwrap(env, this, (void**) &jbn), finish); // -V580
1829 JNGO(ns, env, napi_create_reference(env, argv[1], 1, &qs->stream_ref), finish); // Reference to stream
1830 JNGO(ns, env, napi_wrap(env, argv[1], qs, 0, 0, 0), finish);
1831
1832 // Launch async work
1833 qs->refs = 2; // +1 for jn_jql_stream_complete
1834 // +1 for jn_jql_stream_destroy
1835 qs->jbn = jbn;
1836 qs->work.data = qs;
1837 ret = jn_launch_promise(env, info, "query", jn_jql_stream_execute, jn_jql_stream_complete, &qs->work);
1838
1839 finish:
1840 if (rc) {
1841 JNRC(env, rc);
1842 if (qs) {
1843 qs->refs = 0; // needed to destroy it completely
1844 }
1845 jn_jnqs_destroy_mt(env, &qs);
1846 }
1847 return ret ? ret : jn_undefined(env);
1848 }
1849
jn_jql_free_set_string_value(void * ptr,void * op)1850 static void jn_jql_free_set_string_value(void *ptr, void *op) {
1851 IWPOOL *pool = op;
1852 if (pool) {
1853 iwpool_destroy(pool);
1854 }
1855 }
1856
1857 // this._impl.jql_set(jql, placeholder, val, 1);
jn_jql_set(napi_env env,napi_callback_info info)1858 static napi_value jn_jql_set(napi_env env, napi_callback_info info) {
1859 iwrc rc = 0;
1860 int iplh = 0;
1861 napi_value argv[4], this;
1862 size_t argc = sizeof(argv) / sizeof(argv[0]);
1863 const char *splh = 0, *svalue;
1864 JNQL jnql;
1865 void *data;
1866 napi_status ns;
1867 napi_valuetype vtype;
1868
1869 IWPOOL *vpool = 0; // jql_set_xxx value
1870 IWPOOL *pool = iwpool_create(32);
1871 if (!pool) {
1872 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1873 goto finish;
1874 }
1875
1876 JNGO(ns, env, napi_get_cb_info(env, info, &argc, argv, &this, &data), finish);
1877 if (argc != sizeof(argv) / sizeof(argv[0])) {
1878 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
1879 goto finish;
1880 }
1881 JNGO(ns, env, napi_unwrap(env, argv[0], &data), finish);
1882 jnql = data;
1883 if (jnql->refs > 0) {
1884 rc = JN_ERROR_QUERY_IN_USE;
1885 goto finish;
1886 }
1887
1888 // Set type
1889 int stype = jn_int(env, argv[3], false, false, &rc);
1890 RCGO(rc, finish);
1891
1892 // Placeholder
1893 JNGO(ns, env, napi_typeof(env, argv[1], &vtype), finish);
1894
1895 if (vtype == napi_string) {
1896 iplh = 0;
1897 splh = jn_string(env, argv[1], pool, false, false, &rc);
1898 } else if (vtype == napi_number) {
1899 splh = 0;
1900 iplh = jn_int(env, argv[1], false, false, &rc);
1901 } else {
1902 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
1903 }
1904 RCGO(rc, finish);
1905
1906 switch (stype) {
1907 case 6: // String
1908 case 1: // JSON
1909 case 2: { // Regexp
1910 JBL_NODE node;
1911 vpool = iwpool_create(64);
1912 if (!vpool) {
1913 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1914 goto finish;
1915 }
1916 RCGO(rc, finish);
1917 switch (stype) {
1918 case 6: // String
1919 case 2: // Regexp
1920 svalue = jn_string(env, argv[2], vpool, false, false, &rc);
1921 RCGO(rc, finish);
1922 if (stype == 6) {
1923 rc = jql_set_str2(jnql->jql, splh, iplh, svalue, jn_jql_free_set_string_value, vpool); // -V614
1924 } else {
1925 rc = jql_set_regexp2(jnql->jql, splh, iplh, svalue, jn_jql_free_set_string_value, vpool);
1926 }
1927 RCGO(rc, finish);
1928 break;
1929 case 1:
1930 svalue = jn_string(env, argv[2], pool, false, false, &rc);
1931 RCGO(rc, finish);
1932 rc = jbn_from_json(svalue, &node, vpool);
1933 RCGO(rc, finish);
1934 rc = jql_set_json2(jnql->jql, splh, iplh, node, jn_jql_free_set_string_value, vpool);
1935 RCGO(rc, finish);
1936 break;
1937 }
1938 }
1939 break;
1940
1941 case 3: { // Integer
1942 int64_t v = jn_int(env, argv[2], false, false, &rc);
1943 RCGO(rc, finish);
1944 rc = jql_set_i64(jnql->jql, splh, iplh, v);
1945 RCGO(rc, finish);
1946 }
1947 break;
1948
1949 case 4: { // Double
1950 double v = jn_double(env, argv[2], false, false, &rc);
1951 RCGO(rc, finish);
1952 rc = jql_set_f64(jnql->jql, splh, iplh, v);
1953 RCGO(rc, finish);
1954 }
1955 break;
1956
1957 case 5: { // Boolean
1958 bool v = jn_bool(env, argv[2], false, false, &rc);
1959 RCGO(rc, finish);
1960 rc = jql_set_bool(jnql->jql, splh, iplh, v);
1961 RCGO(rc, finish);
1962 }
1963 break;
1964 }
1965
1966 finish:
1967 if (pool) {
1968 iwpool_destroy(pool);
1969 }
1970 if (rc) {
1971 if (vpool) { // vpool will be destroyed when JQL freed
1972 iwpool_destroy(vpool);
1973 }
1974 JNRC(env, rc);
1975 }
1976 return jn_undefined(env);
1977 }
1978
1979 // jql_limit(jql);
jn_jql_limit(napi_env env,napi_callback_info info)1980 static napi_value jn_jql_limit(napi_env env, napi_callback_info info) {
1981 iwrc rc = 0;
1982 napi_status ns;
1983 napi_value ret = 0, argv, this;
1984 size_t argc = 1;
1985 void *data;
1986 JNQL jnql;
1987 int64_t limit;
1988
1989 JNGO(ns, env, napi_get_cb_info(env, info, &argc, &argv, &this, &data), finish);
1990 if (argc != 1) {
1991 rc = JN_ERROR_INVALID_NATIVE_CALL_ARGS;
1992 goto finish;
1993 }
1994 JNGO(ns, env, napi_unwrap(env, argv, &data), finish);
1995 jnql = data;
1996
1997 rc = jql_get_limit(jnql->jql, &limit);
1998 RCGO(rc, finish);
1999
2000 ret = jn_create_int64(env, limit);
2001
2002 finish:
2003 if (rc) {
2004 JNRC(env, rc);
2005 }
2006 return ret ? ret : jn_undefined(env);
2007 }
2008
2009 // ----------------
2010
jn_ecodefn(locale_t locale,uint32_t ecode)2011 static const char *jn_ecodefn(locale_t locale, uint32_t ecode) {
2012 if (!((ecode > _JN_ERROR_START) && (ecode < _JN_ERROR_END))) {
2013 return 0;
2014 }
2015 switch (ecode) {
2016 case JN_ERROR_INVALID_NATIVE_CALL_ARGS:
2017 return "Invalid native function call args (JN_ERROR_INVALID_NATIVE_CALL_ARGS)";
2018 case JN_ERROR_INVALID_STATE:
2019 return "Invalid native extension state (JN_ERROR_INVALID_STATE)";
2020 case JN_ERROR_QUERY_IN_USE:
2021 return "Query object is in-use by active async iteration, and cannot be changed (JN_ERROR_QUERY_IN_USE)";
2022 case JN_ERROR_NAPI:
2023 return "N-API Error (JN_ERROR_NAPI)";
2024 }
2025 return 0;
2026 }
2027
Init(napi_env env,napi_value exports)2028 napi_value Init(napi_env env, napi_value exports) {
2029 napi_status ns = 0;
2030 static volatile int jn_ecodefn_initialized = 0;
2031 if (__sync_bool_compare_and_swap(&jn_ecodefn_initialized, 0, 1)) {
2032 iwrc rc = ejdb_init();
2033 if (rc) {
2034 JNRC(env, rc);
2035 return 0;
2036 }
2037 iwlog_register_ecodefn(jn_ecodefn);
2038 }
2039 napi_value vglobal, vadd_streamfn;
2040
2041 JNGO(ns, env, napi_get_global(env, &vglobal), finish);
2042
2043 // Define EJDB2Impl class
2044 napi_value ejdb2impl_clazz;
2045 napi_property_descriptor properties[] = {
2046 JNFUNC(open),
2047 JNFUNC(close),
2048 JNFUNC(put),
2049 JNFUNC(patch),
2050 JNFUNC(patch_or_put),
2051 JNFUNC(get),
2052 JNFUNC(del),
2053 JNFUNC(rename_collection),
2054 JNFUNC(info),
2055 JNFUNC(index),
2056 JNFUNC(rmcoll),
2057 JNFUNC(online_backup),
2058 JNFUNC(jql_init),
2059 JNFUNC(jql_set),
2060 JNFUNC(jql_limit),
2061 JNFUNC(jql_stream_destroy),
2062 JNFUNC(jql_stream_attach),
2063 JNFUNC(jql_stream_pause),
2064 JNFUNC(jql_stream_resume),
2065 JNFUNC(jql_stream_abort)
2066 };
2067 JNGO(ns, env, napi_define_class(env, "EJDB2Impl", NAPI_AUTO_LENGTH, jn_ejdb2impl_ctor, 0,
2068 sizeof(properties) / sizeof(properties[0]),
2069 properties, &ejdb2impl_clazz), finish);
2070
2071 napi_property_descriptor descriptors[] = {
2072 { "EJDB2Impl", 0, 0, 0, 0, ejdb2impl_clazz, napi_default, 0 }
2073 };
2074 JNGO(ns, env, napi_define_properties(env, exports,
2075 sizeof(descriptors) / sizeof(descriptors[0]),
2076 descriptors),
2077 finish);
2078
2079 JNGO(ns, env, napi_get_named_property(env, vglobal, "__ejdb_add_stream_result__", &vadd_streamfn), finish);
2080 if (jn_is_null_or_undefined(env, vadd_streamfn)) {
2081 iwrc rc = JN_ERROR_INVALID_STATE;
2082 JNRC(env, rc);
2083 goto finish;
2084 }
2085 JNGO(ns, env, napi_create_reference(env, vadd_streamfn, 1, &k_vadd_streamfn_ref), finish);
2086
2087 finish:
2088 return exports;
2089 }
2090
2091 NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
2092