From e66ef32a8f55b0ac8388542c447e383cfb611f23 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Tue, 25 Feb 2025 19:27:55 +0530 Subject: [PATCH] fix(ext/node): SQLite reset guards to prevent database locks (#28298) Fixes https://github.com/denoland/deno/issues/28295 --- ext/node/ops/sqlite/statement.rs | 19 +++++++++++++++---- tests/unit_node/sqlite_test.ts | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/ext/node/ops/sqlite/statement.rs b/ext/node/ops/sqlite/statement.rs index ebe759a7dc..46198dbafd 100644 --- a/ext/node/ops/sqlite/statement.rs +++ b/ext/node/ops/sqlite/statement.rs @@ -391,6 +391,14 @@ impl StatementSync { } } +struct ResetGuard<'a>(&'a StatementSync); + +impl<'a> Drop for ResetGuard<'a> { + fn drop(&mut self) { + let _ = self.0.reset(); + } +} + // Represents a single prepared statement. Cannot be initialized directly via constructor. // Instances are created using `DatabaseSync#prepare`. // @@ -416,6 +424,8 @@ impl StatementSync { self.bind_params(scope, params)?; + let _reset = ResetGuard(self); + let entry = self.read_row(scope)?; let result = entry .map(|r| r.into()) @@ -438,9 +448,10 @@ impl StatementSync { let db = db.as_ref().ok_or(SqliteError::InUse)?; self.bind_params(scope, params)?; - self.step()?; - self.reset()?; + let _reset = ResetGuard(self); + + self.step()?; Ok(RunStatementResult { last_insert_rowid: db.last_insert_rowid(), @@ -460,12 +471,12 @@ impl StatementSync { let mut arr = vec![]; self.bind_params(scope, params)?; + + let _reset = ResetGuard(self); while let Some(result) = self.read_row(scope)? { arr.push(result.into()); } - self.reset()?; - let arr = v8::Array::new_with_elements(scope, &arr); Ok(arr) } diff --git a/tests/unit_node/sqlite_test.ts b/tests/unit_node/sqlite_test.ts index 0ece60a080..83996301bb 100644 --- a/tests/unit_node/sqlite_test.ts +++ b/tests/unit_node/sqlite_test.ts @@ -272,3 +272,17 @@ Deno.test("[node/sqlite] error message", () => { "NOT NULL constraint failed: foo.b", ); }); + +// https://github.com/denoland/deno/issues/28295 +Deno.test("[node/sqlite] StatementSync reset guards don't lock db", () => { + const db = new DatabaseSync(":memory:"); + + db.exec("CREATE TABLE foo(a integer, b text)"); + db.exec("CREATE TABLE bar(a integer, b text)"); + + const stmt = db.prepare("SELECT name FROM sqlite_master WHERE type='table' "); + + assertEquals(stmt.get(), { name: "foo", __proto__: null }); + + db.exec("DROP TABLE IF EXISTS foo"); +});