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;
6667
68+ 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 ) \
6883 do { \
6984 int r_ = (expr); \
@@ -106,7 +121,10 @@ using v8::Value;
106121 case SQLITE_TEXT: { \
107122 const char * v = \
108123 reinterpret_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>(); \
110128 break ; \
111129 } \
112130 case SQLITE_NULL: { \
@@ -2547,6 +2565,11 @@ StatementSync::~StatementSync() {
25472565void StatementSync::Finalize () {
25482566 sqlite3_finalize (statement_);
25492567 statement_ = nullptr ;
2568+ InvalidateColumnNameCache ();
2569+ }
2570+
2571+ void StatementSync::InvalidateColumnNameCache () {
2572+ cached_column_names_.clear ();
25502573}
25512574
25522575inline bool StatementSync::IsFinalized () {
@@ -2730,7 +2753,42 @@ MaybeLocal<Name> StatementSync::ColumnNameToName(const int column) {
27302753 return MaybeLocal<Name>();
27312754 }
27322755
2733- 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}
27352793
27362794MaybeLocal<Value> StatementExecutionHelper::ColumnToValue (Environment* env,
@@ -2752,7 +2810,9 @@ MaybeLocal<Name> StatementExecutionHelper::ColumnNameToName(Environment* env,
27522810 return MaybeLocal<Name>();
27532811 }
27542812
2755- return String::NewFromUtf8 (env->isolate (), col_name).As <Name>();
2813+ return String::NewFromUtf8 (
2814+ env->isolate (), col_name, NewStringType::kInternalized )
2815+ .As <Name>();
27562816}
27572817
27582818void StatementSync::MemoryInfo (MemoryTracker* tracker) const {}
@@ -3662,12 +3722,9 @@ void StatementSyncIterator::Next(const FunctionCallbackInfo<Value>& args) {
36623722 if (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 ;
36713728
36723729 DCHECK_EQ (row_keys.size (), row_values.size ());
36733730 row_value = Object::New (
0 commit comments