From 2a019218e1635c6bfe507f14d2bdff3cb68570a5 Mon Sep 17 00:00:00 2001 From: Marc Schraffenberger Date: Wed, 20 Sep 2017 14:49:01 -0500 Subject: [PATCH 1/3] added backup function to database object --- src/database.cc | 82 +++++++++++++++++++++++++++++++++++++++++++++ src/database.h | 11 ++++++ src/macros.h | 1 + test/backup.test.js | 14 ++++++++ 4 files changed, 108 insertions(+) create mode 100644 test/backup.test.js diff --git a/src/database.cc b/src/database.cc index e7891cbd2..2639d2a37 100644 --- a/src/database.cc +++ b/src/database.cc @@ -24,6 +24,7 @@ NAN_MODULE_INIT(Database::Init) { Nan::SetPrototypeMethod(t, "parallelize", Parallelize); Nan::SetPrototypeMethod(t, "configure", Configure); Nan::SetPrototypeMethod(t, "interrupt", Interrupt); + Nan::SetPrototypeMethod(t, "backup", Backup); NODE_SET_GETTER(t, "open", OpenGetter); @@ -143,6 +144,7 @@ NAN_METHOD(Database::New) { void Database::Work_BeginOpen(Baton* baton) { int status = uv_queue_work(uv_default_loop(), &baton->request, Work_Open, (uv_after_work_cb)Work_AfterOpen); + UNUSED_VARIABLE(status); assert(status == 0); } @@ -229,6 +231,7 @@ void Database::Work_BeginClose(Baton* baton) { int status = uv_queue_work(uv_default_loop(), &baton->request, Work_Close, (uv_after_work_cb)Work_AfterClose); + UNUSED_VARIABLE(status); assert(status == 0); } @@ -371,6 +374,83 @@ NAN_METHOD(Database::Interrupt) { info.GetReturnValue().Set(info.This()); } +NAN_METHOD(Database::Backup) { + Database* db = Nan::ObjectWrap::Unwrap(info.This()); + + REQUIRE_ARGUMENT_STRING(0, filename); + OPTIONAL_ARGUMENT_FUNCTION(1, callback); + + Baton* baton = new BackupBaton(db, callback, *filename); + db->Schedule(Work_BeginBackup, baton, false); + + info.GetReturnValue().Set(info.This()); +} + +void Database::Work_BeginBackup(Baton* baton) { + assert(baton->db->open); + assert(baton->db->_handle); + int status = uv_queue_work(uv_default_loop(), + &baton->request, Work_Backup, (uv_after_work_cb)Work_AfterExec); + UNUSED_VARIABLE(status); + assert(status == 0); +} + +void Database::Work_Backup(uv_work_t* req) { + BackupBaton* baton = static_cast(req->data); + sqlite3* file; + sqlite3_backup* backup; + + baton->status = sqlite3_open(baton->filename.c_str(), &file); + + if (baton->status == SQLITE_OK) { + backup = sqlite3_backup_init(file, "main", baton->db->_handle, "main"); + + if (backup) { + if (sqlite3_backup_step(backup, -1) == SQLITE_OK) { + sqlite3_backup_finish(backup); + } + } + + baton->status = sqlite3_errcode(file); + } + + sqlite3_close(file); + + if (baton->status != SQLITE_OK) { + baton->message = std::string(sqlite3_errmsg(file)); + } +} + +void Database::Work_AfterBackup(uv_work_t* req) { + Nan::HandleScope scope; + + BackupBaton* baton = static_cast(req->data); + Database* db = baton->db; + + Local cb = Nan::New(baton->callback); + + if (baton->status != SQLITE_OK) { + EXCEPTION(Nan::New(baton->message.c_str()).ToLocalChecked(), baton->status, exception); + + if (!cb.IsEmpty() && cb->IsFunction()) { + Local argv[] = { exception }; + TRY_CATCH_CALL(db->handle(), cb, 1, argv); + } + else { + Local info[] = { Nan::New("error").ToLocalChecked(), exception }; + EMIT_EVENT(db->handle(), 2, info); + } + } + else if (!cb.IsEmpty() && cb->IsFunction()) { + Local argv[] = { Nan::Null() }; + TRY_CATCH_CALL(db->handle(), cb, 1, argv); + } + + db->Process(); + + delete baton; +} + void Database::SetBusyTimeout(Baton* baton) { assert(baton->db->open); assert(baton->db->_handle); @@ -524,6 +604,7 @@ void Database::Work_BeginExec(Baton* baton) { assert(baton->db->pending == 0); int status = uv_queue_work(uv_default_loop(), &baton->request, Work_Exec, (uv_after_work_cb)Work_AfterExec); + UNUSED_VARIABLE(status); assert(status == 0); } @@ -624,6 +705,7 @@ void Database::Work_BeginLoadExtension(Baton* baton) { assert(baton->db->pending == 0); int status = uv_queue_work(uv_default_loop(), &baton->request, Work_LoadExtension, reinterpret_cast(Work_AfterLoadExtension)); + UNUSED_VARIABLE(status); assert(status == 0); } diff --git a/src/database.h b/src/database.h index c5455abe3..40fb27267 100644 --- a/src/database.h +++ b/src/database.h @@ -68,6 +68,12 @@ class Database : public Nan::ObjectWrap { Baton(db_, cb_), filename(filename_) {} }; + struct BackupBaton : Baton { + std::string filename; + BackupBaton(Database* db_, Local cb_, const char* filename_) : + Baton(db_, cb_), filename(filename_) {} + }; + typedef void (*Work_Callback)(Baton* baton); struct Call { @@ -154,6 +160,11 @@ class Database : public Nan::ObjectWrap { static NAN_METHOD(Interrupt); + static NAN_METHOD(Backup); + static void Work_BeginBackup(Baton* baton); + static void Work_Backup(uv_work_t* req); + static void Work_AfterBackup(uv_work_t* req); + static void SetBusyTimeout(Baton* baton); static void RegisterTraceCallback(Baton* baton); diff --git a/src/macros.h b/src/macros.h index 38399ee86..63d75dc90 100644 --- a/src/macros.h +++ b/src/macros.h @@ -4,6 +4,7 @@ const char* sqlite_code_string(int code); const char* sqlite_authorizer_string(int type); +#define UNUSED_VARIABLE(var) (void)var #define REQUIRE_ARGUMENTS(n) \ if (info.Length() < (n)) { \ diff --git a/test/backup.test.js b/test/backup.test.js new file mode 100644 index 000000000..14fb122bc --- /dev/null +++ b/test/backup.test.js @@ -0,0 +1,14 @@ +var sqlite3 = require('..'); +var assert = require('assert'); + +describe('backup', function() { + var db; + before(function(done) { + db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); + }); + + it('backup database', function(done) { + db.backup('test/support/backup.db', done); + }); +}); + From 0248b5df08050315cfd4244f13eb056485f32b6c Mon Sep 17 00:00:00 2001 From: Kewde Date: Sun, 13 Jan 2019 20:23:39 +0000 Subject: [PATCH 2/3] make compatible with v8 7.1 --- src/database.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database.cc b/src/database.cc index 2639d2a37..077cd3279 100644 --- a/src/database.cc +++ b/src/database.cc @@ -430,7 +430,7 @@ void Database::Work_AfterBackup(uv_work_t* req) { Local cb = Nan::New(baton->callback); if (baton->status != SQLITE_OK) { - EXCEPTION(Nan::New(baton->message.c_str()).ToLocalChecked(), baton->status, exception); + EXCEPTION(baton->message, baton->status, exception); if (!cb.IsEmpty() && cb->IsFunction()) { Local argv[] = { exception }; From 0aa7aa5f0f332cfc5bd00222f2fcc636d50df0af Mon Sep 17 00:00:00 2001 From: Kewde Date: Sun, 13 Jan 2019 20:49:56 +0000 Subject: [PATCH 3/3] Update brew (node) --- scripts/build_against_node.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/build_against_node.sh b/scripts/build_against_node.sh index 2982740f2..e599f68df 100755 --- a/scripts/build_against_node.sh +++ b/scripts/build_against_node.sh @@ -29,6 +29,7 @@ publish # now test building against shared sqlite export NODE_SQLITE3_JSON1=no if [[ $(uname -s) == 'Darwin' ]]; then + brew update brew install sqlite npm install --build-from-source --sqlite=$(brew --prefix) --clang=1 else