From bce1561710a5ec47f5255d28b9f4048a13164dcd Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Fri, 9 Sep 2016 17:25:25 -0400 Subject: [PATCH] added .checkpoint method --- binding.gyp | 1 + src/objects/database/checkpoint.cc | 27 +++++++++++ src/objects/database/database.cc | 3 ++ src/objects/database/database.h | 6 ++- src/objects/database/pragma.cc | 12 +++-- src/workers/database-workers/checkpoint.cc | 55 ++++++++++++++++++++++ src/workers/database-workers/checkpoint.h | 19 ++++++++ src/workers/database-workers/close.cc | 38 +++++++-------- src/workers/database-workers/open.cc | 16 +++---- src/workers/database-workers/open.h | 2 +- 10 files changed, 146 insertions(+), 33 deletions(-) create mode 100644 src/objects/database/checkpoint.cc create mode 100644 src/workers/database-workers/checkpoint.cc create mode 100644 src/workers/database-workers/checkpoint.h diff --git a/binding.gyp b/binding.gyp index f0b7b233d..a185f1a56 100644 --- a/binding.gyp +++ b/binding.gyp @@ -23,6 +23,7 @@ "src/objects/transaction/transaction.cc", "src/workers/database-workers/open.cc", "src/workers/database-workers/close.cc", + "src/workers/database-workers/checkpoint.cc", "src/workers/statement-workers/run.cc", "src/workers/statement-workers/get.cc", "src/workers/statement-workers/all.cc", diff --git a/src/objects/database/checkpoint.cc b/src/objects/database/checkpoint.cc new file mode 100644 index 000000000..f242bce90 --- /dev/null +++ b/src/objects/database/checkpoint.cc @@ -0,0 +1,27 @@ +// .checkpoint([boolean force], Function callback) -> this + +NAN_METHOD(Database::Checkpoint) { + bool force; + v8::Local func; + if (info.Length() >= 2) { + TRUTHINESS_OF_ARGUMENT(0, a); + REQUIRE_ARGUMENT_FUNCTION(1, b); + force = a; + func = b; + } else { + force = false; + REQUIRE_ARGUMENT_FUNCTION(0, a); + func = a; + } + + Database* db = Nan::ObjectWrap::Unwrap(info.This()); + if (db->state != DB_READY) { + return Nan::ThrowError("The database connection is not open."); + } + + db->Ref(); + db->workers += 1; + Nan::AsyncQueueWorker(new CheckpointWorker(db, force, new Nan::Callback(func))); + + info.GetReturnValue().Set(info.This()); +} diff --git a/src/objects/database/database.cc b/src/objects/database/database.cc index 29b53dad9..b461bd40e 100644 --- a/src/objects/database/database.cc +++ b/src/objects/database/database.cc @@ -6,6 +6,7 @@ #include "../transaction/transaction.h" #include "../../workers/database-workers/open.h" #include "../../workers/database-workers/close.h" +#include "../../workers/database-workers/checkpoint.h" #include "../../util/macros.h" #include "../../util/data.h" #include "../../util/list.h" @@ -22,6 +23,7 @@ sqlite3_uint64 NEXT_TRANSACTION_ID = 0; #include "create-statement.cc" #include "create-transaction.cc" #include "pragma.cc" +#include "checkpoint.cc" Database::Database() : Nan::ObjectWrap(), read_handle(NULL), @@ -55,6 +57,7 @@ NAN_MODULE_INIT(Database::Init) { Nan::SetPrototypeMethod(t, "statement", CreateStatement); Nan::SetPrototypeMethod(t, "transaction", CreateTransaction); Nan::SetPrototypeMethod(t, "pragma", Pragma); + Nan::SetPrototypeMethod(t, "checkpoint", Checkpoint); Nan::SetAccessor(t->InstanceTemplate(), Nan::New("open").ToLocalChecked(), Open); Nan::Set(target, Nan::New("Database").ToLocalChecked(), diff --git a/src/objects/database/database.h b/src/objects/database/database.h index 54019549e..2f3e0c1cd 100644 --- a/src/objects/database/database.h +++ b/src/objects/database/database.h @@ -22,10 +22,11 @@ class Database : public Nan::ObjectWrap { static NAN_MODULE_INIT(Init); // Friends - friend class OpenWorker; - friend class CloseWorker; friend class Statement; friend class Transaction; + friend class OpenWorker; + friend class CloseWorker; + friend class CheckpointWorker; template friend class QueryWorker; friend class RunWorker; friend class TransactionWorker; @@ -37,6 +38,7 @@ class Database : public Nan::ObjectWrap { static NAN_METHOD(CreateStatement); static NAN_METHOD(CreateTransaction); static NAN_METHOD(Pragma); + static NAN_METHOD(Checkpoint); int CloseHandles(); void MaybeClose(); diff --git a/src/objects/database/pragma.cc b/src/objects/database/pragma.cc index 6016716bc..a746f6323 100644 --- a/src/objects/database/pragma.cc +++ b/src/objects/database/pragma.cc @@ -34,8 +34,9 @@ NAN_METHOD(Database::Pragma) { // Executes the SQL on the read handle. sqlite3_exec(db->read_handle, *utf8, NULL, NULL, &err); if (err != NULL) { - Nan::ThrowError(err); + CONCAT2(message, "SQLite: ", err); sqlite3_free(err); + Nan::ThrowError(message); return; } sqlite3_free(err); @@ -44,15 +45,20 @@ NAN_METHOD(Database::Pragma) { List table[2] {}; sqlite3_exec(db->write_handle, *utf8, PragmaCallback, table, &err); if (err != NULL) { - Nan::ThrowError(err); + CONCAT2(message, "SQLite: ", err); sqlite3_free(err); + Nan::ThrowError(message); return; } sqlite3_free(err); if (simple_result) { Data::Row* values = table[1].Shift(); - info.GetReturnValue().Set(values->values[0]->ToJS()); + if (values == NULL) { + info.GetReturnValue().Set(Nan::Undefined()); + } else { + info.GetReturnValue().Set(values->values[0]->ToJS()); + } delete values; } else { unsigned int i = 0; diff --git a/src/workers/database-workers/checkpoint.cc b/src/workers/database-workers/checkpoint.cc new file mode 100644 index 000000000..44cd7806f --- /dev/null +++ b/src/workers/database-workers/checkpoint.cc @@ -0,0 +1,55 @@ +#include +#include +#include "checkpoint.h" +#include "../../objects/database/database.h" +#include "../../util/macros.h" + +CheckpointWorker::CheckpointWorker(Database* db, bool force, Nan::Callback* cb) : Nan::AsyncWorker(cb), + db(db), + force(force) {} +void CheckpointWorker::Execute() { + sqlite3* db_handle = db->write_handle; + LOCK_DB(db_handle); + + int total_frames; + int checkpointed_frames; + int status = sqlite3_wal_checkpoint_v2( + db_handle, + "main", + force ? SQLITE_CHECKPOINT_RESTART : SQLITE_CHECKPOINT_PASSIVE, + &total_frames, + &checkpointed_frames + ); + + if (status == SQLITE_OK) { + if (checkpointed_frames < 0 || total_frames < 0) { + fraction_checkpointed = 0; + } else if (total_frames == 0) { + fraction_checkpointed = 1; + } else { + fraction_checkpointed = (double)checkpointed_frames / (double)total_frames; + } + } else { + SetErrorMessage(sqlite3_errmsg(db_handle)); + } + + UNLOCK_DB(db_handle); +} +void CheckpointWorker::HandleOKCallback() { + Nan::HandleScope scope; + if (--db->workers == 0) {db->Unref();} + + v8::Local args[2] = { + Nan::Null(), + Nan::New(fraction_checkpointed) + }; + Nan::AsyncWorker::callback->Call(2, args); +} +void CheckpointWorker::HandleErrorCallback() { + Nan::HandleScope scope; + if (--db->workers == 0) {db->Unref();} + + CONCAT2(message, "SQLite: ", Nan::AsyncWorker::ErrorMessage()); + v8::Local args[1] = {Nan::Error(message)}; + Nan::AsyncWorker::callback->Call(1, args); +} diff --git a/src/workers/database-workers/checkpoint.h b/src/workers/database-workers/checkpoint.h new file mode 100644 index 000000000..e587e01da --- /dev/null +++ b/src/workers/database-workers/checkpoint.h @@ -0,0 +1,19 @@ +#ifndef BETTER_SQLITE3_WORKER_CHECKPOINT_H +#define BETTER_SQLITE3_WORKER_CHECKPOINT_H + +#include +class Database; + +class CheckpointWorker : public Nan::AsyncWorker { + public: + CheckpointWorker(Database*, bool, Nan::Callback*); + void Execute(); + void HandleOKCallback(); + void HandleErrorCallback(); + private: + Database* const db; + bool force; + double fraction_checkpointed; +}; + +#endif \ No newline at end of file diff --git a/src/workers/database-workers/close.cc b/src/workers/database-workers/close.cc index bef7a79b0..0e205b2a1 100644 --- a/src/workers/database-workers/close.cc +++ b/src/workers/database-workers/close.cc @@ -18,28 +18,28 @@ void CloseWorker::Execute() { } void CloseWorker::HandleOKCallback() { Nan::HandleScope scope; - v8::Local database = db->handle(); - - if (--db->workers == 0) {db->Unref();} - - v8::Local args[2] = { - NEW_INTERNAL_STRING_FAST("close"), - Nan::Null() - }; - + v8::Local database = db->handle(); + + if (--db->workers == 0) {db->Unref();} + + v8::Local args[2] = { + NEW_INTERNAL_STRING_FAST("close"), + Nan::Null() + }; + EMIT_EVENT(database, 2, args); } void CloseWorker::HandleErrorCallback() { Nan::HandleScope scope; - v8::Local database = db->handle(); - - if (--db->workers == 0) {db->Unref();} - - CONCAT2(message, "SQLite: ", ErrorMessage()); - v8::Local args[2] = { - NEW_INTERNAL_STRING_FAST("close"), - Nan::Error(message) - }; - + v8::Local database = db->handle(); + + if (--db->workers == 0) {db->Unref();} + + CONCAT2(message, "SQLite: ", ErrorMessage()); + v8::Local args[2] = { + NEW_INTERNAL_STRING_FAST("close"), + Nan::Error(message) + }; + EMIT_EVENT(database, 2, args); } diff --git a/src/workers/database-workers/open.cc b/src/workers/database-workers/open.cc index 8febb75cf..a5a6fa008 100644 --- a/src/workers/database-workers/open.cc +++ b/src/workers/database-workers/open.cc @@ -43,10 +43,10 @@ void OpenWorker::Execute() { } void OpenWorker::HandleOKCallback() { Nan::HandleScope scope; - v8::Local database = db->handle(); - - if (--db->workers == 0) {db->Unref();} - + v8::Local database = db->handle(); + + if (--db->workers == 0) {db->Unref();} + if (db->state == DB_DONE) { db->CloseHandles(); } else { @@ -57,10 +57,10 @@ void OpenWorker::HandleOKCallback() { } void OpenWorker::HandleErrorCallback() { Nan::HandleScope scope; - v8::Local database = db->handle(); - - if (--db->workers == 0) {db->Unref();} - + v8::Local database = db->handle(); + + if (--db->workers == 0) {db->Unref();} + if (db->state != DB_DONE) { db->state = DB_DONE; diff --git a/src/workers/database-workers/open.h b/src/workers/database-workers/open.h index 8860223d9..392ae0ed8 100644 --- a/src/workers/database-workers/open.h +++ b/src/workers/database-workers/open.h @@ -6,7 +6,7 @@ class Database; class OpenWorker : public Nan::AsyncWorker { public: - OpenWorker(Database*, char*, bool); + OpenWorker(Database*, char*); ~OpenWorker(); void Execute(); void HandleOKCallback();