Skip to content
Closed
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
21 changes: 21 additions & 0 deletions integration-tests/tests/async.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,27 @@ test.serial("Database.batch() rejects non-array argument", async (t) => {
await t.throwsAsync(() => db.batch("SELECT 1"), { instanceOf: TypeError });
});

test.serial("A statement that failed with SQLITE_BUSY does not block the next COMMIT", async (t) => {
const path = genDatabaseFilename();
const [holder] = await connect(path);
await holder.exec("PRAGMA journal_mode=WAL");
await holder.exec("CREATE TABLE t(x)");
const [db] = await connect(path, { timeout: 50 });
await holder.exec("BEGIN IMMEDIATE");
// Keep the failed statement referenced so that garbage collection cannot finalize it.
const begin = await db.prepare("BEGIN IMMEDIATE");
await t.throwsAsync(() => begin.run(), { code: "SQLITE_BUSY" });
await holder.exec("ROLLBACK");
await (await db.prepare("BEGIN IMMEDIATE")).run();
await (await db.prepare("INSERT INTO t VALUES (1)")).run();
await (await db.prepare("COMMIT")).run();
const row = await (await db.prepare("SELECT count(*) AS n FROM t")).get();
t.is(row.n, 1);
db.close();
holder.close();
for (const suffix of ["", "-wal", "-shm"]) fs.rmSync(path + suffix, { force: true });
});

const connect = async (path_opt, options = {}) => {
const path = path_opt ?? "hello.db";
const provider = process.env.PROVIDER;
Expand Down
20 changes: 20 additions & 0 deletions integration-tests/tests/sync.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,26 @@ test.serial("Database.batch() rejects non-array argument", async (t) => {
t.throws(() => db.batch("SELECT 1"), { instanceOf: TypeError });
});

test.serial("A statement that failed with SQLITE_BUSY does not block the next COMMIT", async (t) => {
const path = genDatabaseFilename();
const [holder] = await connect(path);
holder.exec("PRAGMA journal_mode=WAL");
holder.exec("CREATE TABLE t(x)");
const [db, errorType] = await connect(path, { timeout: 50 });
holder.exec("BEGIN IMMEDIATE");
// Keep the failed statement referenced so that garbage collection cannot finalize it.
const begin = db.prepare("BEGIN IMMEDIATE");
t.throws(() => begin.run(), { instanceOf: errorType, code: "SQLITE_BUSY" });
holder.exec("ROLLBACK");
db.prepare("BEGIN IMMEDIATE").run();
db.prepare("INSERT INTO t VALUES (1)").run();
db.prepare("COMMIT").run();
t.is(db.prepare("SELECT count(*) AS n FROM t").get().n, 1);
db.close();
holder.close();
for (const suffix of ["", "-wal", "-shm"]) fs.rmSync(path + suffix, { force: true });
});

const connect = async (path_opt, options = {}) => {
const path = path_opt ?? "hello.db";
const provider = process.env.PROVIDER;
Expand Down
12 changes: 10 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,11 @@ impl Statement {

let future = async move {
let _timeout_guard = register_timeout(&stmt, query_timeout);
stmt.run(params).await.map_err(Error::from)?;
let result = stmt.run(params).await;
if result.is_err() {
stmt.reset();
}
result.map_err(Error::from)?;
let changes = if conn.total_changes() == total_changes_before {
0
} else {
Expand Down Expand Up @@ -1386,7 +1390,11 @@ pub fn statement_run_sync(
let total_changes_before = conn.total_changes();
let start = std::time::Instant::now();

inner_stmt.run(params).await.map_err(Error::from)?;
let result = inner_stmt.run(params).await;
if result.is_err() {
inner_stmt.reset();
}
result.map_err(Error::from)?;
let changes = if conn.total_changes() == total_changes_before {
0
} else {
Expand Down
Loading