sqlite: add `StatementSync.prototype.iterate` method · nodejs/node@b4041e5
@@ -22,6 +22,7 @@ using v8::ConstructorBehavior;
2222using v8::Context;
2323using v8::DontDelete;
2424using v8::Exception;
25+using v8::External;
2526using v8::Function;
2627using v8::FunctionCallback;
2728using 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+793968void StatementSync::Get(const FunctionCallbackInfo<Value>& args) {
794969 StatementSync* stmt;
795970ASSIGN_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);
9901166SetProtoMethod(isolate, tmpl, "all", StatementSync::All);
9911167SetProtoMethod(isolate, tmpl, "get", StatementSync::Get);
9921168SetProtoMethod(isolate, tmpl, "run", StatementSync::Run);