@homeofthings/sqlite3 0.0.1-alpha0 → 6.1.1

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,939 @@
1
+ #include <cstring>
2
+ #include <napi.h>
3
+ #include <uv.h>
4
+
5
+ #include "macros.h"
6
+ #include "database.h"
7
+ #include "statement.h"
8
+
9
+ using namespace node_sqlite3;
10
+
11
+ Napi::Object Statement::Init(Napi::Env env, Napi::Object exports) {
12
+ Napi::HandleScope scope(env);
13
+
14
+ // declare napi_default_method here as it is only available in Node v14.12.0+
15
+ auto napi_default_method = static_cast<napi_property_attributes>(napi_writable | napi_configurable);
16
+
17
+ auto t = DefineClass(env, "Statement", {
18
+ InstanceMethod("bind", &Statement::Bind, napi_default_method),
19
+ InstanceMethod("get", &Statement::Get, napi_default_method),
20
+ InstanceMethod("run", &Statement::Run, napi_default_method),
21
+ InstanceMethod("all", &Statement::All, napi_default_method),
22
+ InstanceMethod("each", &Statement::Each, napi_default_method),
23
+ InstanceMethod("reset", &Statement::Reset, napi_default_method),
24
+ InstanceMethod("finalize", &Statement::Finalize_, napi_default_method),
25
+ });
26
+
27
+ exports.Set("Statement", t);
28
+ return exports;
29
+ }
30
+
31
+ // A Napi InstanceOf for Javascript Objects "Date" and "RegExp"
32
+ bool OtherInstanceOf(Napi::Object source, const char* object_type) {
33
+ if (strncmp(object_type, "Date", 4) == 0) {
34
+ return source.InstanceOf(source.Env().Global().Get("Date").As<Function>());
35
+ } else if (strncmp(object_type, "RegExp", 6) == 0) {
36
+ return source.InstanceOf(source.Env().Global().Get("RegExp").As<Function>());
37
+ }
38
+
39
+ return false;
40
+ }
41
+
42
+ void Statement::Process() {
43
+ if (finalized && !queue.empty()) {
44
+ return CleanQueue();
45
+ }
46
+
47
+ while (prepared && !locked && !queue.empty()) {
48
+ auto call = std::unique_ptr<Call>(queue.front());
49
+ queue.pop();
50
+
51
+ call->callback(call->baton);
52
+ }
53
+ }
54
+
55
+ void Statement::Schedule(Work_Callback callback, Baton* baton) {
56
+ if (finalized) {
57
+ queue.emplace(new Call(callback, baton));
58
+ CleanQueue();
59
+ }
60
+ else if (!prepared || locked) {
61
+ queue.emplace(new Call(callback, baton));
62
+ }
63
+ else {
64
+ callback(baton);
65
+ }
66
+ }
67
+
68
+ template <class T> void Statement::Error(T* baton) {
69
+ Statement* stmt = baton->stmt;
70
+
71
+ auto env = stmt->Env();
72
+ Napi::HandleScope scope(env);
73
+
74
+ // Fail hard on logic errors.
75
+ assert(stmt->status != 0);
76
+ EXCEPTION(Napi::String::New(env, stmt->message.c_str()), stmt->status, exception);
77
+
78
+ Napi::Function cb = baton->callback.Value();
79
+
80
+ if (IS_FUNCTION(cb)) {
81
+ Napi::Value argv[] = { exception };
82
+ TRY_CATCH_CALL(stmt->Value(), cb, 1, argv);
83
+ }
84
+ else {
85
+ Napi::Value argv[] = { Napi::String::New(env, "error"), exception };
86
+ EMIT_EVENT(stmt->Value(), 2, argv);
87
+ }
88
+ }
89
+
90
+ // { Database db, String sql, Array params, Function callback }
91
+ Statement::Statement(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Statement>(info) {
92
+ auto env = info.Env();
93
+ int length = info.Length();
94
+
95
+ if (length <= 0 || !Database::HasInstance(info[0])) {
96
+ Napi::TypeError::New(env, "Database object expected").ThrowAsJavaScriptException();
97
+ return;
98
+ }
99
+ else if (length <= 1 || !info[1].IsString()) {
100
+ Napi::TypeError::New(env, "SQL query expected").ThrowAsJavaScriptException();
101
+ return;
102
+ }
103
+ else if (length > 2 && !info[2].IsUndefined() && !info[2].IsFunction()) {
104
+ Napi::TypeError::New(env, "Callback expected").ThrowAsJavaScriptException();
105
+ return;
106
+ }
107
+
108
+ this->db = Napi::ObjectWrap<Database>::Unwrap(info[0].As<Napi::Object>());
109
+ this->db->Ref();
110
+
111
+ auto sql = info[1].As<Napi::String>();
112
+
113
+ info.This().As<Napi::Object>().DefineProperty(Napi::PropertyDescriptor::Value("sql", sql, napi_default));
114
+
115
+
116
+ Statement* stmt = this;
117
+
118
+ auto* baton = new PrepareBaton(this->db, info[2].As<Napi::Function>(), stmt);
119
+ baton->sql = std::string(sql.As<Napi::String>().Utf8Value().c_str());
120
+ this->db->Schedule(Work_BeginPrepare, baton);
121
+ }
122
+
123
+ void Statement::Work_BeginPrepare(Database::Baton* baton) {
124
+ assert(baton->db->open);
125
+ baton->db->pending++;
126
+
127
+ auto env = baton->db->Env();
128
+ CREATE_WORK("sqlite3.Statement.Prepare", Work_Prepare, Work_AfterPrepare);
129
+ }
130
+
131
+ void Statement::Work_Prepare(napi_env e, void* data) {
132
+ STATEMENT_INIT(PrepareBaton);
133
+
134
+ // In case preparing fails, we use a mutex to make sure we get the associated
135
+ // error message.
136
+ STATEMENT_MUTEX(mtx);
137
+ sqlite3_mutex_enter(mtx);
138
+
139
+ stmt->status = sqlite3_prepare_v2(
140
+ baton->db->_handle,
141
+ baton->sql.c_str(),
142
+ baton->sql.size(),
143
+ &stmt->_handle,
144
+ NULL
145
+ );
146
+
147
+ if (stmt->status != SQLITE_OK) {
148
+ stmt->message = std::string(sqlite3_errmsg(baton->db->_handle));
149
+ stmt->_handle = NULL;
150
+ }
151
+
152
+ sqlite3_mutex_leave(mtx);
153
+ }
154
+
155
+ void Statement::Work_AfterPrepare(napi_env e, napi_status status, void* data) {
156
+ std::unique_ptr<PrepareBaton> baton(static_cast<PrepareBaton*>(data));
157
+ auto* stmt = baton->stmt;
158
+
159
+ auto env = stmt->Env();
160
+ Napi::HandleScope scope(env);
161
+
162
+ if (stmt->status != SQLITE_OK) {
163
+ Error(baton.get());
164
+ stmt->Finalize_();
165
+ }
166
+ else {
167
+ stmt->prepared = true;
168
+ if (!baton->callback.IsEmpty() && baton->callback.Value().IsFunction()) {
169
+ Napi::Function cb = baton->callback.Value();
170
+ Napi::Value argv[] = { env.Null() };
171
+ TRY_CATCH_CALL(stmt->Value(), cb, 1, argv);
172
+ }
173
+ }
174
+
175
+ STATEMENT_END();
176
+ }
177
+
178
+ template <class T> std::unique_ptr<Values::Field>
179
+ Statement::BindParameter(const Napi::Value source, T pos) {
180
+ if (source.IsString()) {
181
+ std::string val = source.As<Napi::String>().Utf8Value();
182
+ return std::make_unique<Values::Text>(pos, val.length(), val.c_str());
183
+ }
184
+ else if (OtherInstanceOf(source.As<Object>(), "RegExp")) {
185
+ std::string val = source.ToString().Utf8Value();
186
+ return std::make_unique<Values::Text>(pos, val.length(), val.c_str());
187
+ }
188
+ else if (source.IsNumber()) {
189
+ if (OtherIsInt(source.As<Napi::Number>())) {
190
+ return std::make_unique<Values::Integer>(pos, source.As<Napi::Number>().Int32Value());
191
+ } else {
192
+ return std::make_unique<Values::Float>(pos, source.As<Napi::Number>().DoubleValue());
193
+ }
194
+ }
195
+ else if (source.IsBoolean()) {
196
+ return std::make_unique<Values::Integer>(pos, source.As<Napi::Boolean>().Value() ? 1 : 0);
197
+ }
198
+ else if (source.IsNull()) {
199
+ return std::make_unique<Values::Null>(pos);
200
+ }
201
+ else if (source.IsBuffer()) {
202
+ Napi::Buffer<char> buffer = source.As<Napi::Buffer<char>>();
203
+ return std::make_unique<Values::Blob>(pos, buffer.Length(), buffer.Data());
204
+ }
205
+ else if (OtherInstanceOf(source.As<Object>(), "Date")) {
206
+ return std::make_unique<Values::Float>(pos, source.ToNumber().DoubleValue());
207
+ }
208
+ else if (source.IsObject()) {
209
+ auto napiVal = Napi::String::New(source.Env(), "[object Object]");
210
+ // Check whether toString returned a value that is not undefined.
211
+ if(napiVal.Type() == 0) {
212
+ return NULL;
213
+ }
214
+
215
+ std::string val = napiVal.Utf8Value();
216
+ return std::make_unique<Values::Text>(pos, val.length(), val.c_str());
217
+ }
218
+ else {
219
+ return NULL;
220
+ }
221
+ }
222
+
223
+ template <class T> T* Statement::Bind(const Napi::CallbackInfo& info, int start, int last) {
224
+ auto env = info.Env();
225
+ Napi::HandleScope scope(env);
226
+
227
+ if (last < 0) last = info.Length();
228
+ Napi::Function callback;
229
+ if (last > start && info[last - 1].IsFunction()) {
230
+ callback = info[last - 1].As<Napi::Function>();
231
+ last--;
232
+ }
233
+
234
+ auto *baton = new T(this, callback);
235
+
236
+ if (start < last) {
237
+ if (info[start].IsArray()) {
238
+ auto array = info[start].As<Napi::Array>();
239
+ int length = array.Length();
240
+ // Note: bind parameters start with 1.
241
+ for (int i = 0, pos = 1; i < length; i++, pos++) {
242
+ baton->parameters.emplace_back(BindParameter((array).Get(i), i + 1));
243
+ }
244
+ }
245
+ else if (!info[start].IsObject() || OtherInstanceOf(info[start].As<Object>(), "RegExp")
246
+ || OtherInstanceOf(info[start].As<Object>(), "Date") || info[start].IsBuffer()) {
247
+ // Parameters directly in array.
248
+ // Note: bind parameters start with 1.
249
+ for (int i = start, pos = 1; i < last; i++, pos++) {
250
+ baton->parameters.emplace_back(BindParameter(info[i], pos));
251
+ }
252
+ }
253
+ else if (info[start].IsObject()) {
254
+ auto object = info[start].As<Napi::Object>();
255
+ auto array = object.GetPropertyNames();
256
+ int length = array.Length();
257
+ for (int i = 0; i < length; i++) {
258
+ Napi::Value name = (array).Get(i);
259
+ Napi::Number num = name.ToNumber();
260
+
261
+ if (num.Int32Value() == num.DoubleValue()) {
262
+ baton->parameters.emplace_back(
263
+ BindParameter((object).Get(name), num.Int32Value()));
264
+ }
265
+ else {
266
+ baton->parameters.emplace_back(BindParameter((object).Get(name),
267
+ name.As<Napi::String>().Utf8Value().c_str()));
268
+ }
269
+ }
270
+ }
271
+ else {
272
+ return NULL;
273
+ }
274
+ }
275
+
276
+ return baton;
277
+ }
278
+
279
+ bool Statement::Bind(const Parameters & parameters) {
280
+ if (parameters.empty()) {
281
+ return true;
282
+ }
283
+
284
+ sqlite3_reset(_handle);
285
+ sqlite3_clear_bindings(_handle);
286
+
287
+ for (auto& field : parameters) {
288
+ if (field == NULL)
289
+ continue;
290
+
291
+ unsigned int pos;
292
+ if (field->index > 0) {
293
+ pos = field->index;
294
+ }
295
+ else {
296
+ pos = sqlite3_bind_parameter_index(_handle, field->name.c_str());
297
+ }
298
+
299
+ switch (field->type) {
300
+ case SQLITE_INTEGER: {
301
+ status = sqlite3_bind_int(_handle, pos,
302
+ (static_cast<Values::Integer*>(field.get()))->value);
303
+ } break;
304
+ case SQLITE_FLOAT: {
305
+ status = sqlite3_bind_double(_handle, pos,
306
+ (static_cast<Values::Float*>(field.get()))->value);
307
+ } break;
308
+ case SQLITE_TEXT: {
309
+ status = sqlite3_bind_text(_handle, pos,
310
+ (static_cast<Values::Text*>(field.get()))->value.c_str(),
311
+ (static_cast<Values::Text*>(field.get()))->value.size(), SQLITE_TRANSIENT);
312
+ } break;
313
+ case SQLITE_BLOB: {
314
+ status = sqlite3_bind_blob(_handle, pos,
315
+ (static_cast<Values::Blob*>(field.get()))->value,
316
+ (static_cast<Values::Blob*>(field.get()))->length, SQLITE_TRANSIENT);
317
+ } break;
318
+ case SQLITE_NULL: {
319
+ status = sqlite3_bind_null(_handle, pos);
320
+ } break;
321
+ }
322
+
323
+ if (status != SQLITE_OK) {
324
+ message = std::string(sqlite3_errmsg(db->_handle));
325
+ return false;
326
+ }
327
+ }
328
+
329
+ return true;
330
+ }
331
+
332
+ Napi::Value Statement::Bind(const Napi::CallbackInfo& info) {
333
+ auto env = info.Env();
334
+ Statement* stmt = this;
335
+
336
+ auto baton = stmt->Bind<Baton>(info);
337
+ if (baton == NULL) {
338
+ Napi::TypeError::New(env, "Data type is not supported").ThrowAsJavaScriptException();
339
+ return env.Null();
340
+ }
341
+ else {
342
+ stmt->Schedule(Work_BeginBind, baton);
343
+ return info.This();
344
+ }
345
+ }
346
+
347
+ void Statement::Work_BeginBind(Baton* baton) {
348
+ STATEMENT_BEGIN(Bind);
349
+ }
350
+
351
+ void Statement::Work_Bind(napi_env e, void* data) {
352
+ STATEMENT_INIT(Baton);
353
+
354
+ STATEMENT_MUTEX(mtx);
355
+ sqlite3_mutex_enter(mtx);
356
+ stmt->Bind(baton->parameters);
357
+ sqlite3_mutex_leave(mtx);
358
+ }
359
+
360
+ void Statement::Work_AfterBind(napi_env e, napi_status status, void* data) {
361
+ std::unique_ptr<Baton> baton(static_cast<Baton*>(data));
362
+ auto* stmt = baton->stmt;
363
+
364
+ auto env = stmt->Env();
365
+ Napi::HandleScope scope(env);
366
+
367
+ if (stmt->status != SQLITE_OK) {
368
+ Error(baton.get());
369
+ }
370
+ else {
371
+ // Fire callbacks.
372
+ Napi::Function cb = baton->callback.Value();
373
+ if (IS_FUNCTION(cb)) {
374
+ Napi::Value argv[] = { env.Null() };
375
+ TRY_CATCH_CALL(stmt->Value(), cb, 1, argv);
376
+ }
377
+ }
378
+
379
+ STATEMENT_END();
380
+ }
381
+
382
+
383
+
384
+ Napi::Value Statement::Get(const Napi::CallbackInfo& info) {
385
+ auto env = info.Env();
386
+ Statement* stmt = this;
387
+
388
+ Baton* baton = stmt->Bind<RowBaton>(info);
389
+ if (baton == NULL) {
390
+ Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException();
391
+ return env.Null();
392
+ }
393
+ else {
394
+ stmt->Schedule(Work_BeginGet, baton);
395
+ return info.This();
396
+ }
397
+ }
398
+
399
+ void Statement::Work_BeginGet(Baton* baton) {
400
+ STATEMENT_BEGIN(Get);
401
+ }
402
+
403
+ void Statement::Work_Get(napi_env e, void* data) {
404
+ STATEMENT_INIT(RowBaton);
405
+
406
+ if (stmt->status != SQLITE_DONE || baton->parameters.size()) {
407
+ STATEMENT_MUTEX(mtx);
408
+ sqlite3_mutex_enter(mtx);
409
+
410
+ if (stmt->Bind(baton->parameters)) {
411
+ stmt->status = sqlite3_step(stmt->_handle);
412
+
413
+ if (!(stmt->status == SQLITE_ROW || stmt->status == SQLITE_DONE)) {
414
+ stmt->message = std::string(sqlite3_errmsg(stmt->db->_handle));
415
+ }
416
+ }
417
+
418
+ sqlite3_mutex_leave(mtx);
419
+
420
+ if (stmt->status == SQLITE_ROW) {
421
+ // Acquire one result row before returning.
422
+ GetRow(&baton->row, stmt->_handle);
423
+ }
424
+ }
425
+ }
426
+
427
+ void Statement::Work_AfterGet(napi_env e, napi_status status, void* data) {
428
+ std::unique_ptr<RowBaton> baton(static_cast<RowBaton*>(data));
429
+ auto* stmt = baton->stmt;
430
+
431
+ auto env = stmt->Env();
432
+ Napi::HandleScope scope(env);
433
+
434
+ if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) {
435
+ Error(baton.get());
436
+ }
437
+ else {
438
+ // Fire callbacks.
439
+ Napi::Function cb = baton->callback.Value();
440
+ if (IS_FUNCTION(cb)) {
441
+ if (stmt->status == SQLITE_ROW) {
442
+ // Create the result array from the data we acquired.
443
+ Napi::Value argv[] = { env.Null(), RowToJS(env, &baton->row) };
444
+ TRY_CATCH_CALL(stmt->Value(), cb, 2, argv);
445
+ }
446
+ else {
447
+ Napi::Value argv[] = { env.Null() };
448
+ TRY_CATCH_CALL(stmt->Value(), cb, 1, argv);
449
+ }
450
+ }
451
+ }
452
+
453
+ STATEMENT_END();
454
+ }
455
+
456
+ Napi::Value Statement::Run(const Napi::CallbackInfo& info) {
457
+ auto env = info.Env();
458
+ Statement* stmt = this;
459
+
460
+ Baton* baton = stmt->Bind<RunBaton>(info);
461
+ if (baton == NULL) {
462
+ Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException();
463
+ return env.Null();
464
+ }
465
+ else {
466
+ stmt->Schedule(Work_BeginRun, baton);
467
+ return info.This();
468
+ }
469
+ }
470
+
471
+ void Statement::Work_BeginRun(Baton* baton) {
472
+ STATEMENT_BEGIN(Run);
473
+ }
474
+
475
+ void Statement::Work_Run(napi_env e, void* data) {
476
+ STATEMENT_INIT(RunBaton);
477
+
478
+ STATEMENT_MUTEX(mtx);
479
+ sqlite3_mutex_enter(mtx);
480
+
481
+ // Make sure that we also reset when there are no parameters.
482
+ if (!baton->parameters.size()) {
483
+ sqlite3_reset(stmt->_handle);
484
+ }
485
+
486
+ if (stmt->Bind(baton->parameters)) {
487
+ stmt->status = sqlite3_step(stmt->_handle);
488
+
489
+ if (!(stmt->status == SQLITE_ROW || stmt->status == SQLITE_DONE)) {
490
+ stmt->message = std::string(sqlite3_errmsg(stmt->db->_handle));
491
+ }
492
+ else {
493
+ baton->inserted_id = sqlite3_last_insert_rowid(stmt->db->_handle);
494
+ baton->changes = sqlite3_changes(stmt->db->_handle);
495
+ }
496
+ }
497
+
498
+ sqlite3_mutex_leave(mtx);
499
+ }
500
+
501
+ void Statement::Work_AfterRun(napi_env e, napi_status status, void* data) {
502
+ std::unique_ptr<RunBaton> baton(static_cast<RunBaton*>(data));
503
+ auto* stmt = baton->stmt;
504
+
505
+ auto env = stmt->Env();
506
+ Napi::HandleScope scope(env);
507
+
508
+ if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) {
509
+ Error(baton.get());
510
+ }
511
+ else {
512
+ // Fire callbacks.
513
+ Napi::Function cb = baton->callback.Value();
514
+ if (IS_FUNCTION(cb)) {
515
+ (stmt->Value()).Set(Napi::String::New(env, "lastID"), Napi::Number::New(env, baton->inserted_id));
516
+ (stmt->Value()).Set( Napi::String::New(env, "changes"), Napi::Number::New(env, baton->changes));
517
+
518
+ Napi::Value argv[] = { env.Null() };
519
+ TRY_CATCH_CALL(stmt->Value(), cb, 1, argv);
520
+ }
521
+ }
522
+
523
+ STATEMENT_END();
524
+ }
525
+
526
+ Napi::Value Statement::All(const Napi::CallbackInfo& info) {
527
+ auto env = info.Env();
528
+ Statement* stmt = this;
529
+
530
+ Baton* baton = stmt->Bind<RowsBaton>(info);
531
+ if (baton == NULL) {
532
+ Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException();
533
+ return env.Null();
534
+ }
535
+ else {
536
+ stmt->Schedule(Work_BeginAll, baton);
537
+ return info.This();
538
+ }
539
+ }
540
+
541
+ void Statement::Work_BeginAll(Baton* baton) {
542
+ STATEMENT_BEGIN(All);
543
+ }
544
+
545
+ void Statement::Work_All(napi_env e, void* data) {
546
+ STATEMENT_INIT(RowsBaton);
547
+
548
+ STATEMENT_MUTEX(mtx);
549
+ sqlite3_mutex_enter(mtx);
550
+
551
+ // Make sure that we also reset when there are no parameters.
552
+ if (!baton->parameters.size()) {
553
+ sqlite3_reset(stmt->_handle);
554
+ }
555
+
556
+ if (stmt->Bind(baton->parameters)) {
557
+ while ((stmt->status = sqlite3_step(stmt->_handle)) == SQLITE_ROW) {
558
+ auto row = std::make_unique<Row>();
559
+ GetRow(row.get(), stmt->_handle);
560
+ baton->rows.emplace_back(std::move(row));
561
+ }
562
+
563
+ if (stmt->status != SQLITE_DONE) {
564
+ stmt->message = std::string(sqlite3_errmsg(stmt->db->_handle));
565
+ }
566
+ }
567
+
568
+ sqlite3_mutex_leave(mtx);
569
+ }
570
+
571
+ void Statement::Work_AfterAll(napi_env e, napi_status status, void* data) {
572
+ std::unique_ptr<RowsBaton> baton(static_cast<RowsBaton*>(data));
573
+ auto* stmt = baton->stmt;
574
+
575
+ auto env = stmt->Env();
576
+ Napi::HandleScope scope(env);
577
+
578
+ if (stmt->status != SQLITE_DONE) {
579
+ Error(baton.get());
580
+ }
581
+ else {
582
+ // Fire callbacks.
583
+ Napi::Function cb = baton->callback.Value();
584
+ if (IS_FUNCTION(cb)) {
585
+ if (baton->rows.size()) {
586
+ // Create the result array from the data we acquired.
587
+ Napi::Array result(Napi::Array::New(env, baton->rows.size()));
588
+ auto it = static_cast<Rows::const_iterator>(baton->rows.begin());
589
+ decltype(it) end = baton->rows.end();
590
+ for (int i = 0; it < end; ++it, i++) {
591
+ (result).Set(i, RowToJS(env, it->get()));
592
+ }
593
+
594
+ Napi::Value argv[] = { env.Null(), result };
595
+ TRY_CATCH_CALL(stmt->Value(), cb, 2, argv);
596
+ }
597
+ else {
598
+ // There were no result rows.
599
+ Napi::Value argv[] = {
600
+ env.Null(),
601
+ Napi::Array::New(env, 0)
602
+ };
603
+ TRY_CATCH_CALL(stmt->Value(), cb, 2, argv);
604
+ }
605
+ }
606
+ }
607
+
608
+ STATEMENT_END();
609
+ }
610
+
611
+ Napi::Value Statement::Each(const Napi::CallbackInfo& info) {
612
+ auto env = info.Env();
613
+ Statement* stmt = this;
614
+
615
+ int last = info.Length();
616
+
617
+ Napi::Function completed;
618
+ if (last >= 2 && info[last - 1].IsFunction() && info[last - 2].IsFunction()) {
619
+ completed = info[--last].As<Napi::Function>();
620
+ }
621
+
622
+ auto baton = stmt->Bind<EachBaton>(info, 0, last);
623
+ if (baton == NULL) {
624
+ Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException();
625
+ return env.Null();
626
+ }
627
+ else {
628
+ baton->completed.Reset(completed, 1);
629
+ stmt->Schedule(Work_BeginEach, baton);
630
+ return info.This();
631
+ }
632
+ }
633
+
634
+ void Statement::Work_BeginEach(Baton* baton) {
635
+ // Only create the Async object when we're actually going into
636
+ // the event loop. This prevents dangling events.
637
+ auto* each_baton = static_cast<EachBaton*>(baton);
638
+ each_baton->async = new Async(each_baton->stmt, reinterpret_cast<uv_async_cb>(AsyncEach));
639
+ each_baton->async->item_cb.Reset(each_baton->callback.Value(), 1);
640
+ each_baton->async->completed_cb.Reset(each_baton->completed.Value(), 1);
641
+
642
+ STATEMENT_BEGIN(Each);
643
+ }
644
+
645
+ void Statement::Work_Each(napi_env e, void* data) {
646
+ STATEMENT_INIT(EachBaton);
647
+
648
+ auto* async = baton->async;
649
+
650
+ STATEMENT_MUTEX(mtx);
651
+
652
+ // Make sure that we also reset when there are no parameters.
653
+ if (!baton->parameters.size()) {
654
+ sqlite3_reset(stmt->_handle);
655
+ }
656
+
657
+ if (stmt->Bind(baton->parameters)) {
658
+ while (true) {
659
+ sqlite3_mutex_enter(mtx);
660
+ stmt->status = sqlite3_step(stmt->_handle);
661
+ if (stmt->status == SQLITE_ROW) {
662
+ sqlite3_mutex_leave(mtx);
663
+ auto row = std::make_unique<Row>();
664
+ GetRow(row.get(), stmt->_handle);
665
+ NODE_SQLITE3_MUTEX_LOCK(&async->mutex)
666
+ async->data.emplace_back(std::move(row));
667
+ NODE_SQLITE3_MUTEX_UNLOCK(&async->mutex)
668
+
669
+ uv_async_send(&async->watcher);
670
+ }
671
+ else {
672
+ if (stmt->status != SQLITE_DONE) {
673
+ stmt->message = std::string(sqlite3_errmsg(stmt->db->_handle));
674
+ }
675
+ sqlite3_mutex_leave(mtx);
676
+ break;
677
+ }
678
+ }
679
+ }
680
+
681
+ async->completed = true;
682
+ uv_async_send(&async->watcher);
683
+ }
684
+
685
+ void Statement::CloseCallback(uv_handle_t* handle) {
686
+ assert(handle != NULL);
687
+ assert(handle->data != NULL);
688
+ auto* async = static_cast<Async*>(handle->data);
689
+ delete async;
690
+ }
691
+
692
+ void Statement::AsyncEach(uv_async_t* handle) {
693
+ auto* async = static_cast<Async*>(handle->data);
694
+
695
+ auto env = async->stmt->Env();
696
+ Napi::HandleScope scope(env);
697
+
698
+ while (true) {
699
+ // Get the contents out of the data cache for us to process in the JS callback.
700
+ Rows rows;
701
+ NODE_SQLITE3_MUTEX_LOCK(&async->mutex)
702
+ rows.swap(async->data);
703
+ NODE_SQLITE3_MUTEX_UNLOCK(&async->mutex)
704
+
705
+ if (rows.empty()) {
706
+ break;
707
+ }
708
+
709
+ Napi::Function cb = async->item_cb.Value();
710
+ if (IS_FUNCTION(cb)) {
711
+ Napi::Value argv[2];
712
+ argv[0] = env.Null();
713
+
714
+ for(auto& row : rows) {
715
+ argv[1] = RowToJS(env,row.get());
716
+ async->retrieved++;
717
+ TRY_CATCH_CALL(async->stmt->Value(), cb, 2, argv);
718
+ }
719
+ }
720
+ }
721
+
722
+ Napi::Function cb = async->completed_cb.Value();
723
+ if (async->completed) {
724
+ if (!cb.IsEmpty() &&
725
+ cb.IsFunction()) {
726
+ Napi::Value argv[] = {
727
+ env.Null(),
728
+ Napi::Number::New(env, async->retrieved)
729
+ };
730
+ TRY_CATCH_CALL(async->stmt->Value(), cb, 2, argv);
731
+ }
732
+ uv_close(reinterpret_cast<uv_handle_t*>(handle), CloseCallback);
733
+ }
734
+ }
735
+
736
+ void Statement::Work_AfterEach(napi_env e, napi_status status, void* data) {
737
+ std::unique_ptr<EachBaton> baton(static_cast<EachBaton*>(data));
738
+ auto* stmt = baton->stmt;
739
+
740
+ auto env = stmt->Env();
741
+ Napi::HandleScope scope(env);
742
+
743
+ if (stmt->status != SQLITE_DONE) {
744
+ Error(baton.get());
745
+ }
746
+
747
+ STATEMENT_END();
748
+ }
749
+
750
+ Napi::Value Statement::Reset(const Napi::CallbackInfo& info) {
751
+ auto env = info.Env();
752
+ Statement* stmt = this;
753
+
754
+ OPTIONAL_ARGUMENT_FUNCTION(0, callback);
755
+
756
+ auto* baton = new Baton(stmt, callback);
757
+ stmt->Schedule(Work_BeginReset, baton);
758
+
759
+ return info.This();
760
+ }
761
+
762
+ void Statement::Work_BeginReset(Baton* baton) {
763
+ STATEMENT_BEGIN(Reset);
764
+ }
765
+
766
+ void Statement::Work_Reset(napi_env e, void* data) {
767
+ STATEMENT_INIT(Baton);
768
+
769
+ sqlite3_reset(stmt->_handle);
770
+ stmt->status = SQLITE_OK;
771
+ }
772
+
773
+ void Statement::Work_AfterReset(napi_env e, napi_status status, void* data) {
774
+ std::unique_ptr<Baton> baton(static_cast<Baton*>(data));
775
+ auto* stmt = baton->stmt;
776
+
777
+ auto env = stmt->Env();
778
+ Napi::HandleScope scope(env);
779
+
780
+ // Fire callbacks.
781
+ Napi::Function cb = baton->callback.Value();
782
+ if (IS_FUNCTION(cb)) {
783
+ Napi::Value argv[] = { env.Null() };
784
+ TRY_CATCH_CALL(stmt->Value(), cb, 1, argv);
785
+ }
786
+
787
+ STATEMENT_END();
788
+ }
789
+
790
+ Napi::Value Statement::RowToJS(Napi::Env env, Row* row) {
791
+ Napi::EscapableHandleScope scope(env);
792
+
793
+ auto result = Napi::Object::New(env);
794
+
795
+ for (auto& field : *row) {
796
+
797
+ Napi::Value value;
798
+
799
+ switch (field->type) {
800
+ case SQLITE_INTEGER: {
801
+ value = Napi::Number::New(env, (static_cast<Values::Integer*>(field.get()))->value);
802
+ } break;
803
+ case SQLITE_FLOAT: {
804
+ value = Napi::Number::New(env, (static_cast<Values::Float*>(field.get()))->value);
805
+ } break;
806
+ case SQLITE_TEXT: {
807
+ value = Napi::String::New(env, (static_cast<Values::Text*>(field.get()))->value.c_str(),
808
+ (static_cast<Values::Text*>(field.get()))->value.size());
809
+ } break;
810
+ case SQLITE_BLOB: {
811
+ value = Napi::Buffer<char>::Copy(env, (static_cast<Values::Blob*>(field.get()))->value,
812
+ (static_cast<Values::Blob*>(field.get()))->length);
813
+ } break;
814
+ case SQLITE_NULL: {
815
+ value = env.Null();
816
+ } break;
817
+ }
818
+
819
+ result.Set(field->name, value);
820
+ }
821
+
822
+ return scope.Escape(result);
823
+ }
824
+
825
+ void Statement::GetRow(Row* row, sqlite3_stmt* stmt) {
826
+ int cols = sqlite3_column_count(stmt);
827
+
828
+ for (int i = 0; i < cols; i++) {
829
+ int type = sqlite3_column_type(stmt, i);
830
+ const char* name = sqlite3_column_name(stmt, i);
831
+ if (name == NULL) {
832
+ assert(false);
833
+ }
834
+
835
+ switch (type) {
836
+ case SQLITE_INTEGER: {
837
+ row->emplace_back(std::make_unique<Values::Integer>(name, sqlite3_column_int64(stmt, i)));
838
+ } break;
839
+ case SQLITE_FLOAT: {
840
+ row->emplace_back(std::make_unique<Values::Float>(name, sqlite3_column_double(stmt, i)));
841
+ } break;
842
+ case SQLITE_TEXT: {
843
+ const char* text = (const char*)sqlite3_column_text(stmt, i);
844
+ int length = sqlite3_column_bytes(stmt, i);
845
+ row->emplace_back(std::make_unique<Values::Text>(name, length, text));
846
+ } break;
847
+ case SQLITE_BLOB: {
848
+ const void* blob = sqlite3_column_blob(stmt, i);
849
+ int length = sqlite3_column_bytes(stmt, i);
850
+ row->emplace_back(std::make_unique<Values::Blob>(name, length, blob));
851
+ } break;
852
+ case SQLITE_NULL: {
853
+ row->emplace_back(std::make_unique<Values::Null>(name));
854
+ } break;
855
+ default:
856
+ assert(false);
857
+ }
858
+ }
859
+ }
860
+
861
+ Napi::Value Statement::Finalize_(const Napi::CallbackInfo& info) {
862
+ auto env = info.Env();
863
+ Statement* stmt = this;
864
+ OPTIONAL_ARGUMENT_FUNCTION(0, callback);
865
+
866
+ auto *baton = new Baton(stmt, callback);
867
+ stmt->Schedule(Finalize_, baton);
868
+
869
+ return stmt->db->Value();
870
+ }
871
+
872
+ void Statement::Finalize_(Baton* b) {
873
+ auto baton = std::unique_ptr<Baton>(b);
874
+ auto env = baton->stmt->Env();
875
+ Napi::HandleScope scope(env);
876
+
877
+ baton->stmt->Finalize_();
878
+
879
+ // Fire callback in case there was one.
880
+ Napi::Function cb = baton->callback.Value();
881
+ if (IS_FUNCTION(cb)) {
882
+ TRY_CATCH_CALL(baton->stmt->Value(), cb, 0, NULL);
883
+ }
884
+ }
885
+
886
+ void Statement::Finalize_() {
887
+ assert(!finalized);
888
+ finalized = true;
889
+ CleanQueue();
890
+ // Finalize returns the status code of the last operation. We already fired
891
+ // error events in case those failed.
892
+ sqlite3_finalize(_handle);
893
+ _handle = NULL;
894
+ db->Unref();
895
+ }
896
+
897
+ void Statement::CleanQueue() {
898
+ auto env = this->Env();
899
+ Napi::HandleScope scope(env);
900
+
901
+ if (prepared && !queue.empty()) {
902
+ // This statement has already been prepared and is now finalized.
903
+ // Fire error for all remaining items in the queue.
904
+ EXCEPTION(Napi::String::New(env, "Statement is already finalized"), SQLITE_MISUSE, exception);
905
+ Napi::Value argv[] = { exception };
906
+ bool called = false;
907
+
908
+ // Clear out the queue so that this object can get GC'ed.
909
+ while (!queue.empty()) {
910
+ auto call = std::unique_ptr<Call>(queue.front());
911
+ queue.pop();
912
+
913
+ auto baton = std::unique_ptr<Baton>(call->baton);
914
+ Napi::Function cb = baton->callback.Value();
915
+
916
+ if (prepared && !cb.IsEmpty() &&
917
+ cb.IsFunction()) {
918
+ TRY_CATCH_CALL(Value(), cb, 1, argv);
919
+ called = true;
920
+ }
921
+ }
922
+
923
+ // When we couldn't call a callback function, emit an error on the
924
+ // Statement object.
925
+ if (!called) {
926
+ Napi::Value info[] = { Napi::String::New(env, "error"), exception };
927
+ EMIT_EVENT(Value(), 2, info);
928
+ }
929
+ }
930
+ else while (!queue.empty()) {
931
+ // Just delete all items in the queue; we already fired an event when
932
+ // preparing the statement failed.
933
+ auto call = std::unique_ptr<Call>(queue.front());
934
+ queue.pop();
935
+ // We don't call the actual callback, so we have to make sure that
936
+ // the baton gets destroyed.
937
+ delete call->baton;
938
+ }
939
+ }