Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions lib/sqlite.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
'use strict';
const {
SymbolDispose,
} = primordials;
const { emitExperimentalWarning } = require('internal/util');
const binding = internalBinding('sqlite');

emitExperimentalWarning('SQLite');

// TODO(cjihrig): Move this to C++ once Symbol.dispose reaches Stage 4.
binding.DatabaseSync.prototype[SymbolDispose] = function() {
try {
this.close();
} catch {
// Ignore errors.
}
};

module.exports = binding;
module.exports = internalBinding('sqlite');
20 changes: 20 additions & 0 deletions src/node_sqlite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,14 @@ void DatabaseSync::Close(const FunctionCallbackInfo<Value>& args) {
db->connection_ = nullptr;
}

void DatabaseSync::Dispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::TryCatch try_catch(args.GetIsolate());
Close(args);
if (try_catch.HasCaught()) {
CHECK(try_catch.CanContinue());
}
}

void DatabaseSync::Prepare(const FunctionCallbackInfo<Value>& args) {
DatabaseSync* db;
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
Expand Down Expand Up @@ -2577,6 +2585,7 @@ Local<FunctionTemplate> Session::GetConstructorTemplate(Environment* env) {
SetProtoMethod(
isolate, tmpl, "patchset", Session::Changeset<sqlite3session_patchset>);
SetProtoMethod(isolate, tmpl, "close", Session::Close);
SetProtoDispose(isolate, tmpl, Session::Dispose);
env->set_sqlite_session_constructor_template(tmpl);
}
return tmpl;
Expand Down Expand Up @@ -2621,6 +2630,14 @@ void Session::Close(const FunctionCallbackInfo<Value>& args) {
session->Delete();
}

void Session::Dispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::TryCatch try_catch(args.GetIsolate());
Close(args);
if (try_catch.HasCaught()) {
CHECK(try_catch.CanContinue());
}
}

void Session::Delete() {
if (!database_ || !database_->connection_ || session_ == nullptr) return;
sqlite3session_delete(session_);
Expand Down Expand Up @@ -2656,6 +2673,7 @@ static void Initialize(Local<Object> target,

SetProtoMethod(isolate, db_tmpl, "open", DatabaseSync::Open);
SetProtoMethod(isolate, db_tmpl, "close", DatabaseSync::Close);
SetProtoDispose(isolate, db_tmpl, DatabaseSync::Dispose);
SetProtoMethod(isolate, db_tmpl, "prepare", DatabaseSync::Prepare);
SetProtoMethod(isolate, db_tmpl, "exec", DatabaseSync::Exec);
SetProtoMethod(isolate, db_tmpl, "function", DatabaseSync::CustomFunction);
Expand Down Expand Up @@ -2686,6 +2704,8 @@ static void Initialize(Local<Object> target,
target,
"StatementSync",
StatementSync::GetConstructorTemplate(env));
SetConstructorFunction(
context, target, "Session", Session::GetConstructorTemplate(env));
Comment on lines +2707 to +2708
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: there should probably be an IllegalConstructor test for Session.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before this gets exposed, does there need to be a brief discussion regarding Session vs SessionSync?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already exposed in the sense that folks can be grabbing the constructor off the returned object already. I'm not sure bikeshedding on the name is worthwhile but could be convinced.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is otherwise ready to land so the naming discussion is the only thing that would hold it up. Is it worth blocking on?


target->Set(context, env->constants_string(), constants).Check();

Expand Down
2 changes: 2 additions & 0 deletions src/node_sqlite.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class DatabaseSync : public BaseObject {
static void IsTransactionGetter(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Dispose(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 Location(const v8::FunctionCallbackInfo<v8::Value>& args);
Expand Down Expand Up @@ -194,6 +195,7 @@ class Session : public BaseObject {
template <Sqlite3ChangesetGenFunc sqliteChangesetFunc>
static void Changeset(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Dispose(const v8::FunctionCallbackInfo<v8::Value>& args);
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);
static BaseObjectPtr<Session> Create(Environment* env,
Expand Down
26 changes: 26 additions & 0 deletions src/util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,32 @@ void SetMethodNoSideEffect(Isolate* isolate,
that->Set(name_string, t);
}

void SetProtoDispose(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> that,
v8::FunctionCallback callback) {
Local<v8::Signature> signature = v8::Signature::New(isolate, that);
Local<v8::FunctionTemplate> t =
NewFunctionTemplate(isolate,
callback,
signature,
v8::ConstructorBehavior::kThrow,
v8::SideEffectType::kHasSideEffect);
that->PrototypeTemplate()->Set(v8::Symbol::GetDispose(isolate), t);
}

void SetProtoAsyncDispose(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> that,
v8::FunctionCallback callback) {
Local<v8::Signature> signature = v8::Signature::New(isolate, that);
Local<v8::FunctionTemplate> t =
NewFunctionTemplate(isolate,
callback,
signature,
v8::ConstructorBehavior::kThrow,
v8::SideEffectType::kHasSideEffect);
that->PrototypeTemplate()->Set(v8::Symbol::GetAsyncDispose(isolate), t);
}

void SetProtoMethod(v8::Isolate* isolate,
Local<v8::FunctionTemplate> that,
const std::string_view name,
Expand Down
10 changes: 10 additions & 0 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,16 @@ void SetMethodNoSideEffect(v8::Isolate* isolate,
const std::string_view name,
v8::FunctionCallback callback);

// Set the Symbol.dispose method on the prototype of the class.
void SetProtoDispose(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> that,
v8::FunctionCallback callback);

// Set the Symbol.asyncDispose method on the prototype of the class.
void SetProtoAsyncDispose(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> that,
v8::FunctionCallback callback);

enum class SetConstructorFunctionFlag {
NONE,
SET_CLASS_NAME,
Expand Down
15 changes: 15 additions & 0 deletions test/parallel/test-sqlite-session.js
Original file line number Diff line number Diff line change
Expand Up @@ -539,3 +539,18 @@ test('session.close() - closing twice', (t) => {
message: 'session is not open'
});
});

test('session supports ERM', (t) => {
const database = new DatabaseSync(':memory:');
let afterDisposeSession;
{
using session = database.createSession();
afterDisposeSession = session;
const changeset = session.changeset();
t.assert.ok(changeset instanceof Uint8Array);
t.assert.strictEqual(changeset.length, 0);
}
t.assert.throws(() => afterDisposeSession.changeset(), {
message: /session is not open/,
});
});
Loading