@@ -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+
295312inline 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+
37473901void 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
0 commit comments