Skip to content

Commit eb6611e

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 7af07bc commit eb6611e

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
@@ -274,6 +274,23 @@ inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, int errcode) {
274274
}
275275
}
276276

277+
inline void RejectWithSQLiteError(Environment* env,
278+
Local<Promise::Resolver> resolver,
279+
sqlite3* db) {
280+
resolver
281+
->Reject(env->context(),
282+
CreateSQLiteError(env->isolate(), db).ToLocalChecked())
283+
.Check();
284+
}
285+
inline void RejectWithSQLiteError(Environment* env,
286+
Local<Promise::Resolver> resolver,
287+
int errcode) {
288+
resolver
289+
->Reject(env->context(),
290+
CreateSQLiteError(env->isolate(), errcode).ToLocalChecked())
291+
.Check();
292+
}
293+
277294
inline MaybeLocal<Value> NullableSQLiteStringToValue(Isolate* isolate,
278295
const char* str) {
279296
if (str == nullptr) {
@@ -3474,6 +3491,143 @@ void Session::Delete() {
34743491
session_ = nullptr;
34753492
}
34763493

3494+
Database::Database(Environment* env,
3495+
v8::Local<v8::Object> object,
3496+
DatabaseOpenConfiguration&& open_config,
3497+
bool open,
3498+
bool allow_load_extension)
3499+
: DatabaseCommon(
3500+
env, object, std::move(open_config), allow_load_extension) {
3501+
MakeWeak();
3502+
3503+
if (open) {
3504+
Open();
3505+
}
3506+
}
3507+
3508+
Database::~Database() {
3509+
if (IsOpen()) {
3510+
(void)sqlite3_close_v2(connection_);
3511+
connection_ = nullptr;
3512+
}
3513+
}
3514+
3515+
void Database::MemoryInfo(MemoryTracker* tracker) const {
3516+
// TODO(BurningEnlightenment): more accurately track the size of all fields
3517+
tracker->TrackFieldWithSize(
3518+
"open_config", sizeof(open_config_), "DatabaseOpenConfiguration");
3519+
}
3520+
3521+
namespace {
3522+
v8::Local<v8::FunctionTemplate> CreateDatabaseConstructorTemplate(
3523+
Environment* env) {
3524+
Isolate* isolate = env->isolate();
3525+
3526+
Local<FunctionTemplate> tmpl = NewFunctionTemplate(isolate, Database::New);
3527+
tmpl->InstanceTemplate()->SetInternalFieldCount(
3528+
Database::kInternalFieldCount);
3529+
3530+
AddDatabaseCommonMethodsToTemplate(isolate, tmpl);
3531+
3532+
SetProtoMethod(isolate, tmpl, "close", Database::Close);
3533+
SetProtoAsyncDispose(isolate, tmpl, Database::AsyncDispose);
3534+
3535+
Local<String> sqlite_type_key = FIXED_ONE_BYTE_STRING(isolate, "sqlite-type");
3536+
Local<v8::Symbol> sqlite_type_symbol =
3537+
v8::Symbol::For(isolate, sqlite_type_key);
3538+
Local<String> database_sync_string =
3539+
FIXED_ONE_BYTE_STRING(isolate, "node:sqlite-async");
3540+
tmpl->InstanceTemplate()->Set(sqlite_type_symbol, database_sync_string);
3541+
3542+
return tmpl;
3543+
}
3544+
} // namespace
3545+
3546+
void Database::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
3547+
Environment* env = Environment::GetCurrent(args);
3548+
if (!args.IsConstructCall()) {
3549+
THROW_ERR_CONSTRUCT_CALL_REQUIRED(env);
3550+
return;
3551+
}
3552+
3553+
std::optional<std::string> location =
3554+
ValidateDatabasePath(env, args[0], "path");
3555+
if (!location.has_value()) {
3556+
return;
3557+
}
3558+
3559+
DatabaseOpenConfiguration open_config(std::move(location.value()));
3560+
bool open = true;
3561+
bool allow_load_extension = false;
3562+
if (args.Length() > 1) {
3563+
if (!args[1]->IsObject()) {
3564+
THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
3565+
"The \"options\" argument must be an object.");
3566+
return;
3567+
}
3568+
3569+
Local<Object> options = args[1].As<Object>();
3570+
if (!ParseCommonDatabaseOptions(
3571+
env, options, open_config, open, allow_load_extension)) {
3572+
return;
3573+
}
3574+
}
3575+
3576+
new Database(
3577+
env, args.This(), std::move(open_config), open, allow_load_extension);
3578+
}
3579+
3580+
void Database::Close(const v8::FunctionCallbackInfo<v8::Value>& args) {
3581+
Database* db;
3582+
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
3583+
3584+
Environment* env = Environment::GetCurrent(args);
3585+
Local<Context> context = env->context();
3586+
if (!db->IsOpen()) {
3587+
auto resolver = Promise::Resolver::New(context).ToLocalChecked();
3588+
resolver
3589+
->Reject(context,
3590+
ERR_INVALID_STATE(env->isolate(), "database is not open"))
3591+
.Check();
3592+
args.GetReturnValue().Set(resolver->GetPromise());
3593+
return;
3594+
}
3595+
3596+
AsyncDispose(args);
3597+
}
3598+
3599+
void Database::AsyncDispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
3600+
Database* db;
3601+
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
3602+
3603+
// Disposing is idempotent, so if a close is already in progress or has been
3604+
// completed, return the existing promise.
3605+
Local<Value> close_resolver =
3606+
db->object()->GetInternalField(Database::kClosingPromiseSlot).As<Value>();
3607+
if (close_resolver->IsPromise()) {
3608+
args.GetReturnValue().Set(close_resolver.As<Promise>());
3609+
return;
3610+
}
3611+
3612+
Environment* env = Environment::GetCurrent(args);
3613+
Local<Context> context = env->context();
3614+
auto resolver = Promise::Resolver::New(context).ToLocalChecked();
3615+
3616+
if (!db->IsOpen()) {
3617+
resolver->Resolve(context, Undefined(env->isolate())).Check();
3618+
} else if (int errcode = sqlite3_close_v2(db->connection_);
3619+
errcode != SQLITE_OK) {
3620+
// Note: passing `db->connection_` to sqlite3_extended_errcode after calling
3621+
// `sqlite3_close_v2` is not safe.
3622+
RejectWithSQLiteError(env, resolver, errcode);
3623+
} else {
3624+
resolver->Resolve(context, Undefined(env->isolate())).Check();
3625+
}
3626+
db->connection_ = nullptr;
3627+
db->object()->SetInternalField(Database::kClosingPromiseSlot, resolver);
3628+
args.GetReturnValue().Set(resolver->GetPromise());
3629+
}
3630+
34773631
void DefineConstants(Local<Object> target) {
34783632
NODE_DEFINE_CONSTANT(target, SQLITE_CHANGESET_OMIT);
34793633
NODE_DEFINE_CONSTANT(target, SQLITE_CHANGESET_REPLACE);
@@ -3548,6 +3702,11 @@ static void Initialize(Local<Object> target,
35483702
StatementSync::GetConstructorTemplate(env));
35493703
SetConstructorFunction(
35503704
context, target, "Session", Session::GetConstructorTemplate(env));
3705+
SetConstructorFunction(
3706+
context, target, "Database", CreateDatabaseConstructorTemplate(env));
3707+
target
3708+
->Set(context, FIXED_ONE_BYTE_STRING(isolate, "Statement"), Null(isolate))
3709+
.Check();
35513710

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

src/node_sqlite.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,32 @@ class UserDefinedFunction {
368368
bool use_bigint_args_;
369369
};
370370

371+
class Database : public DatabaseCommon {
372+
public:
373+
enum InternalFields {
374+
kClosingPromiseSlot = DatabaseCommon::kInternalFieldCount,
375+
kInternalFieldCount
376+
};
377+
378+
Database(Environment* env,
379+
v8::Local<v8::Object> object,
380+
DatabaseOpenConfiguration&& open_config,
381+
bool open,
382+
bool allow_load_extension);
383+
void MemoryInfo(MemoryTracker* tracker) const override;
384+
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
385+
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
386+
static void AsyncDispose(const v8::FunctionCallbackInfo<v8::Value>& args);
387+
388+
SET_MEMORY_INFO_NAME(Database)
389+
SET_SELF_SIZE(Database)
390+
391+
private:
392+
~Database() override;
393+
394+
friend class StatementExecutionHelper;
395+
};
396+
371397
} // namespace sqlite
372398
} // namespace node
373399

0 commit comments

Comments
 (0)