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
22 changes: 18 additions & 4 deletions src/node_sqlite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,12 @@ void DatabaseSync::UntrackStatement(StatementSync* statement) {
}
}

int DatabaseSync::Step(sqlite3_stmt* statement) {
statement_execution_depth_++;
auto leave = OnScopeLeave([&]() { statement_execution_depth_--; });
return sqlite3_step(statement);
}

inline bool DatabaseSync::IsOpen() {
return connection_ != nullptr;
}
Expand Down Expand Up @@ -1434,6 +1440,10 @@ void DatabaseSync::Close(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
Environment* env = Environment::GetCurrent(args);
THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open");
THROW_AND_RETURN_ON_BAD_STATE(
env,
db->statement_execution_depth_ != 0,
"cannot close database while a statement is executing");
db->FinalizeStatements();
db->DeleteSessions();
int r = sqlite3_close_v2(db->connection_);
Expand Down Expand Up @@ -1829,6 +1839,10 @@ void DatabaseSync::Deserialize(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
Environment* env = Environment::GetCurrent(args);
THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open");
THROW_AND_RETURN_ON_BAD_STATE(
env,
db->statement_execution_depth_ != 0,
"cannot deserialize while a statement is executing");

if (!args[0]->IsUint8Array()) {
THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
Expand Down Expand Up @@ -2884,7 +2898,7 @@ MaybeLocal<Value> StatementExecutionHelper::All(Environment* env,
LocalVector<Value> row_values(isolate);
LocalVector<Name> row_keys(isolate);

while ((r = sqlite3_step(stmt)) == SQLITE_ROW) {
while ((r = db->Step(stmt)) == SQLITE_ROW) {
if (num_cols == 0) {
num_cols = sqlite3_column_count(stmt);
}
Expand Down Expand Up @@ -2926,7 +2940,7 @@ MaybeLocal<Object> StatementExecutionHelper::Run(Environment* env,
bool use_big_ints) {
Isolate* isolate = env->isolate();
EscapableHandleScope scope(isolate);
sqlite3_step(stmt);
db->Step(stmt);
int r = sqlite3_reset(stmt);
CHECK_ERROR_OR_THROW(isolate, db, r, SQLITE_OK, MaybeLocal<Object>());

Expand Down Expand Up @@ -3004,7 +3018,7 @@ MaybeLocal<Value> StatementExecutionHelper::Get(Environment* env,
EscapableHandleScope scope(isolate);
auto reset = OnScopeLeave([&]() { sqlite3_reset(stmt); });

int r = sqlite3_step(stmt);
int r = db->Step(stmt);
if (r == SQLITE_DONE) return scope.Escape(Undefined(isolate));
if (r != SQLITE_ROW) {
THROW_ERR_SQLITE_ERROR(isolate, db);
Expand Down Expand Up @@ -3731,7 +3745,7 @@ void StatementSyncIterator::Next(const FunctionCallbackInfo<Value>& args) {
iter->statement_reset_generation_ != iter->stmt_->reset_generation_,
"iterator was invalidated");

int r = sqlite3_step(iter->stmt_->statement_);
int r = iter->stmt_->db_->Step(iter->stmt_->statement_);
if (r != SQLITE_ROW) {
CHECK_ERROR_OR_THROW(
env->isolate(), iter->stmt_->db_.get(), r, SQLITE_DONE, void());
Expand Down
3 changes: 3 additions & 0 deletions src/node_sqlite.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,13 +234,15 @@ class DatabaseSync : public BaseObject {
private:
bool Open();
void DeleteSessions();
int Step(sqlite3_stmt* statement);

~DatabaseSync() override;
DatabaseOpenConfiguration open_config_;
bool allow_load_extension_;
bool enable_load_extension_;
sqlite3* connection_;
bool ignore_next_sqlite_error_;
uint32_t statement_execution_depth_ = 0;

std::set<BackupJob*> backups_;
std::set<sqlite3_session*> sessions_;
Expand All @@ -250,6 +252,7 @@ class DatabaseSync : public BaseObject {
friend class Session;
friend class SQLTagStore;
friend class StatementExecutionHelper;
friend class StatementSyncIterator;
};

class StatementSync : public BaseObject {
Expand Down
13 changes: 13 additions & 0 deletions test/parallel/test-sqlite-custom-functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ const { DatabaseSync } = require('node:sqlite');
const { suite, test } = require('node:test');

suite('DatabaseSync.prototype.function()', () => {
test('cannot close the database while a statement is executing', (t) => {
const db = new DatabaseSync(':memory:');
t.after(() => db.close());
db.function('close_database', () => db.close());
const stmt = db.prepare('SELECT close_database()');

t.assert.throws(() => stmt.get(), {
code: 'ERR_INVALID_STATE',
message: 'cannot close database while a statement is executing',
});
t.assert.strictEqual(db.isOpen, true);
});

suite('input validation', () => {
const db = new DatabaseSync(':memory:');

Expand Down
17 changes: 17 additions & 0 deletions test/parallel/test-sqlite-serialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,23 @@ suite('DatabaseSync.prototype.serialize()', () => {
});

suite('DatabaseSync.prototype.deserialize()', () => {
test('cannot deserialize while a statement is executing', (t) => {
const source = new DatabaseSync(':memory:');
const serialized = source.serialize();
source.close();

const db = new DatabaseSync(':memory:');
t.after(() => db.close());
db.function('deserialize_database', () => db.deserialize(serialized));
const stmt = db.prepare('SELECT deserialize_database()');

t.assert.throws(() => stmt.get(), {
code: 'ERR_INVALID_STATE',
message: 'cannot deserialize while a statement is executing',
});
t.assert.strictEqual(db.isOpen, true);
});

test('loads a serialized database', (t) => {
const db1 = new DatabaseSync(':memory:');
db1.exec('CREATE TABLE t(id INTEGER PRIMARY KEY, name TEXT)');
Expand Down
Loading