@@ -424,6 +424,51 @@ class CustomAggregate {
424424 Global<Function> result_fn_;
425425};
426426
427+ class SQLiteAsyncWork : public ThreadPoolWork {
428+ public:
429+ explicit SQLiteAsyncWork (Environment* env,
430+ DatabaseSync* db,
431+ Local<Promise::Resolver> resolver,
432+ std::function<int ()> work,
433+ std::function<void()> after)
434+ : ThreadPoolWork(env, " node_sqlite_async" ),
435+ env_(env),
436+ db_(db),
437+ work_(work),
438+ after_(after) {
439+ resolver_.Reset (env->isolate (), resolver);
440+ }
441+
442+ void DoThreadPoolWork () override {
443+ if (work_) {
444+ work_status_ = work_ ();
445+ }
446+ }
447+
448+ void AfterThreadPoolWork (int status) override {
449+ Isolate* isolate = env_->isolate ();
450+ HandleScope handle_scope (isolate);
451+ Local<Promise::Resolver> resolver =
452+ Local<Promise::Resolver>::New (isolate, resolver_);
453+
454+ if (work_status_ != 0 ) {
455+ // create sqlite error and put in the rejection
456+ resolver->Reject (env_->context (), Null (isolate)).ToChecked ();
457+ return ;
458+ }
459+
460+ resolver->Resolve (env ()->context (), Undefined (isolate)).ToChecked ();
461+ }
462+
463+ private:
464+ Environment* env_;
465+ DatabaseSync* db_;
466+ Global<Promise::Resolver> resolver_;
467+ std::function<int ()> work_ = nullptr ;
468+ std::function<void ()> after_ = nullptr ;
469+ int work_status_ = 0 ;
470+ };
471+
427472class BackupJob : public ThreadPoolWork {
428473 public:
429474 explicit BackupJob (Environment* env,
@@ -1018,17 +1063,31 @@ void DatabaseSync::Prepare(const FunctionCallbackInfo<Value>& args) {
10181063 ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
10191064 Environment* env = Environment::GetCurrent (args);
10201065 THROW_AND_RETURN_ON_BAD_STATE (env, !db->IsOpen (), " database is not open" );
1066+ bool async = false ;
10211067
10221068 if (!args[0 ]->IsString ()) {
10231069 THROW_ERR_INVALID_ARG_TYPE (env->isolate (),
10241070 " The \" sql\" argument must be a string." );
10251071 return ;
10261072 }
10271073
1074+ if (args[1 ]->IsBoolean ()) {
1075+ async = args[1 ].As <Boolean>()->IsTrue ();
1076+ }
1077+
10281078 Utf8Value sql (env->isolate (), args[0 ].As <String>());
10291079 sqlite3_stmt* s = nullptr ;
10301080 int r = sqlite3_prepare_v2 (db->connection_ , *sql, -1 , &s, 0 );
10311081 CHECK_ERROR_OR_THROW (env->isolate (), db, r, SQLITE_OK, void ());
1082+
1083+ if (async) {
1084+ BaseObjectPtr<Statement> stmt =
1085+ Statement::Create (env, BaseObjectPtr<DatabaseSync>(db), s, async);
1086+ db->async_statements_ .insert (stmt.get ());
1087+ args.GetReturnValue ().Set (stmt->object ());
1088+ return ;
1089+ }
1090+
10321091 BaseObjectPtr<StatementSync> stmt =
10331092 StatementSync::Create (env, BaseObjectPtr<DatabaseSync>(db), s);
10341093 db->statements_ .insert (stmt.get ());
@@ -2122,6 +2181,224 @@ void StatementSync::Get(const FunctionCallbackInfo<Value>& args) {
21222181 }
21232182}
21242183
2184+ Statement::Statement (Environment* env,
2185+ Local<Object> object,
2186+ BaseObjectPtr<DatabaseSync> db,
2187+ sqlite3_stmt* stmt,
2188+ bool async)
2189+ : BaseObject(env, object), db_(std::move(db)) {
2190+ MakeWeak ();
2191+ statement_ = stmt;
2192+ // In the future, some of these options could be set at the database
2193+ // connection level and inherited by statements to reduce boilerplate.
2194+ return_arrays_ = false ;
2195+ use_big_ints_ = false ;
2196+ allow_bare_named_params_ = true ;
2197+ allow_unknown_named_params_ = false ;
2198+ bare_named_params_ = std::nullopt ;
2199+ }
2200+
2201+ void Statement::MemoryInfo (MemoryTracker* tracker) const {}
2202+
2203+ bool Statement::BindValue (const Local<Value>& value, const int index) {
2204+ // SQLite only supports a subset of JavaScript types. Some JS types such as
2205+ // functions don't make sense to support. Other JS types such as booleans and
2206+ // Dates could be supported by converting them to numbers. However, there
2207+ // would not be a good way to read the values back from SQLite with the
2208+ // original type.
2209+ int r;
2210+ if (value->IsNumber ()) {
2211+ double val = value.As <Number>()->Value ();
2212+ r = sqlite3_bind_double (statement_, index, val);
2213+ } else if (value->IsString ()) {
2214+ Utf8Value val (env ()->isolate (), value.As <String>());
2215+ r = sqlite3_bind_text (
2216+ statement_, index, *val, val.length (), SQLITE_TRANSIENT);
2217+ } else if (value->IsNull ()) {
2218+ r = sqlite3_bind_null (statement_, index);
2219+ } else if (value->IsArrayBufferView ()) {
2220+ ArrayBufferViewContents<uint8_t > buf (value);
2221+ r = sqlite3_bind_blob (
2222+ statement_, index, buf.data (), buf.length (), SQLITE_TRANSIENT);
2223+ } else if (value->IsBigInt ()) {
2224+ bool lossless;
2225+ int64_t as_int = value.As <BigInt>()->Int64Value (&lossless);
2226+ if (!lossless) {
2227+ THROW_ERR_INVALID_ARG_VALUE (env (), " BigInt value is too large to bind." );
2228+ return false ;
2229+ }
2230+ r = sqlite3_bind_int64 (statement_, index, as_int);
2231+ } else {
2232+ THROW_ERR_INVALID_ARG_TYPE (
2233+ env ()->isolate (),
2234+ " Provided value cannot be bound to SQLite parameter %d." ,
2235+ index);
2236+ return false ;
2237+ }
2238+
2239+ CHECK_ERROR_OR_THROW (env ()->isolate (), db_.get (), r, SQLITE_OK, false );
2240+ return true ;
2241+ }
2242+
2243+ inline bool Statement::IsFinalized () {
2244+ return statement_ == nullptr ;
2245+ }
2246+
2247+ bool Statement::BindParams (const FunctionCallbackInfo<Value>& args) {
2248+ int r = sqlite3_clear_bindings (statement_);
2249+ CHECK_ERROR_OR_THROW (env ()->isolate (), db_.get (), r, SQLITE_OK, false );
2250+
2251+ int anon_idx = 1 ;
2252+ int anon_start = 0 ;
2253+
2254+ if (args[0 ]->IsObject () && !args[0 ]->IsArrayBufferView ()) {
2255+ Local<Object> obj = args[0 ].As <Object>();
2256+ Local<Context> context = obj->GetIsolate ()->GetCurrentContext ();
2257+ Local<Array> keys;
2258+ if (!obj->GetOwnPropertyNames (context).ToLocal (&keys)) {
2259+ return false ;
2260+ }
2261+
2262+ if (allow_bare_named_params_ && !bare_named_params_.has_value ()) {
2263+ bare_named_params_.emplace ();
2264+ int param_count = sqlite3_bind_parameter_count (statement_);
2265+ // Parameter indexing starts at one.
2266+ for (int i = 1 ; i <= param_count; ++i) {
2267+ const char * name = sqlite3_bind_parameter_name (statement_, i);
2268+ if (name == nullptr ) {
2269+ continue ;
2270+ }
2271+
2272+ auto bare_name = std::string (name + 1 );
2273+ auto full_name = std::string (name);
2274+ auto insertion = bare_named_params_->insert ({bare_name, full_name});
2275+ if (insertion.second == false ) {
2276+ auto existing_full_name = (*insertion.first ).second ;
2277+ if (full_name != existing_full_name) {
2278+ THROW_ERR_INVALID_STATE (
2279+ env (),
2280+ " Cannot create bare named parameter '%s' because of "
2281+ " conflicting names '%s' and '%s'." ,
2282+ bare_name,
2283+ existing_full_name,
2284+ full_name);
2285+ return false ;
2286+ }
2287+ }
2288+ }
2289+ }
2290+
2291+ uint32_t len = keys->Length ();
2292+ for (uint32_t j = 0 ; j < len; j++) {
2293+ Local<Value> key;
2294+ if (!keys->Get (context, j).ToLocal (&key)) {
2295+ return false ;
2296+ }
2297+
2298+ Utf8Value utf8_key (env ()->isolate (), key);
2299+ int r = sqlite3_bind_parameter_index (statement_, *utf8_key);
2300+ if (r == 0 ) {
2301+ if (allow_bare_named_params_) {
2302+ auto lookup = bare_named_params_->find (std::string (*utf8_key));
2303+ if (lookup != bare_named_params_->end ()) {
2304+ r = sqlite3_bind_parameter_index (statement_,
2305+ lookup->second .c_str ());
2306+ }
2307+ }
2308+
2309+ if (r == 0 ) {
2310+ if (allow_unknown_named_params_) {
2311+ continue ;
2312+ } else {
2313+ THROW_ERR_INVALID_STATE (
2314+ env (), " Unknown named parameter '%s'" , *utf8_key);
2315+ return false ;
2316+ }
2317+ }
2318+ }
2319+
2320+ Local<Value> value;
2321+ if (!obj->Get (context, key).ToLocal (&value)) {
2322+ return false ;
2323+ }
2324+
2325+ if (!BindValue (value, r)) {
2326+ return false ;
2327+ }
2328+ }
2329+ anon_start++;
2330+ }
2331+
2332+ for (int i = anon_start; i < args.Length (); ++i) {
2333+ while (sqlite3_bind_parameter_name (statement_, anon_idx) != nullptr ) {
2334+ anon_idx++;
2335+ }
2336+
2337+ if (!BindValue (args[i], anon_idx)) {
2338+ return false ;
2339+ }
2340+
2341+ anon_idx++;
2342+ }
2343+
2344+ return true ;
2345+ }
2346+
2347+ void Statement::Run (const FunctionCallbackInfo<Value>& args) {
2348+ Statement* stmt;
2349+ ASSIGN_OR_RETURN_UNWRAP (&stmt, args.This ());
2350+ Environment* env = Environment::GetCurrent (args);
2351+ THROW_AND_RETURN_ON_BAD_STATE (
2352+ env, stmt->IsFinalized (), " statement has been finalized" );
2353+
2354+ auto task = [args, stmt, env]() -> Local<Value> {
2355+ int r = sqlite3_reset (stmt->statement_ );
2356+ /* CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK,
2357+ * void()); */
2358+ if (r != SQLITE_OK) {
2359+ return Local<Value>();
2360+ }
2361+
2362+ if (!stmt->BindParams (args)) {
2363+ return Local<Value>();
2364+ }
2365+
2366+ sqlite3_step (stmt->statement_ );
2367+ r = sqlite3_reset (stmt->statement_ );
2368+ /* CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK,
2369+ * void()); */
2370+ if (r != SQLITE_OK) {
2371+ return Local<Value>();
2372+ }
2373+ Local<Object> result = Object::New (env->isolate ());
2374+ sqlite3_int64 last_insert_rowid =
2375+ sqlite3_last_insert_rowid (stmt->db_ ->Connection ());
2376+ sqlite3_int64 changes = sqlite3_changes64 (stmt->db_ ->Connection ());
2377+ Local<Value> last_insert_rowid_val;
2378+ Local<Value> changes_val;
2379+
2380+ if (stmt->use_big_ints_ ) {
2381+ last_insert_rowid_val = BigInt::New (env->isolate (), last_insert_rowid);
2382+ changes_val = BigInt::New (env->isolate (), changes);
2383+ } else {
2384+ last_insert_rowid_val = Number::New (env->isolate (), last_insert_rowid);
2385+ changes_val = Number::New (env->isolate (), changes);
2386+ }
2387+
2388+ if (result
2389+ ->Set (env->context (),
2390+ env->last_insert_rowid_string (),
2391+ last_insert_rowid_val)
2392+ .IsNothing () ||
2393+ result->Set (env->context (), env->changes_string (), changes_val)
2394+ .IsNothing ()) {
2395+ return Local<Value>();
2396+ }
2397+
2398+ return result;
2399+ };
2400+ }
2401+
21252402void StatementSync::Run (const FunctionCallbackInfo<Value>& args) {
21262403 StatementSync* stmt;
21272404 ASSIGN_OR_RETURN_UNWRAP (&stmt, args.This ());
@@ -2356,6 +2633,19 @@ static inline void SetSideEffectFreeGetter(
23562633 name, getter, Local<FunctionTemplate>(), DontDelete);
23572634}
23582635
2636+ Local<FunctionTemplate> Statement::GetConstructorTemplate (Environment* env) {
2637+ Local<FunctionTemplate> tmpl = env->sqlite_statement_constructor_template ();
2638+ if (tmpl.IsEmpty ()) {
2639+ Isolate* isolate = env->isolate ();
2640+ tmpl = NewFunctionTemplate (isolate, IllegalConstructor);
2641+ tmpl->SetClassName (FIXED_ONE_BYTE_STRING (isolate, " Statement" ));
2642+ tmpl->InstanceTemplate ()->SetInternalFieldCount (
2643+ Statement::kInternalFieldCount );
2644+ env->set_sqlite_statement_constructor_template (tmpl);
2645+ }
2646+ return tmpl;
2647+ }
2648+
23592649Local<FunctionTemplate> StatementSync::GetConstructorTemplate (
23602650 Environment* env) {
23612651 Local<FunctionTemplate> tmpl =
@@ -2397,6 +2687,21 @@ Local<FunctionTemplate> StatementSync::GetConstructorTemplate(
23972687 return tmpl;
23982688}
23992689
2690+ BaseObjectPtr<Statement> Statement::Create (Environment* env,
2691+ BaseObjectPtr<DatabaseSync> db,
2692+ sqlite3_stmt* stmt,
2693+ bool async) {
2694+ Local<Object> obj;
2695+ if (!GetConstructorTemplate (env)
2696+ ->InstanceTemplate ()
2697+ ->NewInstance (env->context ())
2698+ .ToLocal (&obj)) {
2699+ return nullptr ;
2700+ }
2701+
2702+ return MakeBaseObject<Statement>(env, obj, std::move (db), stmt, async);
2703+ }
2704+
24002705BaseObjectPtr<StatementSync> StatementSync::Create (
24012706 Environment* env, BaseObjectPtr<DatabaseSync> db, sqlite3_stmt* stmt) {
24022707 Local<Object> obj;
0 commit comments