UNPKG

22.2 kBtext/x-cView Raw
1#include <cstring>
2#include <napi.h>
3
4#include "macros.h"
5#include "database.h"
6#include "statement.h"
7
8using namespace node_sqlite3;
9
10#if NAPI_VERSION < 6
11Napi::FunctionReference Database::constructor;
12#endif
13
14Napi::Object Database::Init(Napi::Env env, Napi::Object exports) {
15 Napi::HandleScope scope(env);
16 // declare napi_default_method here as it is only available in Node v14.12.0+
17 auto napi_default_method = static_cast<napi_property_attributes>(napi_writable | napi_configurable);
18
19 auto t = DefineClass(env, "Database", {
20 InstanceMethod("close", &Database::Close, napi_default_method),
21 InstanceMethod("exec", &Database::Exec, napi_default_method),
22 InstanceMethod("wait", &Database::Wait, napi_default_method),
23 InstanceMethod("loadExtension", &Database::LoadExtension, napi_default_method),
24 InstanceMethod("serialize", &Database::Serialize, napi_default_method),
25 InstanceMethod("parallelize", &Database::Parallelize, napi_default_method),
26 InstanceMethod("configure", &Database::Configure, napi_default_method),
27 InstanceMethod("interrupt", &Database::Interrupt, napi_default_method),
28 InstanceAccessor("open", &Database::Open, nullptr)
29 });
30
31#if NAPI_VERSION < 6
32 constructor = Napi::Persistent(t);
33 constructor.SuppressDestruct();
34#else
35 Napi::FunctionReference* constructor = new Napi::FunctionReference();
36 *constructor = Napi::Persistent(t);
37 env.SetInstanceData<Napi::FunctionReference>(constructor);
38#endif
39
40 exports.Set("Database", t);
41 return exports;
42}
43
44void Database::Process() {
45 auto env = this->Env();
46 Napi::HandleScope scope(env);
47
48 if (!open && locked && !queue.empty()) {
49 EXCEPTION(Napi::String::New(env, "Database handle is closed"), SQLITE_MISUSE, exception);
50 Napi::Value argv[] = { exception };
51 bool called = false;
52
53 // Call all callbacks with the error object.
54 while (!queue.empty()) {
55 auto call = std::unique_ptr<Call>(queue.front());
56 queue.pop();
57 auto baton = std::unique_ptr<Baton>(call->baton);
58 Napi::Function cb = baton->callback.Value();
59 if (IS_FUNCTION(cb)) {
60 TRY_CATCH_CALL(this->Value(), cb, 1, argv);
61 called = true;
62 }
63 }
64
65 // When we couldn't call a callback function, emit an error on the
66 // Database object.
67 if (!called) {
68 Napi::Value info[] = { Napi::String::New(env, "error"), exception };
69 EMIT_EVENT(Value(), 2, info);
70 }
71 return;
72 }
73
74 while (open && (!locked || pending == 0) && !queue.empty()) {
75 Call *c = queue.front();
76
77 if (c->exclusive && pending > 0) {
78 break;
79 }
80
81 queue.pop();
82 std::unique_ptr<Call> call(c);
83 locked = call->exclusive;
84 call->callback(call->baton);
85
86 if (locked) break;
87 }
88}
89
90void Database::Schedule(Work_Callback callback, Baton* baton, bool exclusive) {
91 auto env = this->Env();
92 Napi::HandleScope scope(env);
93
94 if (!open && locked) {
95 EXCEPTION(Napi::String::New(env, "Database is closed"), SQLITE_MISUSE, exception);
96 Napi::Function cb = baton->callback.Value();
97 // We don't call the actual callback, so we have to make sure that
98 // the baton gets destroyed.
99 delete baton;
100 if (IS_FUNCTION(cb)) {
101 Napi::Value argv[] = { exception };
102 TRY_CATCH_CALL(Value(), cb, 1, argv);
103 }
104 else {
105 Napi::Value argv[] = { Napi::String::New(env, "error"), exception };
106 EMIT_EVENT(Value(), 2, argv);
107 }
108 return;
109 }
110
111 if (!open || ((locked || exclusive || serialize) && pending > 0)) {
112 queue.emplace(new Call(callback, baton, exclusive || serialize));
113 }
114 else {
115 locked = exclusive;
116 callback(baton);
117 }
118}
119
120Database::Database(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Database>(info) {
121 auto env = info.Env();
122
123 if (info.Length() <= 0 || !info[0].IsString()) {
124 Napi::TypeError::New(env, "String expected").ThrowAsJavaScriptException();
125 return;
126 }
127 auto filename = info[0].As<Napi::String>().Utf8Value();
128
129 unsigned int pos = 1;
130
131 int mode;
132 if (info.Length() >= pos && info[pos].IsNumber() && OtherIsInt(info[pos].As<Napi::Number>())) {
133 mode = info[pos++].As<Napi::Number>().Int32Value();
134 }
135 else {
136 mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
137 }
138
139 Napi::Function callback;
140 if (info.Length() >= pos && info[pos].IsFunction()) {
141 callback = info[pos++].As<Napi::Function>();
142 }
143
144 info.This().As<Napi::Object>().DefineProperty(Napi::PropertyDescriptor::Value("filename", info[0].As<Napi::String>(), napi_default));
145 info.This().As<Napi::Object>().DefineProperty(Napi::PropertyDescriptor::Value("mode", Napi::Number::New(env, mode), napi_default));
146
147 // Start opening the database.
148 auto* baton = new OpenBaton(this, callback, filename.c_str(), mode);
149 Work_BeginOpen(baton);
150}
151
152void Database::Work_BeginOpen(Baton* baton) {
153 auto env = baton->db->Env();
154 CREATE_WORK("sqlite3.Database.Open", Work_Open, Work_AfterOpen);
155}
156
157void Database::Work_Open(napi_env e, void* data) {
158 auto* baton = static_cast<OpenBaton*>(data);
159 auto* db = baton->db;
160
161 baton->status = sqlite3_open_v2(
162 baton->filename.c_str(),
163 &db->_handle,
164 baton->mode,
165 NULL
166 );
167
168 if (baton->status != SQLITE_OK) {
169 baton->message = std::string(sqlite3_errmsg(db->_handle));
170 sqlite3_close(db->_handle);
171 db->_handle = NULL;
172 }
173 else {
174 // Set default database handle values.
175 sqlite3_busy_timeout(db->_handle, 1000);
176 }
177}
178
179void Database::Work_AfterOpen(napi_env e, napi_status status, void* data) {
180 std::unique_ptr<OpenBaton> baton(static_cast<OpenBaton*>(data));
181
182 auto* db = baton->db;
183
184 auto env = db->Env();
185 Napi::HandleScope scope(env);
186
187 Napi::Value argv[1];
188 if (baton->status != SQLITE_OK) {
189 EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception);
190 argv[0] = exception;
191 }
192 else {
193 db->open = true;
194 argv[0] = env.Null();
195 }
196
197 Napi::Function cb = baton->callback.Value();
198
199 if (IS_FUNCTION(cb)) {
200 TRY_CATCH_CALL(db->Value(), cb, 1, argv);
201 }
202 else if (!db->open) {
203 Napi::Value info[] = { Napi::String::New(env, "error"), argv[0] };
204 EMIT_EVENT(db->Value(), 2, info);
205 }
206
207 if (db->open) {
208 Napi::Value info[] = { Napi::String::New(env, "open") };
209 EMIT_EVENT(db->Value(), 1, info);
210 db->Process();
211 }
212}
213
214Napi::Value Database::Open(const Napi::CallbackInfo& info) {
215 auto env = this->Env();
216 auto* db = this;
217 return Napi::Boolean::New(env, db->open);
218}
219
220Napi::Value Database::Close(const Napi::CallbackInfo& info) {
221 auto env = info.Env();
222 auto* db = this;
223 OPTIONAL_ARGUMENT_FUNCTION(0, callback);
224
225 auto* baton = new Baton(db, callback);
226 db->Schedule(Work_BeginClose, baton, true);
227
228 return info.This();
229}
230
231void Database::Work_BeginClose(Baton* baton) {
232 assert(baton->db->locked);
233 assert(baton->db->open);
234 assert(baton->db->_handle);
235 assert(baton->db->pending == 0);
236
237 baton->db->pending++;
238 baton->db->RemoveCallbacks();
239 baton->db->closing = true;
240
241 auto env = baton->db->Env();
242 CREATE_WORK("sqlite3.Database.Close", Work_Close, Work_AfterClose);
243}
244
245void Database::Work_Close(napi_env e, void* data) {
246 auto* baton = static_cast<Baton*>(data);
247 auto* db = baton->db;
248
249 baton->status = sqlite3_close(db->_handle);
250
251 if (baton->status != SQLITE_OK) {
252 baton->message = std::string(sqlite3_errmsg(db->_handle));
253 }
254 else {
255 db->_handle = NULL;
256 }
257}
258
259void Database::Work_AfterClose(napi_env e, napi_status status, void* data) {
260 std::unique_ptr<Baton> baton(static_cast<Baton*>(data));
261
262 auto* db = baton->db;
263
264 auto env = db->Env();
265 Napi::HandleScope scope(env);
266
267 db->pending--;
268 db->closing = false;
269
270 Napi::Value argv[1];
271 if (baton->status != SQLITE_OK) {
272 EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception);
273 argv[0] = exception;
274 }
275 else {
276 db->open = false;
277 // Leave db->locked to indicate that this db object has reached
278 // the end of its life.
279 argv[0] = env.Null();
280 }
281
282 Napi::Function cb = baton->callback.Value();
283
284 // Fire callbacks.
285 if (IS_FUNCTION(cb)) {
286 TRY_CATCH_CALL(db->Value(), cb, 1, argv);
287 }
288 else if (db->open) {
289 Napi::Value info[] = { Napi::String::New(env, "error"), argv[0] };
290 EMIT_EVENT(db->Value(), 2, info);
291 }
292
293 if (!db->open) {
294 Napi::Value info[] = { Napi::String::New(env, "close") };
295 EMIT_EVENT(db->Value(), 1, info);
296 db->Process();
297 }
298}
299
300Napi::Value Database::Serialize(const Napi::CallbackInfo& info) {
301 auto env = this->Env();
302 auto* db = this;
303 OPTIONAL_ARGUMENT_FUNCTION(0, callback);
304
305 bool before = db->serialize;
306 db->serialize = true;
307
308 if (!callback.IsEmpty() && callback.IsFunction()) {
309 TRY_CATCH_CALL(info.This(), callback, 0, NULL, info.This());
310 db->serialize = before;
311 }
312
313 db->Process();
314
315 return info.This();
316}
317
318Napi::Value Database::Parallelize(const Napi::CallbackInfo& info) {
319 auto env = this->Env();
320 auto* db = this;
321 OPTIONAL_ARGUMENT_FUNCTION(0, callback);
322
323 auto before = db->serialize;
324 db->serialize = false;
325
326 if (!callback.IsEmpty() && callback.IsFunction()) {
327 TRY_CATCH_CALL(info.This(), callback, 0, NULL, info.This());
328 db->serialize = before;
329 }
330
331 db->Process();
332
333 return info.This();
334}
335
336Napi::Value Database::Configure(const Napi::CallbackInfo& info) {
337 auto env = this->Env();
338 auto* db = this;
339
340 REQUIRE_ARGUMENTS(2);
341
342 Napi::Function handle;
343 if (info[0].StrictEquals( Napi::String::New(env, "trace"))) {
344 auto* baton = new Baton(db, handle);
345 db->Schedule(RegisterTraceCallback, baton);
346 }
347 else if (info[0].StrictEquals( Napi::String::New(env, "profile"))) {
348 auto* baton = new Baton(db, handle);
349 db->Schedule(RegisterProfileCallback, baton);
350 }
351 else if (info[0].StrictEquals( Napi::String::New(env, "busyTimeout"))) {
352 if (!info[1].IsNumber()) {
353 Napi::TypeError::New(env, "Value must be an integer").ThrowAsJavaScriptException();
354 return env.Null();
355 }
356 auto* baton = new Baton(db, handle);
357 baton->status = info[1].As<Napi::Number>().Int32Value();
358 db->Schedule(SetBusyTimeout, baton);
359 }
360 else if (info[0].StrictEquals( Napi::String::New(env, "limit"))) {
361 REQUIRE_ARGUMENTS(3);
362 if (!info[1].IsNumber()) {
363 Napi::TypeError::New(env, "limit id must be an integer").ThrowAsJavaScriptException();
364 return env.Null();
365 }
366 if (!info[2].IsNumber()) {
367 Napi::TypeError::New(env, "limit value must be an integer").ThrowAsJavaScriptException();
368 return env.Null();
369 }
370 int id = info[1].As<Napi::Number>().Int32Value();
371 int value = info[2].As<Napi::Number>().Int32Value();
372 Baton* baton = new LimitBaton(db, handle, id, value);
373 db->Schedule(SetLimit, baton);
374 }
375 else if (info[0].StrictEquals(Napi::String::New(env, "change"))) {
376 auto* baton = new Baton(db, handle);
377 db->Schedule(RegisterUpdateCallback, baton);
378 }
379 else {
380 Napi::TypeError::New(env, (StringConcat(
381#if V8_MAJOR_VERSION > 6
382 info.GetIsolate(),
383#endif
384 info[0].As<Napi::String>(),
385 Napi::String::New(env, " is not a valid configuration option")
386 )).Utf8Value().c_str()).ThrowAsJavaScriptException();
387 return env.Null();
388 }
389
390 db->Process();
391
392 return info.This();
393}
394
395Napi::Value Database::Interrupt(const Napi::CallbackInfo& info) {
396 auto env = this->Env();
397 auto* db = this;
398
399 if (!db->open) {
400 Napi::Error::New(env, "Database is not open").ThrowAsJavaScriptException();
401 return env.Null();
402 }
403
404 if (db->closing) {
405 Napi::Error::New(env, "Database is closing").ThrowAsJavaScriptException();
406 return env.Null();
407 }
408
409 sqlite3_interrupt(db->_handle);
410 return info.This();
411}
412
413void Database::SetBusyTimeout(Baton* b) {
414 auto baton = std::unique_ptr<Baton>(b);
415
416 assert(baton->db->open);
417 assert(baton->db->_handle);
418
419 // Abuse the status field for passing the timeout.
420 sqlite3_busy_timeout(baton->db->_handle, baton->status);
421}
422
423void Database::SetLimit(Baton* b) {
424 std::unique_ptr<LimitBaton> baton(static_cast<LimitBaton*>(b));
425
426 assert(baton->db->open);
427 assert(baton->db->_handle);
428
429 sqlite3_limit(baton->db->_handle, baton->id, baton->value);
430}
431
432void Database::RegisterTraceCallback(Baton* b) {
433 auto baton = std::unique_ptr<Baton>(b);
434 assert(baton->db->open);
435 assert(baton->db->_handle);
436 auto* db = baton->db;
437
438 if (db->debug_trace == NULL) {
439 // Add it.
440 db->debug_trace = new AsyncTrace(db, TraceCallback);
441 sqlite3_trace(db->_handle, TraceCallback, db);
442 }
443 else {
444 // Remove it.
445 sqlite3_trace(db->_handle, NULL, NULL);
446 db->debug_trace->finish();
447 db->debug_trace = NULL;
448 }
449}
450
451void Database::TraceCallback(void* db, const char* sql) {
452 // Note: This function is called in the thread pool.
453 // Note: Some queries, such as "EXPLAIN" queries, are not sent through this.
454 static_cast<Database*>(db)->debug_trace->send(new std::string(sql));
455}
456
457void Database::TraceCallback(Database* db, std::string* s) {
458 std::unique_ptr<std::string> sql(s);
459 // Note: This function is called in the main V8 thread.
460 auto env = db->Env();
461 Napi::HandleScope scope(env);
462
463 Napi::Value argv[] = {
464 Napi::String::New(env, "trace"),
465 Napi::String::New(env, sql->c_str())
466 };
467 EMIT_EVENT(db->Value(), 2, argv);
468}
469
470void Database::RegisterProfileCallback(Baton* b) {
471 auto baton = std::unique_ptr<Baton>(b);
472 assert(baton->db->open);
473 assert(baton->db->_handle);
474 auto* db = baton->db;
475
476 if (db->debug_profile == NULL) {
477 // Add it.
478 db->debug_profile = new AsyncProfile(db, ProfileCallback);
479 sqlite3_profile(db->_handle, ProfileCallback, db);
480 }
481 else {
482 // Remove it.
483 sqlite3_profile(db->_handle, NULL, NULL);
484 db->debug_profile->finish();
485 db->debug_profile = NULL;
486 }
487}
488
489void Database::ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs) {
490 // Note: This function is called in the thread pool.
491 // Note: Some queries, such as "EXPLAIN" queries, are not sent through this.
492 auto* info = new ProfileInfo();
493 info->sql = std::string(sql);
494 info->nsecs = nsecs;
495 static_cast<Database*>(db)->debug_profile->send(info);
496}
497
498void Database::ProfileCallback(Database *db, ProfileInfo* i) {
499 auto info = std::unique_ptr<ProfileInfo>(i);
500 auto env = db->Env();
501 Napi::HandleScope scope(env);
502
503 Napi::Value argv[] = {
504 Napi::String::New(env, "profile"),
505 Napi::String::New(env, info->sql.c_str()),
506 Napi::Number::New(env, (double)info->nsecs / 1000000.0)
507 };
508 EMIT_EVENT(db->Value(), 3, argv);
509}
510
511void Database::RegisterUpdateCallback(Baton* b) {
512 auto baton = std::unique_ptr<Baton>(b);
513 assert(baton->db->open);
514 assert(baton->db->_handle);
515 auto* db = baton->db;
516
517 if (db->update_event == NULL) {
518 // Add it.
519 db->update_event = new AsyncUpdate(db, UpdateCallback);
520 sqlite3_update_hook(db->_handle, UpdateCallback, db);
521 }
522 else {
523 // Remove it.
524 sqlite3_update_hook(db->_handle, NULL, NULL);
525 db->update_event->finish();
526 db->update_event = NULL;
527 }
528}
529
530void Database::UpdateCallback(void* db, int type, const char* database,
531 const char* table, sqlite3_int64 rowid) {
532 // Note: This function is called in the thread pool.
533 // Note: Some queries, such as "EXPLAIN" queries, are not sent through this.
534 auto* info = new UpdateInfo();
535 info->type = type;
536 info->database = std::string(database);
537 info->table = std::string(table);
538 info->rowid = rowid;
539 static_cast<Database*>(db)->update_event->send(info);
540}
541
542void Database::UpdateCallback(Database *db, UpdateInfo* i) {
543 auto info = std::unique_ptr<UpdateInfo>(i);
544 auto env = db->Env();
545 Napi::HandleScope scope(env);
546
547 Napi::Value argv[] = {
548 Napi::String::New(env, "change"),
549 Napi::String::New(env, sqlite_authorizer_string(info->type)),
550 Napi::String::New(env, info->database.c_str()),
551 Napi::String::New(env, info->table.c_str()),
552 Napi::Number::New(env, info->rowid),
553 };
554 EMIT_EVENT(db->Value(), 5, argv);
555}
556
557Napi::Value Database::Exec(const Napi::CallbackInfo& info) {
558 auto env = this->Env();
559 auto* db = this;
560
561 REQUIRE_ARGUMENT_STRING(0, sql);
562 OPTIONAL_ARGUMENT_FUNCTION(1, callback);
563
564 Baton* baton = new ExecBaton(db, callback, sql.c_str());
565 db->Schedule(Work_BeginExec, baton, true);
566
567 return info.This();
568}
569
570void Database::Work_BeginExec(Baton* baton) {
571 assert(baton->db->locked);
572 assert(baton->db->open);
573 assert(baton->db->_handle);
574 assert(baton->db->pending == 0);
575 baton->db->pending++;
576
577 auto env = baton->db->Env();
578 CREATE_WORK("sqlite3.Database.Exec", Work_Exec, Work_AfterExec);
579}
580
581void Database::Work_Exec(napi_env e, void* data) {
582 auto* baton = static_cast<ExecBaton*>(data);
583
584 char* message = NULL;
585 baton->status = sqlite3_exec(
586 baton->db->_handle,
587 baton->sql.c_str(),
588 NULL,
589 NULL,
590 &message
591 );
592
593 if (baton->status != SQLITE_OK && message != NULL) {
594 baton->message = std::string(message);
595 sqlite3_free(message);
596 }
597}
598
599void Database::Work_AfterExec(napi_env e, napi_status status, void* data) {
600 std::unique_ptr<ExecBaton> baton(static_cast<ExecBaton*>(data));
601
602 auto* db = baton->db;
603 db->pending--;
604
605 auto env = db->Env();
606 Napi::HandleScope scope(env);
607
608 Napi::Function cb = baton->callback.Value();
609
610 if (baton->status != SQLITE_OK) {
611 EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception);
612
613 if (IS_FUNCTION(cb)) {
614 Napi::Value argv[] = { exception };
615 TRY_CATCH_CALL(db->Value(), cb, 1, argv);
616 }
617 else {
618 Napi::Value info[] = { Napi::String::New(env, "error"), exception };
619 EMIT_EVENT(db->Value(), 2, info);
620 }
621 }
622 else if (IS_FUNCTION(cb)) {
623 Napi::Value argv[] = { env.Null() };
624 TRY_CATCH_CALL(db->Value(), cb, 1, argv);
625 }
626
627 db->Process();
628}
629
630Napi::Value Database::Wait(const Napi::CallbackInfo& info) {
631 auto env = info.Env();
632 auto* db = this;
633
634 OPTIONAL_ARGUMENT_FUNCTION(0, callback);
635
636 auto* baton = new Baton(db, callback);
637 db->Schedule(Work_Wait, baton, true);
638
639 return info.This();
640}
641
642void Database::Work_Wait(Baton* b) {
643 auto baton = std::unique_ptr<Baton>(b);
644
645 auto env = baton->db->Env();
646 Napi::HandleScope scope(env);
647
648 assert(baton->db->locked);
649 assert(baton->db->open);
650 assert(baton->db->_handle);
651 assert(baton->db->pending == 0);
652
653 Napi::Function cb = baton->callback.Value();
654 if (IS_FUNCTION(cb)) {
655 Napi::Value argv[] = { env.Null() };
656 TRY_CATCH_CALL(baton->db->Value(), cb, 1, argv);
657 }
658
659 baton->db->Process();
660}
661
662Napi::Value Database::LoadExtension(const Napi::CallbackInfo& info) {
663 auto env = this->Env();
664 auto* db = this;
665
666 REQUIRE_ARGUMENT_STRING(0, filename);
667 OPTIONAL_ARGUMENT_FUNCTION(1, callback);
668
669 Baton* baton = new LoadExtensionBaton(db, callback, filename.c_str());
670 db->Schedule(Work_BeginLoadExtension, baton, true);
671
672 return info.This();
673}
674
675void Database::Work_BeginLoadExtension(Baton* baton) {
676 assert(baton->db->locked);
677 assert(baton->db->open);
678 assert(baton->db->_handle);
679 assert(baton->db->pending == 0);
680 baton->db->pending++;
681
682 auto env = baton->db->Env();
683 CREATE_WORK("sqlite3.Database.LoadExtension", Work_LoadExtension, Work_AfterLoadExtension);
684}
685
686void Database::Work_LoadExtension(napi_env e, void* data) {
687 auto* baton = static_cast<LoadExtensionBaton*>(data);
688
689 sqlite3_enable_load_extension(baton->db->_handle, 1);
690
691 char* message = NULL;
692 baton->status = sqlite3_load_extension(
693 baton->db->_handle,
694 baton->filename.c_str(),
695 0,
696 &message
697 );
698
699 sqlite3_enable_load_extension(baton->db->_handle, 0);
700
701 if (baton->status != SQLITE_OK && message != NULL) {
702 baton->message = std::string(message);
703 sqlite3_free(message);
704 }
705}
706
707void Database::Work_AfterLoadExtension(napi_env e, napi_status status, void* data) {
708 std::unique_ptr<LoadExtensionBaton> baton(static_cast<LoadExtensionBaton*>(data));
709
710 auto* db = baton->db;
711 db->pending--;
712
713 auto env = db->Env();
714 Napi::HandleScope scope(env);
715
716 Napi::Function cb = baton->callback.Value();
717
718 if (baton->status != SQLITE_OK) {
719 EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception);
720
721 if (IS_FUNCTION(cb)) {
722 Napi::Value argv[] = { exception };
723 TRY_CATCH_CALL(db->Value(), cb, 1, argv);
724 }
725 else {
726 Napi::Value info[] = { Napi::String::New(env, "error"), exception };
727 EMIT_EVENT(db->Value(), 2, info);
728 }
729 }
730 else if (IS_FUNCTION(cb)) {
731 Napi::Value argv[] = { env.Null() };
732 TRY_CATCH_CALL(db->Value(), cb, 1, argv);
733 }
734
735 db->Process();
736}
737
738void Database::RemoveCallbacks() {
739 if (debug_trace) {
740 debug_trace->finish();
741 debug_trace = NULL;
742 }
743 if (debug_profile) {
744 debug_profile->finish();
745 debug_profile = NULL;
746 }
747 if (update_event) {
748 update_event->finish();
749 update_event = NULL;
750 }
751}