◐ Shell
clean mode source ↗

sqlite: add `StatementSync.prototype.iterate` method · nodejs/node@b4041e5

@@ -22,6 +22,7 @@ using v8::ConstructorBehavior;

2222

using v8::Context;

2323

using v8::DontDelete;

2424

using v8::Exception;

25+

using v8::External;

2526

using v8::Function;

2627

using v8::FunctionCallback;

2728

using v8::FunctionCallbackInfo;

@@ -790,6 +791,180 @@ void StatementSync::All(const FunctionCallbackInfo<Value>& args) {

790791

args.GetReturnValue().Set(Array::New(isolate, rows.data(), rows.size()));

791792

}

792793794+

void StatementSync::IterateReturnCallback(

795+

const FunctionCallbackInfo<Value>& args) {

796+

Environment* env = Environment::GetCurrent(args);

797+

auto isolate = env->isolate();

798+

auto context = isolate->GetCurrentContext();

799+800+

auto self = args.This();

801+

// iterator has fetch all result or break, prevent next func to return result

802+

self->Set(context, env->isfinished_string(), Boolean::New(isolate, true))

803+

.ToChecked();

804+805+

auto external_stmt = Local<External>::Cast(

806+

self->Get(context, env->statement_string()).ToLocalChecked());

807+

auto stmt = static_cast<StatementSync*>(external_stmt->Value());

808+

if (!stmt->IsFinalized()) {

809+

sqlite3_reset(stmt->statement_);

810+

}

811+812+

LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()});

813+

LocalVector<Value> values(isolate,

814+

{Boolean::New(isolate, true), Null(isolate)});

815+816+

DCHECK_EQ(keys.size(), values.size());

817+

Local<Object> result = Object::New(

818+

isolate, Null(isolate), keys.data(), values.data(), keys.size());

819+

args.GetReturnValue().Set(result);

820+

}

821+822+

void StatementSync::IterateNextCallback(

823+

const FunctionCallbackInfo<Value>& args) {

824+

Environment* env = Environment::GetCurrent(args);

825+

auto isolate = env->isolate();

826+

auto context = isolate->GetCurrentContext();

827+828+

auto self = args.This();

829+830+

// skip iteration if is_finished

831+

auto is_finished = Local<Boolean>::Cast(

832+

self->Get(context, env->isfinished_string()).ToLocalChecked());

833+

if (is_finished->Value()) {

834+

LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()});

835+

LocalVector<Value> values(isolate,

836+

{Boolean::New(isolate, true), Null(isolate)});

837+838+

DCHECK_EQ(keys.size(), values.size());

839+

Local<Object> result = Object::New(

840+

isolate, Null(isolate), keys.data(), values.data(), keys.size());

841+

args.GetReturnValue().Set(result);

842+

return;

843+

}

844+845+

auto external_stmt = Local<External>::Cast(

846+

self->Get(context, env->statement_string()).ToLocalChecked());

847+

auto stmt = static_cast<StatementSync*>(external_stmt->Value());

848+

auto num_cols =

849+

Local<Integer>::Cast(

850+

self->Get(context, env->num_cols_string()).ToLocalChecked())

851+

->Value();

852+853+

THROW_AND_RETURN_ON_BAD_STATE(

854+

env, stmt->IsFinalized(), "statement has been finalized");

855+856+

int r = sqlite3_step(stmt->statement_);

857+

if (r != SQLITE_ROW) {

858+

CHECK_ERROR_OR_THROW(

859+

env->isolate(), stmt->db_->Connection(), r, SQLITE_DONE, void());

860+861+

// cleanup when no more rows to fetch

862+

sqlite3_reset(stmt->statement_);

863+

self->Set(context, env->isfinished_string(), Boolean::New(isolate, true))

864+

.ToChecked();

865+866+

LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()});

867+

LocalVector<Value> values(isolate,

868+

{Boolean::New(isolate, true), Null(isolate)});

869+870+

DCHECK_EQ(keys.size(), values.size());

871+

Local<Object> result = Object::New(

872+

isolate, Null(isolate), keys.data(), values.data(), keys.size());

873+

args.GetReturnValue().Set(result);

874+

return;

875+

}

876+877+

LocalVector<Name> row_keys(isolate);

878+

row_keys.reserve(num_cols);

879+

LocalVector<Value> row_values(isolate);

880+

row_values.reserve(num_cols);

881+

for (int i = 0; i < num_cols; ++i) {

882+

Local<Name> key;

883+

if (!stmt->ColumnNameToName(i).ToLocal(&key)) return;

884+

Local<Value> val;

885+

if (!stmt->ColumnToValue(i).ToLocal(&val)) return;

886+

row_keys.emplace_back(key);

887+

row_values.emplace_back(val);

888+

}

889+890+

Local<Object> row = Object::New(

891+

isolate, Null(isolate), row_keys.data(), row_values.data(), num_cols);

892+893+

LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()});

894+

LocalVector<Value> values(isolate, {Boolean::New(isolate, false), row});

895+896+

DCHECK_EQ(keys.size(), values.size());

897+

Local<Object> result = Object::New(

898+

isolate, Null(isolate), keys.data(), values.data(), keys.size());

899+

args.GetReturnValue().Set(result);

900+

}

901+902+

void StatementSync::Iterate(const FunctionCallbackInfo<Value>& args) {

903+

StatementSync* stmt;

904+

ASSIGN_OR_RETURN_UNWRAP(&stmt, args.This());

905+

Environment* env = Environment::GetCurrent(args);

906+

THROW_AND_RETURN_ON_BAD_STATE(

907+

env, stmt->IsFinalized(), "statement has been finalized");

908+

auto isolate = env->isolate();

909+

auto context = env->context();

910+

int r = sqlite3_reset(stmt->statement_);

911+

CHECK_ERROR_OR_THROW(

912+

env->isolate(), stmt->db_->Connection(), r, SQLITE_OK, void());

913+914+

if (!stmt->BindParams(args)) {

915+

return;

916+

}

917+918+

Local<Function> next_func =

919+

Function::New(context, StatementSync::IterateNextCallback)

920+

.ToLocalChecked();

921+

Local<Function> return_func =

922+

Function::New(context, StatementSync::IterateReturnCallback)

923+

.ToLocalChecked();

924+925+

LocalVector<Name> keys(isolate, {env->next_string(), env->return_string()});

926+

LocalVector<Value> values(isolate, {next_func, return_func});

927+928+

Local<Object> global = context->Global();

929+

Local<Value> js_iterator;

930+

Local<Value> js_iterator_prototype;

931+

if (!global->Get(context, env->iterator_string()).ToLocal(&js_iterator))

932+

return;

933+

if (!js_iterator.As<Object>()

934+

->Get(context, env->prototype_string())

935+

.ToLocal(&js_iterator_prototype))

936+

return;

937+938+

DCHECK_EQ(keys.size(), values.size());

939+

Local<Object> iterable_iterator = Object::New(

940+

isolate, js_iterator_prototype, keys.data(), values.data(), keys.size());

941+942+

auto num_cols_pd = v8::PropertyDescriptor(

943+

v8::Integer::New(isolate, sqlite3_column_count(stmt->statement_)), false);

944+

num_cols_pd.set_enumerable(false);

945+

num_cols_pd.set_configurable(false);

946+

iterable_iterator

947+

->DefineProperty(context, env->num_cols_string(), num_cols_pd)

948+

.ToChecked();

949+950+

auto stmt_pd =

951+

v8::PropertyDescriptor(v8::External::New(isolate, stmt), false);

952+

stmt_pd.set_enumerable(false);

953+

stmt_pd.set_configurable(false);

954+

iterable_iterator->DefineProperty(context, env->statement_string(), stmt_pd)

955+

.ToChecked();

956+957+

auto is_finished_pd =

958+

v8::PropertyDescriptor(v8::Boolean::New(isolate, false), true);

959+

stmt_pd.set_enumerable(false);

960+

stmt_pd.set_configurable(false);

961+

iterable_iterator

962+

->DefineProperty(context, env->isfinished_string(), is_finished_pd)

963+

.ToChecked();

964+965+

args.GetReturnValue().Set(iterable_iterator);

966+

}

967+793968

void StatementSync::Get(const FunctionCallbackInfo<Value>& args) {

794969

StatementSync* stmt;

795970

ASSIGN_OR_RETURN_UNWRAP(&stmt, args.This());

@@ -987,6 +1162,7 @@ Local<FunctionTemplate> StatementSync::GetConstructorTemplate(

9871162

tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "StatementSync"));

9881163

tmpl->InstanceTemplate()->SetInternalFieldCount(

9891164

StatementSync::kInternalFieldCount);

1165+

SetProtoMethod(isolate, tmpl, "iterate", StatementSync::Iterate);

9901166

SetProtoMethod(isolate, tmpl, "all", StatementSync::All);

9911167

SetProtoMethod(isolate, tmpl, "get", StatementSync::Get);

9921168

SetProtoMethod(isolate, tmpl, "run", StatementSync::Run);