Skip to content

Commit 9b80c2d

Browse files
committed
wip
1 parent 76b80b1 commit 9b80c2d

File tree

3 files changed

+170
-0
lines changed

3 files changed

+170
-0
lines changed

src/node_sqlite.cc

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "async_wrap-inl.h"
12
#include "node_sqlite.h"
23
#include <path.h>
34
#include "base_object-inl.h"
@@ -9,13 +10,20 @@
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+
1522
namespace node {
1623
namespace sqlite {
1724

1825
using v8::Array;
26+
using v8::HandleScope;
1927
using v8::ArrayBuffer;
2028
using v8::BigInt;
2129
using v8::Boolean;
@@ -40,6 +48,7 @@ using v8::NewStringType;
4048
using v8::Null;
4149
using v8::Number;
4250
using v8::Object;
51+
using v8::Promise;
4352
using v8::SideEffectType;
4453
using v8::String;
4554
using 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+
131238
class 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+
536665
void 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);

src/node_sqlite.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class DatabaseSync : public BaseObject {
5757
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
5858
static void Prepare(const v8::FunctionCallbackInfo<v8::Value>& args);
5959
static void Exec(const v8::FunctionCallbackInfo<v8::Value>& args);
60+
static void Backup(const v8::FunctionCallbackInfo<v8::Value>& args);
6061
static void CustomFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
6162
static void CreateSession(const v8::FunctionCallbackInfo<v8::Value>& args);
6263
static void ApplyChangeset(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -81,6 +82,7 @@ class DatabaseSync : public BaseObject {
8182
bool enable_load_extension_;
8283
sqlite3* connection_;
8384

85+
std::set<sqlite3_backup*> backups_;
8486
std::set<sqlite3_session*> sessions_;
8587
std::unordered_set<StatementSync*> statements_;
8688

test/parallel/test-sqlite-backup.mjs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import common from '../common/index.js';
2+
import { DatabaseSync } from 'node:sqlite';
3+
import assert from 'assert';
4+
import { test } from 'node:test';
5+
6+
test('database backup', async () => {
7+
const database = new DatabaseSync(':memory:');
8+
9+
database.exec(`
10+
CREATE TABLE data(
11+
key INTEGER PRIMARY KEY,
12+
value TEXT
13+
) STRICT
14+
`);
15+
16+
const insert = database.prepare('INSERT INTO data (key, value) VALUES (?, ?)');
17+
18+
insert.run(1, 'hello');
19+
insert.run(2, 'world');
20+
21+
console.log(database.prepare('SELECT * FROM data').all());
22+
23+
const p = await database.backup('backup.db', {
24+
sourceDb: 'main',
25+
targetDb: 'main',
26+
progress: (progress) => {
27+
console.log(progress);
28+
}
29+
});
30+
31+
const backup = new DatabaseSync('backup.db');
32+
const rows = backup.prepare('SELECT * FROM data').all();
33+
34+
assert.deepStrictEqual(rows, [
35+
{ __proto__: null, key: 1, value: 'hello'},
36+
{ __proto__: null, key: 2, value: 'world' },
37+
]);
38+
});

0 commit comments

Comments
 (0)