@@ -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+
277294inline 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+
34773631void 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
0 commit comments