Skip to content

Commit

Permalink
sqlite: support db.loadExtension
Browse files Browse the repository at this point in the history
  • Loading branch information
himself65 committed Aug 8, 2024
1 parent 2bcf999 commit 8b1dba0
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 3 deletions.
52 changes: 50 additions & 2 deletions src/node_sqlite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,14 @@ inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, sqlite3* db) {
DatabaseSync::DatabaseSync(Environment* env,
Local<Object> object,
Local<String> location,
bool open)
bool open,
bool allow_load_extension)
: BaseObject(env, object) {
MakeWeak();
node::Utf8Value utf8_location(env->isolate(), location);
location_ = utf8_location.ToString();
connection_ = nullptr;
allow_load_extension_ = allow_load_extension;

if (open) {
Open();
Expand All @@ -109,6 +111,12 @@ bool DatabaseSync::Open() {
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
int r = sqlite3_open_v2(location_.c_str(), &connection_, flags, nullptr);
CHECK_ERROR_OR_THROW(env()->isolate(), connection_, r, SQLITE_OK, false);
if (allow_load_extension_) {
int load_extension_ret = sqlite3_db_config(
connection_, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL);
CHECK_ERROR_OR_THROW(
env()->isolate(), connection_, load_extension_ret, SQLITE_OK, false);
}
return true;
}

Expand All @@ -127,6 +135,7 @@ void DatabaseSync::New(const FunctionCallbackInfo<Value>& args) {
}

bool open = true;
bool allow_load_extension = false;

if (args.Length() > 1) {
if (!args[1]->IsObject()) {
Expand All @@ -137,10 +146,17 @@ void DatabaseSync::New(const FunctionCallbackInfo<Value>& args) {

Local<Object> options = args[1].As<Object>();
Local<String> open_string = FIXED_ONE_BYTE_STRING(env->isolate(), "open");
Local<String> allow_load_extension_string =
FIXED_ONE_BYTE_STRING(env->isolate(), "allowLoadExtension");
Local<Value> open_v;
Local<Value> allow_load_extension_v;
if (!options->Get(env->context(), open_string).ToLocal(&open_v)) {
return;
}
if (!options->Get(env->context(), allow_load_extension_string)
.ToLocal(&allow_load_extension_v)) {
return;
}
if (!open_v->IsUndefined()) {
if (!open_v->IsBoolean()) {
node::THROW_ERR_INVALID_ARG_TYPE(
Expand All @@ -149,9 +165,19 @@ void DatabaseSync::New(const FunctionCallbackInfo<Value>& args) {
}
open = open_v.As<Boolean>()->Value();
}
if (!allow_load_extension_v->IsUndefined()) {
if (!allow_load_extension_v->IsBoolean()) {
node::THROW_ERR_INVALID_ARG_TYPE(
env->isolate(),
"The \"options.allowLoadExtension\" argument must be a boolean.");
return;
}
allow_load_extension = allow_load_extension_v.As<Boolean>()->Value();
}
}

new DatabaseSync(env, args.This(), args[0].As<String>(), open);
new DatabaseSync(
env, args.This(), args[0].As<String>(), open, allow_load_extension);
}

void DatabaseSync::Open(const FunctionCallbackInfo<Value>& args) {
Expand Down Expand Up @@ -211,6 +237,26 @@ void DatabaseSync::Exec(const FunctionCallbackInfo<Value>& args) {
CHECK_ERROR_OR_THROW(env->isolate(), db->connection_, r, SQLITE_OK, void());
}

void DatabaseSync::LoadExtension(const FunctionCallbackInfo<Value>& args) {
DatabaseSync* db;
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
Environment* env = Environment::GetCurrent(args);
THROW_AND_RETURN_ON_BAD_STATE(
env, db->connection_ == nullptr, "database is not open");
THROW_AND_RETURN_ON_BAD_STATE(
env, !db->allow_load_extension_, "load extension is not allowed");

if (!args[0]->IsString()) {
node::THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
"The \"path\" argument must be a string.");
return;
}

auto path = node::Utf8Value(env->isolate(), args[0].As<String>());
int r = sqlite3_load_extension(db->connection_, *path, nullptr, nullptr);
CHECK_ERROR_OR_THROW(env->isolate(), db->connection_, r, SQLITE_OK, void());
}

StatementSync::StatementSync(Environment* env,
Local<Object> object,
sqlite3* db,
Expand Down Expand Up @@ -668,6 +714,8 @@ 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, "loadExtension", DatabaseSync::LoadExtension);
SetConstructorFunction(context, target, "DatabaseSync", db_tmpl);
SetConstructorFunction(context,
target,
Expand Down
5 changes: 4 additions & 1 deletion src/node_sqlite.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ class DatabaseSync : public BaseObject {
DatabaseSync(Environment* env,
v8::Local<v8::Object> object,
v8::Local<v8::String> location,
bool open);
bool open,
bool allow_load_extension);
void MemoryInfo(MemoryTracker* tracker) const override;
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Open(const v8::FunctionCallbackInfo<v8::Value>& args);
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 LoadExtension(const v8::FunctionCallbackInfo<v8::Value>& args);

SET_MEMORY_INFO_NAME(DatabaseSync)
SET_SELF_SIZE(DatabaseSync)
Expand All @@ -34,6 +36,7 @@ class DatabaseSync : public BaseObject {

~DatabaseSync() override;
std::string location_;
bool allow_load_extension_;
sqlite3* connection_;
};

Expand Down
9 changes: 9 additions & 0 deletions test/parallel/test-sqlite.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ suite('DatabaseSync() constructor', () => {
message: /The "options\.open" argument must be a boolean/,
});
});

test('throws if options.allowLoadExtension is provided but is not a boolean', (t) => {
t.assert.throws(() => {
new DatabaseSync('foo', { allowLoadExtension: 5 });
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: /The "options\.allowLoadExtension" argument must be a boolean/,
});
});
});

suite('DatabaseSync.prototype.open()', () => {
Expand Down

0 comments on commit 8b1dba0

Please sign in to comment.