UNPKG

29.4 kBtext/x-cView Raw
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
9using namespace node_sqlite3;
10
11Napi::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"
32bool 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
42void 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
55void 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
68template <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 }
91Statement::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
123void 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
131void 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
155void 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
178template <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
223template <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
279bool 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
332Napi::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
347void Statement::Work_BeginBind(Baton* baton) {
348 STATEMENT_BEGIN(Bind);
349}
350
351void 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
360void 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
384Napi::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
399void Statement::Work_BeginGet(Baton* baton) {
400 STATEMENT_BEGIN(Get);
401}
402
403void 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
427void 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
456Napi::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
471void Statement::Work_BeginRun(Baton* baton) {
472 STATEMENT_BEGIN(Run);
473}
474
475void 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
501void 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
526Napi::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
541void Statement::Work_BeginAll(Baton* baton) {
542 STATEMENT_BEGIN(All);
543}
544
545void 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
571void 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
611Napi::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
634void 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
645void 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
685void 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
692void 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
736void 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
750Napi::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
762void Statement::Work_BeginReset(Baton* baton) {
763 STATEMENT_BEGIN(Reset);
764}
765
766void 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
773void 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
790Napi::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
825void 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
861Napi::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
872void 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
886void 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
897void 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}