Skip to content

Commit d3535a9

Browse files
committed
wip
1 parent 6160108 commit d3535a9

3 files changed

Lines changed: 341 additions & 0 deletions

File tree

src/env_properties.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@
456456
V(secure_context_constructor_template, v8::FunctionTemplate) \
457457
V(shutdown_wrap_template, v8::ObjectTemplate) \
458458
V(socketaddress_constructor_template, v8::FunctionTemplate) \
459+
V(sqlite_statement_constructor_template, v8::FunctionTemplate) \
459460
V(sqlite_statement_sync_constructor_template, v8::FunctionTemplate) \
460461
V(sqlite_statement_sync_iterator_constructor_template, v8::FunctionTemplate) \
461462
V(sqlite_session_constructor_template, v8::FunctionTemplate) \

src/node_sqlite.cc

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
427472
class 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+
21252402
void 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+
23592649
Local<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+
24002705
BaseObjectPtr<StatementSync> StatementSync::Create(
24012706
Environment* env, BaseObjectPtr<DatabaseSync> db, sqlite3_stmt* stmt) {
24022707
Local<Object> obj;

0 commit comments

Comments
 (0)