Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
geeksilva97 committed Jan 10, 2025
1 parent 76b80b1 commit 9b80c2d
Showing 3 changed files with 170 additions and 0 deletions.
130 changes: 130 additions & 0 deletions src/node_sqlite.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "async_wrap-inl.h"
#include "node_sqlite.h"
#include <path.h>
#include "base_object-inl.h"
@@ -9,13 +10,20 @@
#include "node_mem-inl.h"
#include "sqlite3.h"
#include "util-inl.h"
#include "threadpoolwork-inl.h"

#include <cinttypes>

#include <chrono>
#include <thread>
#include <format>
#include <iostream>

namespace node {
namespace sqlite {

using v8::Array;
using v8::HandleScope;
using v8::ArrayBuffer;
using v8::BigInt;
using v8::Boolean;
@@ -40,6 +48,7 @@ using v8::NewStringType;
using v8::Null;
using v8::Number;
using v8::Object;
using v8::Promise;
using v8::SideEffectType;
using v8::String;
using v8::TryCatch;
@@ -128,6 +137,104 @@ inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, int errcode) {
isolate->ThrowException(error);
}

class BackupJob: public ThreadPoolWork {
public:
explicit BackupJob(Environment* env,
DatabaseSync* source,
Local<Promise::Resolver> resolver,
const char* source_db,
const char* destination_name,
const char* dest_db,
int pages,
int sleep,
Local<Function> progressFunc) : ThreadPoolWork(env, "node_sqlite3.BackupJob"),
env_(env),
/* progressFunc_(progressFunc), */
source_(source),
source_db_(source_db),
destination_name_(destination_name),
dest_db_(dest_db),
pages_(pages),
sleep_(sleep) {
resolver_.Reset(env->isolate(), resolver);
progressFunc_.Reset(env->isolate(), progressFunc);
}

void DoThreadPoolWork() override {
HandleScope handle_scope(env()->isolate());
sqlite3 *pDest;
sqlite3_backup *pBackup;

int rc = sqlite3_open(destination_name_.c_str(), &pDest);

if (rc != SQLITE_OK) {
sqlite3_close(pDest);
backup_status_ = -1;

return;
}

pBackup = sqlite3_backup_init(pDest, "main", source_->Connection(), source_db_.c_str());

if (pBackup == nullptr) {
sqlite3_close(pDest);
backup_status_ = -1;

return;
}

Local<Function> fn = Local<Function>::New(env()->isolate(), progressFunc_);

do {
rc = sqlite3_backup_step(pBackup, pages_);
/* fn->Call(env()->context(), Undefined(env()->isolate()), 0, nullptr); */


if (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {
sqlite3_sleep(sleep_);
}
} while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);

sqlite3_backup_finish(pBackup);
rc = sqlite3_errcode(pDest);

if (rc == SQLITE_OK) {
backup_status_ = 0;
} else {
backup_status_ = -1;
}

sqlite3_close(pDest);
}

void AfterThreadPoolWork(int status) override {
HandleScope handle_scope(env()->isolate());

if (!resolver_.IsEmpty()) {
Local<Promise::Resolver> resolver = Local<Promise::Resolver>::New(env()->isolate(), resolver_);
Local<String> message = String::NewFromUtf8(env()->isolate(), "Backup completed", NewStringType::kNormal).ToLocalChecked();

resolver->Resolve(env()->context(), message).ToChecked();
}
}

private:
// https://github.com/nodejs/node/blob/649da3b8377e030ea7b9a1bc0308451e26e28740/src/crypto/crypto_keygen.h#L126
int backup_status_;

Environment* env() const { return env_; }

Environment* env_;
DatabaseSync* source_;
Global<Promise::Resolver> resolver_;
Global<Function> progressFunc_;
std::string source_db_;
std::string destination_name_;
std::string dest_db_;
int pages_;
int sleep_;
};

class UserDefinedFunction {
public:
explicit UserDefinedFunction(Environment* env,
@@ -533,6 +640,28 @@ void DatabaseSync::Exec(const FunctionCallbackInfo<Value>& args) {
CHECK_ERROR_OR_THROW(env->isolate(), db->connection_, r, SQLITE_OK, void());
}

void DatabaseSync::Backup(const FunctionCallbackInfo<Value>& args) {
DatabaseSync* db;
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
Environment* env = Environment::GetCurrent(args);
Local<Promise::Resolver> resolver = Promise::Resolver::New(env->context())
.ToLocalChecked()
.As<Promise::Resolver>();

Utf8Value destFilename(env->isolate(), args[0].As<String>());
Local<Object> options = args[1].As<Object>();
Local<String> progress_string = FIXED_ONE_BYTE_STRING(env->isolate(), "progress");

Local<Value> progressValue =
options->Get(env->context(), progress_string).ToLocalChecked();
Local<Function> progressFunc = progressValue.As<Function>();

args.GetReturnValue().Set(resolver->GetPromise());

BackupJob* job = new BackupJob(env, db, resolver, "main", *destFilename, "main", 100, 250, progressFunc);
job->ScheduleWork();
}

void DatabaseSync::CustomFunction(const FunctionCallbackInfo<Value>& args) {
DatabaseSync* db;
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
@@ -1718,6 +1847,7 @@ static void Initialize(Local<Object> target,
SetProtoMethod(isolate, db_tmpl, "close", DatabaseSync::Close);
SetProtoMethod(isolate, db_tmpl, "prepare", DatabaseSync::Prepare);
SetProtoMethod(isolate, db_tmpl, "exec", DatabaseSync::Exec);
SetProtoMethod(isolate, db_tmpl, "backup", DatabaseSync::Backup);
SetProtoMethod(isolate, db_tmpl, "function", DatabaseSync::CustomFunction);
SetProtoMethod(
isolate, db_tmpl, "createSession", DatabaseSync::CreateSession);
2 changes: 2 additions & 0 deletions src/node_sqlite.h
Original file line number Diff line number Diff line change
@@ -57,6 +57,7 @@ class DatabaseSync : public BaseObject {
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Prepare(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Exec(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Backup(const v8::FunctionCallbackInfo<v8::Value>& args);
static void CustomFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
static void CreateSession(const v8::FunctionCallbackInfo<v8::Value>& args);
static void ApplyChangeset(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -81,6 +82,7 @@ class DatabaseSync : public BaseObject {
bool enable_load_extension_;
sqlite3* connection_;

std::set<sqlite3_backup*> backups_;
std::set<sqlite3_session*> sessions_;
std::unordered_set<StatementSync*> statements_;

38 changes: 38 additions & 0 deletions test/parallel/test-sqlite-backup.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import common from '../common/index.js';
import { DatabaseSync } from 'node:sqlite';
import assert from 'assert';
import { test } from 'node:test';

test('database backup', async () => {
const database = new DatabaseSync(':memory:');

database.exec(`
CREATE TABLE data(
key INTEGER PRIMARY KEY,
value TEXT
) STRICT
`);

const insert = database.prepare('INSERT INTO data (key, value) VALUES (?, ?)');

insert.run(1, 'hello');
insert.run(2, 'world');

console.log(database.prepare('SELECT * FROM data').all());

const p = await database.backup('backup.db', {
sourceDb: 'main',
targetDb: 'main',
progress: (progress) => {
console.log(progress);
}
});

const backup = new DatabaseSync('backup.db');
const rows = backup.prepare('SELECT * FROM data').all();

assert.deepStrictEqual(rows, [
{ __proto__: null, key: 1, value: 'hello'},
{ __proto__: null, key: 2, value: 'world' },
]);
});

0 comments on commit 9b80c2d

Please sign in to comment.