Skip to content

Commit 78f4e12

Browse files
sqlite: add Database class scaffolding
Add a Database class skeleton which is currently only capable of opening and closing a sqlite db connection. Also notably missing is any support for asynchronous operations apart from changes to the close and dispose method signatures. However, the skeleton is capable enough to pass the most basic lifetime tests and therefore useful as a diff target for the asynchronous operations to be added. Re-enable the async database test suites and skip the tests on a case-by -case basis.
1 parent 654ebd5 commit 78f4e12

3 files changed

Lines changed: 314 additions & 117 deletions

File tree

src/node_sqlite.cc

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,23 @@ inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, int errcode) {
292292
}
293293
}
294294

295+
inline void RejectWithSQLiteError(Environment* env,
296+
Local<Promise::Resolver> resolver,
297+
sqlite3* db) {
298+
resolver
299+
->Reject(env->context(),
300+
CreateSQLiteError(env->isolate(), db).ToLocalChecked())
301+
.Check();
302+
}
303+
inline void RejectWithSQLiteError(Environment* env,
304+
Local<Promise::Resolver> resolver,
305+
int errcode) {
306+
resolver
307+
->Reject(env->context(),
308+
CreateSQLiteError(env->isolate(), errcode).ToLocalChecked())
309+
.Check();
310+
}
311+
295312
inline MaybeLocal<Value> NullableSQLiteStringToValue(Isolate* isolate,
296313
const char* str) {
297314
if (str == nullptr) {
@@ -3744,6 +3761,143 @@ void Session::Delete() {
37443761
session_ = nullptr;
37453762
}
37463763

3764+
Database::Database(Environment* env,
3765+
v8::Local<v8::Object> object,
3766+
DatabaseOpenConfiguration&& open_config,
3767+
bool open,
3768+
bool allow_load_extension)
3769+
: DatabaseCommon(
3770+
env, object, std::move(open_config), allow_load_extension) {
3771+
MakeWeak();
3772+
3773+
if (open) {
3774+
Open();
3775+
}
3776+
}
3777+
3778+
Database::~Database() {
3779+
if (IsOpen()) {
3780+
(void)sqlite3_close_v2(connection_);
3781+
connection_ = nullptr;
3782+
}
3783+
}
3784+
3785+
void Database::MemoryInfo(MemoryTracker* tracker) const {
3786+
// TODO(BurningEnlightenment): more accurately track the size of all fields
3787+
tracker->TrackFieldWithSize(
3788+
"open_config", sizeof(open_config_), "DatabaseOpenConfiguration");
3789+
}
3790+
3791+
namespace {
3792+
v8::Local<v8::FunctionTemplate> CreateDatabaseConstructorTemplate(
3793+
Environment* env) {
3794+
Isolate* isolate = env->isolate();
3795+
3796+
Local<FunctionTemplate> tmpl = NewFunctionTemplate(isolate, Database::New);
3797+
tmpl->InstanceTemplate()->SetInternalFieldCount(
3798+
Database::kInternalFieldCount);
3799+
3800+
AddDatabaseCommonMethodsToTemplate(isolate, tmpl);
3801+
3802+
SetProtoMethod(isolate, tmpl, "close", Database::Close);
3803+
SetProtoAsyncDispose(isolate, tmpl, Database::AsyncDispose);
3804+
3805+
Local<String> sqlite_type_key = FIXED_ONE_BYTE_STRING(isolate, "sqlite-type");
3806+
Local<v8::Symbol> sqlite_type_symbol =
3807+
v8::Symbol::For(isolate, sqlite_type_key);
3808+
Local<String> database_sync_string =
3809+
FIXED_ONE_BYTE_STRING(isolate, "node:sqlite-async");
3810+
tmpl->InstanceTemplate()->Set(sqlite_type_symbol, database_sync_string);
3811+
3812+
return tmpl;
3813+
}
3814+
} // namespace
3815+
3816+
void Database::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
3817+
Environment* env = Environment::GetCurrent(args);
3818+
if (!args.IsConstructCall()) {
3819+
THROW_ERR_CONSTRUCT_CALL_REQUIRED(env);
3820+
return;
3821+
}
3822+
3823+
std::optional<std::string> location =
3824+
ValidateDatabasePath(env, args[0], "path");
3825+
if (!location.has_value()) {
3826+
return;
3827+
}
3828+
3829+
DatabaseOpenConfiguration open_config(std::move(location.value()));
3830+
bool open = true;
3831+
bool allow_load_extension = false;
3832+
if (args.Length() > 1) {
3833+
if (!args[1]->IsObject()) {
3834+
THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
3835+
"The \"options\" argument must be an object.");
3836+
return;
3837+
}
3838+
3839+
Local<Object> options = args[1].As<Object>();
3840+
if (!ParseCommonDatabaseOptions(
3841+
env, options, open_config, open, allow_load_extension)) {
3842+
return;
3843+
}
3844+
}
3845+
3846+
new Database(
3847+
env, args.This(), std::move(open_config), open, allow_load_extension);
3848+
}
3849+
3850+
void Database::Close(const v8::FunctionCallbackInfo<v8::Value>& args) {
3851+
Database* db;
3852+
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
3853+
3854+
Environment* env = Environment::GetCurrent(args);
3855+
Local<Context> context = env->context();
3856+
if (!db->IsOpen()) {
3857+
auto resolver = Promise::Resolver::New(context).ToLocalChecked();
3858+
resolver
3859+
->Reject(context,
3860+
ERR_INVALID_STATE(env->isolate(), "database is not open"))
3861+
.Check();
3862+
args.GetReturnValue().Set(resolver->GetPromise());
3863+
return;
3864+
}
3865+
3866+
AsyncDispose(args);
3867+
}
3868+
3869+
void Database::AsyncDispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
3870+
Database* db;
3871+
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
3872+
3873+
// Disposing is idempotent, so if a close is already in progress or has been
3874+
// completed, return the existing promise.
3875+
Local<Value> close_resolver =
3876+
db->object()->GetInternalField(Database::kClosingPromiseSlot).As<Value>();
3877+
if (close_resolver->IsPromise()) {
3878+
args.GetReturnValue().Set(close_resolver.As<Promise>());
3879+
return;
3880+
}
3881+
3882+
Environment* env = Environment::GetCurrent(args);
3883+
Local<Context> context = env->context();
3884+
auto resolver = Promise::Resolver::New(context).ToLocalChecked();
3885+
3886+
if (!db->IsOpen()) {
3887+
resolver->Resolve(context, Undefined(env->isolate())).Check();
3888+
} else if (int errcode = sqlite3_close_v2(db->connection_);
3889+
errcode != SQLITE_OK) {
3890+
// Note: passing `db->connection_` to sqlite3_extended_errcode after calling
3891+
// `sqlite3_close_v2` is not safe.
3892+
RejectWithSQLiteError(env, resolver, errcode);
3893+
} else {
3894+
resolver->Resolve(context, Undefined(env->isolate())).Check();
3895+
}
3896+
db->connection_ = nullptr;
3897+
db->object()->SetInternalField(Database::kClosingPromiseSlot, resolver);
3898+
args.GetReturnValue().Set(resolver->GetPromise());
3899+
}
3900+
37473901
void DefineConstants(Local<Object> target) {
37483902
NODE_DEFINE_CONSTANT(target, SQLITE_CHANGESET_OMIT);
37493903
NODE_DEFINE_CONSTANT(target, SQLITE_CHANGESET_REPLACE);
@@ -3818,6 +3972,11 @@ static void Initialize(Local<Object> target,
38183972
StatementSync::GetConstructorTemplate(env));
38193973
SetConstructorFunction(
38203974
context, target, "Session", Session::GetConstructorTemplate(env));
3975+
SetConstructorFunction(
3976+
context, target, "Database", CreateDatabaseConstructorTemplate(env));
3977+
target
3978+
->Set(context, FIXED_ONE_BYTE_STRING(isolate, "Statement"), Null(isolate))
3979+
.Check();
38213980

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

src/node_sqlite.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,32 @@ class DatabaseSyncLimits : public BaseObject {
448448
BaseObjectWeakPtr<DatabaseSync> database_;
449449
};
450450

451+
class Database : public DatabaseCommon {
452+
public:
453+
enum InternalFields {
454+
kClosingPromiseSlot = DatabaseCommon::kInternalFieldCount,
455+
kInternalFieldCount
456+
};
457+
458+
Database(Environment* env,
459+
v8::Local<v8::Object> object,
460+
DatabaseOpenConfiguration&& open_config,
461+
bool open,
462+
bool allow_load_extension);
463+
void MemoryInfo(MemoryTracker* tracker) const override;
464+
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
465+
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
466+
static void AsyncDispose(const v8::FunctionCallbackInfo<v8::Value>& args);
467+
468+
SET_MEMORY_INFO_NAME(Database)
469+
SET_SELF_SIZE(Database)
470+
471+
private:
472+
~Database() override;
473+
474+
friend class StatementExecutionHelper;
475+
};
476+
451477
} // namespace sqlite
452478
} // namespace node
453479

0 commit comments

Comments
 (0)