sqlite: use OneByte for ASCII text and internalize col names · nodejs/node@2f37f91
@@ -8,6 +8,7 @@
88#include "node_errors.h"
99#include "node_mem-inl.h"
1010#include "node_url.h"
11+#include "simdutf.h"
1112#include "sqlite3.h"
1213#include "threadpoolwork-inl.h"
1314#include "util-inl.h"
@@ -64,6 +65,20 @@ using v8::TryCatch;
6465using v8::Uint8Array;
6566using v8::Value;
666768+inline MaybeLocal<String> Utf8StringMaybeOneByte(Isolate* isolate,
69+ std::string_view input) {
70+const int len = static_cast<int>(input.size());
71+if (simdutf::validate_ascii(input.data(), input.size())) {
72+return String::NewFromOneByte(
73+ isolate,
74+reinterpret_cast<const uint8_t*>(input.data()),
75+ NewStringType::kNormal,
76+ len);
77+ }
78+return String::NewFromUtf8(
79+ isolate, input.data(), NewStringType::kNormal, len);
80+}
81+6782#define CHECK_ERROR_OR_THROW(isolate, db, expr, expected, ret) \
6883do { \
6984int r_ = (expr); \
@@ -106,7 +121,10 @@ using v8::Value;
106121case SQLITE_TEXT: { \
107122const char* v = \
108123reinterpret_cast<const char*>(sqlite3_##from##_text(__VA_ARGS__)); \
109- (result) = String::NewFromUtf8((isolate), v).As<Value>(); \
124+const int v_len = sqlite3_##from##_bytes(__VA_ARGS__); \
125+ (result) = \
126+Utf8StringMaybeOneByte((isolate), std::string_view(v, v_len)) \
127+ .As<Value>(); \
110128break; \
111129 } \
112130case SQLITE_NULL: { \
@@ -2547,6 +2565,11 @@ StatementSync::~StatementSync() {
25472565void StatementSync::Finalize() {
25482566sqlite3_finalize(statement_);
25492567 statement_ = nullptr;
2568+InvalidateColumnNameCache();
2569+}
2570+2571+void StatementSync::InvalidateColumnNameCache() {
2572+ cached_column_names_.clear();
25502573}
2551257425522575inline bool StatementSync::IsFinalized() {
@@ -2730,7 +2753,42 @@ MaybeLocal<Name> StatementSync::ColumnNameToName(const int column) {
27302753return MaybeLocal<Name>();
27312754 }
273227552733-return String::NewFromUtf8(env()->isolate(), col_name).As<Name>();
2756+return String::NewFromUtf8(
2757+env()->isolate(), col_name, NewStringType::kInternalized)
2758+ .As<Name>();
2759+}
2760+2761+// Populates `keys` with cached column names, rebuilding the cache if the
2762+// statement was re-prepared.
2763+bool StatementSync::GetCachedColumnNames(LocalVector<Name>* keys) {
2764+ Isolate* isolate = env()->isolate();
2765+2766+const int reprepare_count =
2767+sqlite3_stmt_status(statement_, SQLITE_STMTSTATUS_REPREPARE, false);
2768+if (reprepare_count != cached_column_names_reprepare_count_) {
2769+ cached_column_names_.clear();
2770+const int num_cols = sqlite3_column_count(statement_);
2771+if (num_cols == 0) {
2772+ cached_column_names_reprepare_count_ = reprepare_count;
2773+return true;
2774+ }
2775+ cached_column_names_.reserve(num_cols);
2776+for (int i = 0; i < num_cols; ++i) {
2777+ Local<Name> key;
2778+if (!ColumnNameToName(i).ToLocal(&key)) {
2779+InvalidateColumnNameCache();
2780+return false;
2781+ }
2782+ cached_column_names_.emplace_back(Global<Name>(isolate, key));
2783+ }
2784+ cached_column_names_reprepare_count_ = reprepare_count;
2785+ }
2786+2787+ keys->reserve(cached_column_names_.size());
2788+for (const auto& name : cached_column_names_) {
2789+ keys->emplace_back(name.Get(isolate));
2790+ }
2791+return true;
27342792}
2735279327362794MaybeLocal<Value> StatementExecutionHelper::ColumnToValue(Environment* env,
@@ -2752,7 +2810,9 @@ MaybeLocal<Name> StatementExecutionHelper::ColumnNameToName(Environment* env,
27522810return MaybeLocal<Name>();
27532811 }
275428122755-return String::NewFromUtf8(env->isolate(), col_name).As<Name>();
2813+return String::NewFromUtf8(
2814+ env->isolate(), col_name, NewStringType::kInternalized)
2815+ .As<Name>();
27562816}
2757281727582818void StatementSync::MemoryInfo(MemoryTracker* tracker) const {}
@@ -3662,12 +3722,9 @@ void StatementSyncIterator::Next(const FunctionCallbackInfo<Value>& args) {
36623722if (iter->stmt_->return_arrays_) {
36633723 row_value = Array::New(isolate, row_values.data(), row_values.size());
36643724 } else {
3665- row_keys.reserve(num_cols);
3666-for (int i = 0; i < num_cols; ++i) {
3667- Local<Name> key;
3668-if (!iter->stmt_->ColumnNameToName(i).ToLocal(&key)) return;
3669- row_keys.emplace_back(key);
3670- }
3725+// Use cached internalized column names to avoid repeated V8 string
3726+// creation and enable hidden class sharing across row objects.
3727+if (!iter->stmt_->GetCachedColumnNames(&row_keys)) return;
3671372836723729DCHECK_EQ(row_keys.size(), row_values.size());
36733730 row_value = Object::New(