Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 23 additions & 43 deletions src/node_sqlite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2836,18 +2836,24 @@ Maybe<void> ExtractRowValues(Environment* env,
}

MaybeLocal<Value> StatementExecutionHelper::All(Environment* env,
DatabaseSync* db,
sqlite3_stmt* stmt,
StatementSync* sync_stmt,
bool return_arrays,
bool use_big_ints) {
Isolate* isolate = env->isolate();
EscapableHandleScope scope(isolate);
int r;
sqlite3_stmt* stmt = sync_stmt->statement_;
int num_cols = sqlite3_column_count(stmt);
LocalVector<Value> rows(isolate);
LocalVector<Value> row_values(isolate);
LocalVector<Name> row_keys(isolate);

if (!return_arrays && num_cols > 0) {
if (!sync_stmt->GetCachedColumnNames(&row_keys)) {
return MaybeLocal<Value>();
}
}

Comment on lines +2845 to +2856
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In StatementExecutionHelper::All(), num_cols and the cached row_keys are computed before the first sqlite3_step(). SQLite can auto-reprepare a statement during sqlite3_step() (see SQLITE_STMTSTATUS_REPREPARE), which can change the column count/names; in that case ExtractRowValues() and Object::New(..., num_cols) may use stale num_cols/keys and can lead to a key/value length mismatch (potentially OOB reads in release builds). Consider initializing num_cols/column names only after the first SQLITE_ROW, and/or tracking the reprepare counter during the loop so you can refresh num_cols and rebuild row_keys if the statement was reprepared mid-iteration.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

@araujogui araujogui Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm that makes sense but I feel it wasn't right before neither. Also this doesn't seem possible in a sync api

CC @nodejs/sqlite

while ((r = sqlite3_step(stmt)) == SQLITE_ROW) {
if (ExtractRowValues(env, stmt, num_cols, use_big_ints, &row_values)
.IsNothing()) {
Expand All @@ -2859,24 +2865,15 @@ MaybeLocal<Value> StatementExecutionHelper::All(Environment* env,
Array::New(isolate, row_values.data(), row_values.size());
rows.emplace_back(row_array);
} else {
if (row_keys.size() == 0) {
row_keys.reserve(num_cols);
for (int i = 0; i < num_cols; ++i) {
Local<Name> key;
if (!ColumnNameToName(env, stmt, i).ToLocal(&key)) {
return MaybeLocal<Value>();
}
row_keys.emplace_back(key);
}
}
DCHECK_EQ(row_keys.size(), row_values.size());
Local<Object> row_obj = Object::New(
isolate, Null(isolate), row_keys.data(), row_values.data(), num_cols);
rows.emplace_back(row_obj);
}
}

CHECK_ERROR_OR_THROW(isolate, db, r, SQLITE_DONE, MaybeLocal<Value>());
CHECK_ERROR_OR_THROW(
isolate, sync_stmt->db_.get(), r, SQLITE_DONE, MaybeLocal<Value>());
return scope.Escape(Array::New(isolate, rows.data(), rows.size()));
}

Expand Down Expand Up @@ -2956,18 +2953,18 @@ BaseObjectPtr<StatementSyncIterator> StatementExecutionHelper::Iterate(
}

MaybeLocal<Value> StatementExecutionHelper::Get(Environment* env,
DatabaseSync* db,
sqlite3_stmt* stmt,
StatementSync* sync_stmt,
bool return_arrays,
bool use_big_ints) {
Isolate* isolate = env->isolate();
EscapableHandleScope scope(isolate);
sqlite3_stmt* stmt = sync_stmt->statement_;
auto reset = OnScopeLeave([&]() { sqlite3_reset(stmt); });

int r = sqlite3_step(stmt);
if (r == SQLITE_DONE) return scope.Escape(Undefined(isolate));
if (r != SQLITE_ROW) {
THROW_ERR_SQLITE_ERROR(isolate, db);
THROW_ERR_SQLITE_ERROR(isolate, sync_stmt->db_.get());
return MaybeLocal<Value>();
}

Expand All @@ -2987,13 +2984,8 @@ MaybeLocal<Value> StatementExecutionHelper::Get(Environment* env,
Array::New(isolate, row_values.data(), row_values.size()));
} else {
LocalVector<Name> keys(isolate);
keys.reserve(num_cols);
for (int i = 0; i < num_cols; ++i) {
Local<Name> key;
if (!ColumnNameToName(env, stmt, i).ToLocal(&key)) {
return MaybeLocal<Value>();
}
keys.emplace_back(key);
if (!sync_stmt->GetCachedColumnNames(&keys)) {
return MaybeLocal<Value>();
}

DCHECK_EQ(keys.size(), row_values.size());
Expand All @@ -3019,11 +3011,8 @@ void StatementSync::All(const FunctionCallbackInfo<Value>& args) {
auto reset = OnScopeLeave([&]() { sqlite3_reset(stmt->statement_); });

Local<Value> result;
if (StatementExecutionHelper::All(env,
stmt->db_.get(),
stmt->statement_,
stmt->return_arrays_,
stmt->use_big_ints_)
if (StatementExecutionHelper::All(
env, stmt, stmt->return_arrays_, stmt->use_big_ints_)
.ToLocal(&result)) {
args.GetReturnValue().Set(result);
}
Expand Down Expand Up @@ -3066,11 +3055,8 @@ void StatementSync::Get(const FunctionCallbackInfo<Value>& args) {
}

Local<Value> result;
if (StatementExecutionHelper::Get(env,
stmt->db_.get(),
stmt->statement_,
stmt->return_arrays_,
stmt->use_big_ints_)
if (StatementExecutionHelper::Get(
env, stmt, stmt->return_arrays_, stmt->use_big_ints_)
.ToLocal(&result)) {
args.GetReturnValue().Set(result);
}
Expand Down Expand Up @@ -3425,11 +3411,8 @@ void SQLTagStore::Get(const FunctionCallbackInfo<Value>& args) {
}

Local<Value> result;
if (StatementExecutionHelper::Get(env,
stmt->db_.get(),
stmt->statement_,
stmt->return_arrays_,
stmt->use_big_ints_)
if (StatementExecutionHelper::Get(
env, stmt.get(), stmt->return_arrays_, stmt->use_big_ints_)
.ToLocal(&result)) {
args.GetReturnValue().Set(result);
}
Expand Down Expand Up @@ -3465,11 +3448,8 @@ void SQLTagStore::All(const FunctionCallbackInfo<Value>& args) {

auto reset = OnScopeLeave([&]() { sqlite3_reset(stmt->statement_); });
Local<Value> result;
if (StatementExecutionHelper::All(env,
stmt->db_.get(),
stmt->statement_,
stmt->return_arrays_,
stmt->use_big_ints_)
if (StatementExecutionHelper::All(
env, stmt.get(), stmt->return_arrays_, stmt->use_big_ints_)
.ToLocal(&result)) {
args.GetReturnValue().Set(result);
}
Expand Down
6 changes: 2 additions & 4 deletions src/node_sqlite.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ class BackupJob;
class StatementExecutionHelper {
public:
static v8::MaybeLocal<v8::Value> All(Environment* env,
DatabaseSync* db,
sqlite3_stmt* stmt,
StatementSync* stmt,
Copy link
Copy Markdown
Contributor

@thisalihassan thisalihassan Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: inconsistency in variable naming we are using sync_stmt for StatementSync

bool return_arrays,
bool use_big_ints);
static v8::MaybeLocal<v8::Object> Run(Environment* env,
Expand All @@ -155,8 +154,7 @@ class StatementExecutionHelper {
sqlite3_stmt* stmt,
const int column);
static v8::MaybeLocal<v8::Value> Get(Environment* env,
DatabaseSync* db,
sqlite3_stmt* stmt,
StatementSync* stmt,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: same

bool return_arrays,
bool use_big_ints);
};
Expand Down
Loading