Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2f9c085

Browse filesBrowse files
araujoguiaduh95
authored andcommitted
sqlite: handle stmt invalidation
PR-URL: #61877 Reviewed-By: René <contact.9a5d6388@renegade334.me.uk>
1 parent a3751f2 commit 2f9c085
Copy full SHA for 2f9c085

3 files changed

+73-4Lines changed: 73 additions & 4 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/node_sqlite.cc‎

Copy file name to clipboardExpand all lines: src/node_sqlite.cc
+15-4Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2421,6 +2421,11 @@ inline bool StatementSync::IsFinalized() {
24212421
return statement_ == nullptr;
24222422
}
24232423

2424+
inline int StatementSync::ResetStatement() {
2425+
reset_generation_++;
2426+
return sqlite3_reset(statement_);
2427+
}
2428+
24242429
bool StatementSync::BindParams(const FunctionCallbackInfo<Value>& args) {
24252430
int r = sqlite3_clear_bindings(statement_);
24262431
CHECK_ERROR_OR_THROW(env()->isolate(), db_.get(), r, SQLITE_OK, false);
@@ -2812,7 +2817,7 @@ void StatementSync::All(const FunctionCallbackInfo<Value>& args) {
28122817
THROW_AND_RETURN_ON_BAD_STATE(
28132818
env, stmt->IsFinalized(), "statement has been finalized");
28142819
Isolate* isolate = env->isolate();
2815-
int r = sqlite3_reset(stmt->statement_);
2820+
int r = stmt->ResetStatement();
28162821
CHECK_ERROR_OR_THROW(isolate, stmt->db_.get(), r, SQLITE_OK, void());
28172822

28182823
if (!stmt->BindParams(args)) {
@@ -2838,7 +2843,7 @@ void StatementSync::Iterate(const FunctionCallbackInfo<Value>& args) {
28382843
Environment* env = Environment::GetCurrent(args);
28392844
THROW_AND_RETURN_ON_BAD_STATE(
28402845
env, stmt->IsFinalized(), "statement has been finalized");
2841-
int r = sqlite3_reset(stmt->statement_);
2846+
int r = stmt->ResetStatement();
28422847
CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK, void());
28432848

28442849
if (!stmt->BindParams(args)) {
@@ -2861,7 +2866,7 @@ void StatementSync::Get(const FunctionCallbackInfo<Value>& args) {
28612866
Environment* env = Environment::GetCurrent(args);
28622867
THROW_AND_RETURN_ON_BAD_STATE(
28632868
env, stmt->IsFinalized(), "statement has been finalized");
2864-
int r = sqlite3_reset(stmt->statement_);
2869+
int r = stmt->ResetStatement();
28652870
CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK, void());
28662871

28672872
if (!stmt->BindParams(args)) {
@@ -2885,7 +2890,7 @@ void StatementSync::Run(const FunctionCallbackInfo<Value>& args) {
28852890
Environment* env = Environment::GetCurrent(args);
28862891
THROW_AND_RETURN_ON_BAD_STATE(
28872892
env, stmt->IsFinalized(), "statement has been finalized");
2888-
int r = sqlite3_reset(stmt->statement_);
2893+
int r = stmt->ResetStatement();
28892894
CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK, void());
28902895

28912896
if (!stmt->BindParams(args)) {
@@ -3430,6 +3435,7 @@ StatementSyncIterator::StatementSyncIterator(Environment* env,
34303435
: BaseObject(env, object), stmt_(std::move(stmt)) {
34313436
MakeWeak();
34323437
done_ = false;
3438+
statement_reset_generation_ = stmt_->reset_generation_;
34333439
}
34343440

34353441
StatementSyncIterator::~StatementSyncIterator() {}
@@ -3488,6 +3494,11 @@ void StatementSyncIterator::Next(const FunctionCallbackInfo<Value>& args) {
34883494
return;
34893495
}
34903496

3497+
THROW_AND_RETURN_ON_BAD_STATE(
3498+
env,
3499+
iter->statement_reset_generation_ != iter->stmt_->reset_generation_,
3500+
"iterator was invalidated");
3501+
34913502
int r = sqlite3_step(iter->stmt_->statement_);
34923503
if (r != SQLITE_ROW) {
34933504
CHECK_ERROR_OR_THROW(
Collapse file

‎src/node_sqlite.h‎

Copy file name to clipboardExpand all lines: src/node_sqlite.h
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,9 @@ class StatementSync : public BaseObject {
291291
bool use_big_ints_;
292292
bool allow_bare_named_params_;
293293
bool allow_unknown_named_params_;
294+
uint64_t reset_generation_ = 0;
294295
std::optional<std::map<std::string, std::string>> bare_named_params_;
296+
inline int ResetStatement();
295297
bool BindParams(const v8::FunctionCallbackInfo<v8::Value>& args);
296298
bool BindValue(const v8::Local<v8::Value>& value, const int index);
297299

@@ -321,6 +323,7 @@ class StatementSyncIterator : public BaseObject {
321323
~StatementSyncIterator() override;
322324
BaseObjectPtr<StatementSync> stmt_;
323325
bool done_;
326+
uint64_t statement_reset_generation_;
324327
};
325328

326329
using Sqlite3ChangesetGenFunc = int (*)(sqlite3_session*, int*, void**);
Collapse file

‎test/parallel/test-sqlite-statement-sync.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-sqlite-statement-sync.js
+55Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,61 @@ suite('StatementSync.prototype.iterate()', () => {
171171
{ __proto__: null, done: true, value: null },
172172
);
173173
});
174+
175+
test('iterator is invalidated when statement is reset by get/all/run/iterate', (t) => {
176+
const db = new DatabaseSync(':memory:');
177+
db.exec('CREATE TABLE test (value INTEGER NOT NULL)');
178+
for (let i = 0; i < 5; i++) {
179+
db.prepare('INSERT INTO test (value) VALUES (?)').run(i);
180+
}
181+
const stmt = db.prepare('SELECT * FROM test');
182+
183+
// Invalidated by stmt.get()
184+
let it = stmt.iterate();
185+
it.next();
186+
stmt.get();
187+
t.assert.throws(() => { it.next(); }, {
188+
code: 'ERR_INVALID_STATE',
189+
message: /iterator was invalidated/,
190+
});
191+
192+
// Invalidated by stmt.all()
193+
it = stmt.iterate();
194+
it.next();
195+
stmt.all();
196+
t.assert.throws(() => { it.next(); }, {
197+
code: 'ERR_INVALID_STATE',
198+
message: /iterator was invalidated/,
199+
});
200+
201+
// Invalidated by stmt.run()
202+
it = stmt.iterate();
203+
it.next();
204+
stmt.run();
205+
t.assert.throws(() => { it.next(); }, {
206+
code: 'ERR_INVALID_STATE',
207+
message: /iterator was invalidated/,
208+
});
209+
210+
// Invalidated by a new stmt.iterate()
211+
it = stmt.iterate();
212+
it.next();
213+
const it2 = stmt.iterate();
214+
t.assert.throws(() => { it.next(); }, {
215+
code: 'ERR_INVALID_STATE',
216+
message: /iterator was invalidated/,
217+
});
218+
219+
// New iterator works fine
220+
t.assert.strictEqual(it2.next().done, false);
221+
222+
// Reset on a different statement does NOT invalidate this iterator
223+
const stmt2 = db.prepare('SELECT * FROM test');
224+
it = stmt.iterate();
225+
it.next();
226+
stmt2.get();
227+
it.next();
228+
});
174229
});
175230

176231
suite('StatementSync.prototype.run()', () => {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.