@@ -4379,8 +4379,10 @@ class alignas(64) OperationResult {
43794379
43804380 public:
43814381 static OperationResult RejectErrorCode (OperationBase* origin,
4382- int error_code) {
4383- return OperationResult{origin, Rejected::ErrorCode (error_code)};
4382+ int error_code,
4383+ const char * error_message = nullptr ) {
4384+ return OperationResult{origin,
4385+ Rejected::ErrorCode (error_code, error_message)};
43844386 }
43854387 static OperationResult RejectLastError (OperationBase* origin,
43864388 sqlite3* connection) {
@@ -4668,6 +4670,28 @@ class UpdateDbConfigOperation : private OperationBase {
46684670 int value_;
46694671};
46704672
4673+ class LoadExtensionOperation : private OperationBase {
4674+ public:
4675+ LoadExtensionOperation (Global<Promise::Resolver>&& resolver,
4676+ std::pmr::string&& extension_path)
4677+ : OperationBase(std::move(resolver)),
4678+ extension_path_ (std::move(extension_path)) {}
4679+
4680+ OperationResult operator ()(sqlite3* connection) {
4681+ char * error_message = nullptr ;
4682+ auto free_error_message =
4683+ OnScopeLeave ([&] { sqlite3_free (error_message); });
4684+ int error_code = sqlite3_load_extension (
4685+ connection, extension_path_.c_str (), nullptr , &error_message);
4686+ return error_code == SQLITE_OK ? OperationResult::ResolveVoid (this )
4687+ : OperationResult::RejectErrorCode (
4688+ this , error_code, error_message);
4689+ }
4690+
4691+ private:
4692+ std::pmr::string extension_path_;
4693+ };
4694+
46714695using Operation = std::variant<ExecOperation,
46724696 StatementGetOperation,
46734697 StatementAllOperation,
@@ -4677,6 +4701,7 @@ using Operation = std::variant<ExecOperation,
46774701 IsInTransactionOperation,
46784702 LocationOperation,
46794703 UpdateDbConfigOperation,
4704+ LoadExtensionOperation,
46804705 CloseOperation>;
46814706
46824707template <typename T, typename V>
@@ -4981,6 +5006,9 @@ v8::Local<v8::FunctionTemplate> CreateDatabaseConstructorTemplate(
49815006 SetProtoMethod (isolate, tmpl, " isInTransaction" , Database::IsInTransaction);
49825007 SetProtoMethod (isolate, tmpl, " location" , Database::Location);
49835008 SetProtoMethod (isolate, tmpl, " enableDefensive" , Database::EnableDefensive);
5009+ SetProtoMethod (
5010+ isolate, tmpl, " enableLoadExtension" , Database::EnableLoadExtension);
5011+ SetProtoMethod (isolate, tmpl, " loadExtension" , Database::LoadExtension);
49845012
49855013 Local<String> sqlite_type_key = FIXED_ONE_BYTE_STRING (isolate, " sqlite-type" );
49865014 Local<v8::Symbol> sqlite_type_symbol =
@@ -5285,6 +5313,57 @@ void Database::EnableDefensive(
52855313 SQLITE_DBCONFIG_DEFENSIVE, enable_defensive ? 1 : 0 ));
52865314}
52875315
5316+ void Database::EnableLoadExtension (
5317+ const v8::FunctionCallbackInfo<v8::Value>& args) {
5318+ Database* db;
5319+ ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
5320+ Environment* env = Environment::GetCurrent (args);
5321+ REJECT_AND_RETURN_ON_INVALID_STATE (
5322+ env, args, !db->IsOpen (), " database is not open" );
5323+
5324+ REJECT_AND_RETURN_ON_INVALID_ARG_TYPE (
5325+ env,
5326+ args,
5327+ !args[0 ]->IsBoolean () || args.Length () != 1 ,
5328+ " \" enableLoadExtension\" requires exactly one boolean argument." );
5329+
5330+ auto enable_load_extension = args[0 ].As <Boolean>()->Value ();
5331+ REJECT_AND_RETURN_ON_INVALID_STATE (
5332+ env,
5333+ args,
5334+ !db->allow_load_extension_ && enable_load_extension,
5335+ " Cannot enable extension loading because it was disabled at database "
5336+ " creation." );
5337+
5338+ db->enable_load_extension_ = enable_load_extension;
5339+ args.GetReturnValue ().Set (db->Schedule <UpdateDbConfigOperation>(
5340+ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, enable_load_extension ? 1 : 0 ));
5341+ }
5342+
5343+ void Database::LoadExtension (const v8::FunctionCallbackInfo<v8::Value>& args) {
5344+ Database* db;
5345+ ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
5346+ Environment* env = Environment::GetCurrent (args);
5347+ REJECT_AND_RETURN_ON_INVALID_STATE (
5348+ env, args, !db->IsOpen (), " database is not open" );
5349+ REJECT_AND_RETURN_ON_INVALID_STATE (env,
5350+ args,
5351+ !db->enable_load_extension_ ,
5352+ " extension loading is not allowed" );
5353+
5354+ REJECT_AND_RETURN_ON_INVALID_ARG_TYPE (
5355+ env,
5356+ args,
5357+ !args[0 ]->IsString () || args.Length () != 1 ,
5358+ " \" loadExtension\" requires exactly one string argument." );
5359+
5360+ BufferValue path (env->isolate (), args[0 ]);
5361+ ToNamespacedPath (env, &path);
5362+
5363+ args.GetReturnValue ().Set (db->Schedule <LoadExtensionOperation>(
5364+ std::pmr::string (path.ToStringView ())));
5365+ }
5366+
52885367Statement::~Statement () {
52895368 if (!IsDisposed ()) {
52905369 // Our operations keep a BaseObjectPtr to this Statement, so we can be sure
0 commit comments