Skip to content

Commit 6adb625

Browse files
committed
sqlite: optimize column name creation and text value encoding
Use simdutf to detect ASCII text values and create them via NewFromOneByte for compact one byte representation. Internalize column name strings with kInternalized Cache column names on StatementSync for the iterate(), invalidated via SQLITE_STMTSTATUS_REPREPARE. Refs: nodejs/performance#181
1 parent da5efc4 commit 6adb625

File tree

2 files changed

+58
-9
lines changed

2 files changed

+58
-9
lines changed

src/node_sqlite.cc

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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"
@@ -55,6 +56,18 @@ using v8::TryCatch;
5556
using v8::Uint8Array;
5657
using v8::Value;
5758

59+
inline MaybeLocal<String> Utf8StringMaybeOneByte(Isolate* isolate,
60+
const char* data,
61+
size_t length) {
62+
int len = static_cast<int>(length);
63+
if (simdutf::validate_ascii(data, length)) {
64+
return String::NewFromOneByte(
65+
isolate, reinterpret_cast<const uint8_t*>(data), NewStringType::kNormal,
66+
len);
67+
}
68+
return String::NewFromUtf8(isolate, data, NewStringType::kNormal, len);
69+
}
70+
5871
#define CHECK_ERROR_OR_THROW(isolate, db, expr, expected, ret) \
5972
do { \
6073
int r_ = (expr); \
@@ -97,7 +110,8 @@ using v8::Value;
97110
case SQLITE_TEXT: { \
98111
const char* v = \
99112
reinterpret_cast<const char*>(sqlite3_##from##_text(__VA_ARGS__)); \
100-
(result) = String::NewFromUtf8((isolate), v).As<Value>(); \
113+
int v_len = sqlite3_##from##_bytes(__VA_ARGS__); \
114+
(result) = Utf8StringMaybeOneByte((isolate), v, v_len).As<Value>(); \
101115
break; \
102116
} \
103117
case SQLITE_NULL: { \
@@ -2147,6 +2161,7 @@ StatementSync::~StatementSync() {
21472161
void StatementSync::Finalize() {
21482162
sqlite3_finalize(statement_);
21492163
statement_ = nullptr;
2164+
cached_column_names_.clear();
21502165
}
21512166

21522167
inline bool StatementSync::IsFinalized() {
@@ -2325,7 +2340,40 @@ MaybeLocal<Name> StatementSync::ColumnNameToName(const int column) {
23252340
return MaybeLocal<Name>();
23262341
}
23272342

2328-
return String::NewFromUtf8(env()->isolate(), col_name).As<Name>();
2343+
return String::NewFromUtf8(
2344+
env()->isolate(), col_name, NewStringType::kInternalized)
2345+
.As<Name>();
2346+
}
2347+
2348+
bool StatementSync::GetCachedColumnNames(LocalVector<Name>* keys) {
2349+
Isolate* isolate = env()->isolate();
2350+
2351+
int reprepare_count = sqlite3_stmt_status(
2352+
statement_, SQLITE_STMTSTATUS_REPREPARE, 0);
2353+
if (reprepare_count != cached_column_names_reprepare_count_) {
2354+
cached_column_names_.clear();
2355+
int num_cols = sqlite3_column_count(statement_);
2356+
if (num_cols == 0) {
2357+
cached_column_names_reprepare_count_ = reprepare_count;
2358+
return true;
2359+
}
2360+
cached_column_names_.reserve(num_cols);
2361+
for (int i = 0; i < num_cols; ++i) {
2362+
Local<Name> key;
2363+
if (!ColumnNameToName(i).ToLocal(&key)) {
2364+
cached_column_names_.clear();
2365+
return false;
2366+
}
2367+
cached_column_names_.emplace_back(Global<Name>(isolate, key));
2368+
}
2369+
cached_column_names_reprepare_count_ = reprepare_count;
2370+
}
2371+
2372+
keys->reserve(cached_column_names_.size());
2373+
for (const auto& name : cached_column_names_) {
2374+
keys->emplace_back(name.Get(isolate));
2375+
}
2376+
return true;
23292377
}
23302378

23312379
MaybeLocal<Value> StatementExecutionHelper::ColumnToValue(Environment* env,
@@ -2347,7 +2395,9 @@ MaybeLocal<Name> StatementExecutionHelper::ColumnNameToName(Environment* env,
23472395
return MaybeLocal<Name>();
23482396
}
23492397

2350-
return String::NewFromUtf8(env->isolate(), col_name).As<Name>();
2398+
return String::NewFromUtf8(
2399+
env->isolate(), col_name, NewStringType::kInternalized)
2400+
.As<Name>();
23512401
}
23522402

23532403
void StatementSync::MemoryInfo(MemoryTracker* tracker) const {}
@@ -3251,12 +3301,7 @@ void StatementSyncIterator::Next(const FunctionCallbackInfo<Value>& args) {
32513301
if (iter->stmt_->return_arrays_) {
32523302
row_value = Array::New(isolate, row_values.data(), row_values.size());
32533303
} else {
3254-
row_keys.reserve(num_cols);
3255-
for (int i = 0; i < num_cols; ++i) {
3256-
Local<Name> key;
3257-
if (!iter->stmt_->ColumnNameToName(i).ToLocal(&key)) return;
3258-
row_keys.emplace_back(key);
3259-
}
3304+
if (!iter->stmt_->GetCachedColumnNames(&row_keys)) return;
32603305

32613306
DCHECK_EQ(row_keys.size(), row_values.size());
32623307
row_value = Object::New(

src/node_sqlite.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <list>
1313
#include <map>
1414
#include <unordered_set>
15+
#include <vector>
1516

1617
namespace node {
1718
namespace sqlite {
@@ -228,6 +229,7 @@ class StatementSync : public BaseObject {
228229
static void SetReturnArrays(const v8::FunctionCallbackInfo<v8::Value>& args);
229230
v8::MaybeLocal<v8::Value> ColumnToValue(const int column);
230231
v8::MaybeLocal<v8::Name> ColumnNameToName(const int column);
232+
bool GetCachedColumnNames(v8::LocalVector<v8::Name>* keys);
231233
void Finalize();
232234
bool IsFinalized();
233235

@@ -243,6 +245,8 @@ class StatementSync : public BaseObject {
243245
bool allow_bare_named_params_;
244246
bool allow_unknown_named_params_;
245247
std::optional<std::map<std::string, std::string>> bare_named_params_;
248+
std::vector<v8::Global<v8::Name>> cached_column_names_;
249+
int cached_column_names_reprepare_count_ = -1;
246250
bool BindParams(const v8::FunctionCallbackInfo<v8::Value>& args);
247251
bool BindValue(const v8::Local<v8::Value>& value, const int index);
248252

0 commit comments

Comments
 (0)