1
+ #include " async_wrap-inl.h"
1
2
#include " node_sqlite.h"
2
3
#include < path.h>
3
4
#include " base_object-inl.h"
9
10
#include " node_mem-inl.h"
10
11
#include " sqlite3.h"
11
12
#include " util-inl.h"
13
+ #include " threadpoolwork-inl.h"
12
14
13
15
#include < cinttypes>
14
16
17
+ #include < chrono>
18
+ #include < thread>
19
+ #include < format>
20
+ #include < iostream>
21
+
15
22
namespace node {
16
23
namespace sqlite {
17
24
18
25
using v8::Array;
26
+ using v8::HandleScope;
19
27
using v8::ArrayBuffer;
20
28
using v8::BigInt;
21
29
using v8::Boolean;
@@ -40,6 +48,7 @@ using v8::NewStringType;
40
48
using v8::Null;
41
49
using v8::Number;
42
50
using v8::Object;
51
+ using v8::Promise;
43
52
using v8::SideEffectType;
44
53
using v8::String;
45
54
using v8::TryCatch;
@@ -128,6 +137,104 @@ inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, int errcode) {
128
137
isolate->ThrowException (error);
129
138
}
130
139
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
+
131
238
class UserDefinedFunction {
132
239
public:
133
240
explicit UserDefinedFunction (Environment* env,
@@ -533,6 +640,28 @@ void DatabaseSync::Exec(const FunctionCallbackInfo<Value>& args) {
533
640
CHECK_ERROR_OR_THROW (env->isolate (), db->connection_ , r, SQLITE_OK, void ());
534
641
}
535
642
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
+
536
665
void DatabaseSync::CustomFunction (const FunctionCallbackInfo<Value>& args) {
537
666
DatabaseSync* db;
538
667
ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
@@ -1718,6 +1847,7 @@ static void Initialize(Local<Object> target,
1718
1847
SetProtoMethod (isolate, db_tmpl, " close" , DatabaseSync::Close);
1719
1848
SetProtoMethod (isolate, db_tmpl, " prepare" , DatabaseSync::Prepare);
1720
1849
SetProtoMethod (isolate, db_tmpl, " exec" , DatabaseSync::Exec);
1850
+ SetProtoMethod (isolate, db_tmpl, " backup" , DatabaseSync::Backup);
1721
1851
SetProtoMethod (isolate, db_tmpl, " function" , DatabaseSync::CustomFunction);
1722
1852
SetProtoMethod (
1723
1853
isolate, db_tmpl, " createSession" , DatabaseSync::CreateSession);
0 commit comments