From ff709c4a12a5aabecaa6d870aab5c4accb3c6be4 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Sun, 24 Mar 2024 00:10:46 -0700 Subject: [PATCH] src: precompute field names Field names do not change from row to row, so let's create JS strings only during the process of calling back into JS with the first row, not with each row. We save the JS strings we created while calling back with the first row, and re-use them when creating objects for subsequent rows. Re: https://github.com/nodejs/abi-stable-node/issues/458 --- src/statement.cc | 21 +++++++++++++++++---- src/statement.h | 2 +- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/statement.cc b/src/statement.cc index fc49b90f..604292a7 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -587,8 +587,9 @@ void Statement::Work_AfterAll(napi_env e, napi_status status, void* data) { Napi::Array result(Napi::Array::New(env, baton->rows.size())); auto it = static_cast(baton->rows.begin()); decltype(it) end = baton->rows.end(); + std::vector js_names; for (int i = 0; it < end; ++it, i++) { - (result).Set(i, RowToJS(env, it->get())); + (result).Set(i, RowToJS(env, it->get(), &js_names)); } Napi::Value argv[] = { env.Null(), result }; @@ -711,8 +712,9 @@ void Statement::AsyncEach(uv_async_t* handle) { Napi::Value argv[2]; argv[0] = env.Null(); + std::vector js_names; for(auto& row : rows) { - argv[1] = RowToJS(env,row.get()); + argv[1] = RowToJS(env, row.get(), &js_names); async->retrieved++; TRY_CATCH_CALL(async->stmt->Value(), cb, 2, argv); } @@ -787,11 +789,19 @@ void Statement::Work_AfterReset(napi_env e, napi_status status, void* data) { STATEMENT_END(); } -Napi::Value Statement::RowToJS(Napi::Env env, Row* row) { +Napi::Value Statement::RowToJS(Napi::Env env, Row* row, std::vector* js_names) { + // Must fill this array if empty before we enter the escapable handle scope below. + if (js_names != nullptr && js_names->empty()) { + for (auto& field : *row) { + js_names->push_back(Napi::String::New(env, field->name)); + } + } + Napi::EscapableHandleScope scope(env); auto result = Napi::Object::New(env); + size_t js_name_idx = 0; for (auto& field : *row) { Napi::Value value; @@ -816,7 +826,10 @@ Napi::Value Statement::RowToJS(Napi::Env env, Row* row) { } break; } - result.Set(field->name, value); + napi_value field_name = js_names && js_names->empty() + ? Napi::String::New(env, field->name) + : (*js_names)[js_name_idx++]; + result.Set(field_name, value); } return scope.Escape(result); diff --git a/src/statement.h b/src/statement.h index c522c0fd..91891840 100644 --- a/src/statement.h +++ b/src/statement.h @@ -220,7 +220,7 @@ class Statement : public Napi::ObjectWrap { bool Bind(const Parameters ¶meters); static void GetRow(Row* row, sqlite3_stmt* stmt); - static Napi::Value RowToJS(Napi::Env env, Row* row); + static Napi::Value RowToJS(Napi::Env env, Row* row, std::vector* js_names = nullptr); void Schedule(Work_Callback callback, Baton* baton); void Process(); void CleanQueue();