1+ #include " async_wrap-inl.h"
12#include " node_sqlite.h"
23#include < path.h>
34#include " base_object-inl.h"
910#include " node_mem-inl.h"
1011#include " sqlite3.h"
1112#include " util-inl.h"
13+ #include " threadpoolwork-inl.h"
1214
1315#include < cinttypes>
1416
17+ #include < chrono>
18+ #include < thread>
19+ #include < format>
20+ #include < iostream>
21+
1522namespace node {
1623namespace sqlite {
1724
1825using v8::Array;
26+ using v8::HandleScope;
1927using v8::ArrayBuffer;
2028using v8::BigInt;
2129using v8::Boolean;
@@ -40,6 +48,7 @@ using v8::NewStringType;
4048using v8::Null;
4149using v8::Number;
4250using v8::Object;
51+ using v8::Promise;
4352using v8::SideEffectType;
4453using v8::String;
4554using v8::TryCatch;
@@ -128,6 +137,104 @@ inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, int errcode) {
128137 isolate->ThrowException (error);
129138}
130139
140+ class BackupJob : public ThreadPoolWork {
141+ public:
142+ explicit BackupJob (Environment* env,
143+ DatabaseSync* source,
144+ Local<Promise::Resolver> resolver,
145+ const char * source_db,
146+ const char * destination_name,
147+ const char * dest_db,
148+ int pages,
149+ int sleep,
150+ Local<Function> progressFunc) : ThreadPoolWork(env, " node_sqlite3.BackupJob" ),
151+ env_(env),
152+ /* progressFunc_(progressFunc), */
153+ source_(source),
154+ source_db_(source_db),
155+ destination_name_(destination_name),
156+ dest_db_(dest_db),
157+ pages_(pages),
158+ sleep_(sleep) {
159+ resolver_.Reset (env->isolate (), resolver);
160+ progressFunc_.Reset (env->isolate (), progressFunc);
161+ }
162+
163+ void DoThreadPoolWork () override {
164+ HandleScope handle_scope (env ()->isolate ());
165+ sqlite3 *pDest;
166+ sqlite3_backup *pBackup;
167+
168+ int rc = sqlite3_open (destination_name_.c_str (), &pDest);
169+
170+ if (rc != SQLITE_OK) {
171+ sqlite3_close (pDest);
172+ backup_status_ = -1 ;
173+
174+ return ;
175+ }
176+
177+ pBackup = sqlite3_backup_init (pDest, " main" , source_->Connection (), source_db_.c_str ());
178+
179+ if (pBackup == nullptr ) {
180+ sqlite3_close (pDest);
181+ backup_status_ = -1 ;
182+
183+ return ;
184+ }
185+
186+ Local<Function> fn = Local<Function>::New (env ()->isolate (), progressFunc_);
187+
188+ do {
189+ rc = sqlite3_backup_step (pBackup, pages_);
190+ /* fn->Call(env()->context(), Undefined(env()->isolate()), 0, nullptr); */
191+
192+
193+ if (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {
194+ sqlite3_sleep (sleep_);
195+ }
196+ } while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);
197+
198+ sqlite3_backup_finish (pBackup);
199+ rc = sqlite3_errcode (pDest);
200+
201+ if (rc == SQLITE_OK) {
202+ backup_status_ = 0 ;
203+ } else {
204+ backup_status_ = -1 ;
205+ }
206+
207+ sqlite3_close (pDest);
208+ }
209+
210+ void AfterThreadPoolWork (int status) override {
211+ HandleScope handle_scope (env ()->isolate ());
212+
213+ if (!resolver_.IsEmpty ()) {
214+ Local<Promise::Resolver> resolver = Local<Promise::Resolver>::New (env ()->isolate (), resolver_);
215+ Local<String> message = String::NewFromUtf8 (env ()->isolate (), " Backup completed" , NewStringType::kNormal ).ToLocalChecked ();
216+
217+ resolver->Resolve (env ()->context (), message).ToChecked ();
218+ }
219+ }
220+
221+ private:
222+ // https://github.com/nodejs/node/blob/649da3b8377e030ea7b9a1bc0308451e26e28740/src/crypto/crypto_keygen.h#L126
223+ int backup_status_;
224+
225+ Environment* env () const { return env_; }
226+
227+ Environment* env_;
228+ DatabaseSync* source_;
229+ Global<Promise::Resolver> resolver_;
230+ Global<Function> progressFunc_;
231+ std::string source_db_;
232+ std::string destination_name_;
233+ std::string dest_db_;
234+ int pages_;
235+ int sleep_;
236+ };
237+
131238class UserDefinedFunction {
132239 public:
133240 explicit UserDefinedFunction (Environment* env,
@@ -533,6 +640,28 @@ void DatabaseSync::Exec(const FunctionCallbackInfo<Value>& args) {
533640 CHECK_ERROR_OR_THROW (env->isolate (), db->connection_ , r, SQLITE_OK, void ());
534641}
535642
643+ void DatabaseSync::Backup (const FunctionCallbackInfo<Value>& args) {
644+ DatabaseSync* db;
645+ ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
646+ Environment* env = Environment::GetCurrent (args);
647+ Local<Promise::Resolver> resolver = Promise::Resolver::New (env->context ())
648+ .ToLocalChecked ()
649+ .As <Promise::Resolver>();
650+
651+ Utf8Value destFilename (env->isolate (), args[0 ].As <String>());
652+ Local<Object> options = args[1 ].As <Object>();
653+ Local<String> progress_string = FIXED_ONE_BYTE_STRING (env->isolate (), " progress" );
654+
655+ Local<Value> progressValue =
656+ options->Get (env->context (), progress_string).ToLocalChecked ();
657+ Local<Function> progressFunc = progressValue.As <Function>();
658+
659+ args.GetReturnValue ().Set (resolver->GetPromise ());
660+
661+ BackupJob* job = new BackupJob (env, db, resolver, " main" , *destFilename, " main" , 100 , 250 , progressFunc);
662+ job->ScheduleWork ();
663+ }
664+
536665void DatabaseSync::CustomFunction (const FunctionCallbackInfo<Value>& args) {
537666 DatabaseSync* db;
538667 ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
@@ -1718,6 +1847,7 @@ static void Initialize(Local<Object> target,
17181847 SetProtoMethod (isolate, db_tmpl, " close" , DatabaseSync::Close);
17191848 SetProtoMethod (isolate, db_tmpl, " prepare" , DatabaseSync::Prepare);
17201849 SetProtoMethod (isolate, db_tmpl, " exec" , DatabaseSync::Exec);
1850+ SetProtoMethod (isolate, db_tmpl, " backup" , DatabaseSync::Backup);
17211851 SetProtoMethod (isolate, db_tmpl, " function" , DatabaseSync::CustomFunction);
17221852 SetProtoMethod (
17231853 isolate, db_tmpl, " createSession" , DatabaseSync::CreateSession);
0 commit comments