Skip to content
Merged
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
6 changes: 5 additions & 1 deletion compat.js
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,11 @@ class Statement {
function wrappedIter(it) {
return {
next() {
return iteratorNextSync(it);
try {
return iteratorNextSync(it);
} catch (err) {
throw convertError(err);
}
},
return(value) {
if (typeof it.close === "function") {
Expand Down
27 changes: 27 additions & 0 deletions integration-tests/tests/async.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,33 @@ test.serial("Statement.interrupt()", async (t) => {
});
});

test.serial("Database.close() releases open statements and iterators", async (t) => {
const path = genDatabaseFilename();
const [conn1] = await connect(path);
await conn1.exec("CREATE TABLE t(x)");
await conn1.exec("INSERT INTO t VALUES (1), (2)");
const stmt = await conn1.prepare("SELECT x FROM t");
const iterator = await stmt.iterate();
// A partially consumed iterator keeps a read transaction open.
t.is((await iterator.next()).value.x, 1);
conn1.close();

// A live reader would hold a SHARED lock and make this write fail with SQLITE_BUSY.
const [conn2] = await connect(path);
await t.notThrowsAsync(() => conn2.exec("INSERT INTO t VALUES (3)"));
await t.throwsAsync(() => stmt.all(), {
instanceOf: TypeError,
message: "The database connection is not open",
});
await t.throwsAsync(() => iterator.next(), {
instanceOf: TypeError,
message: "The database connection is not open",
});
conn2.close();
// Fails with EBUSY on Windows if the database file is still open.
fs.unlinkSync(path);
});

test.serial("Timeout option", async (t) => {
const timeout = 1000;
const path = genDatabaseFilename();
Expand Down
42 changes: 42 additions & 0 deletions integration-tests/tests/sync.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,48 @@ test.serial("Database.exec() after close()", async (t) => {
});
});

test.serial("Database.close() releases open statements", async (t) => {
const path = genDatabaseFilename();
const [db] = await connect(path);
db.exec("CREATE TABLE t(x)");
const stmt = db.prepare("SELECT x FROM t");
db.close();

t.throws(() => stmt.all(), {
instanceOf: TypeError,
message: "The database connection is not open",
});
// Fails with EBUSY on Windows if the database file is still open.
fs.unlinkSync(path);
});

test.serial("Database.close() releases active iterators", async (t) => {
// better-sqlite3 refuses to close a database with an active iterator.
if (t.context.provider !== "libsql") {
t.pass();
return;
}
const path = genDatabaseFilename();
const [conn1] = await connect(path);
conn1.exec("CREATE TABLE t(x)");
conn1.exec("INSERT INTO t VALUES (1), (2)");
const iterator = conn1.prepare("SELECT x FROM t").iterate();
// A partially consumed iterator keeps a read transaction open.
t.is(iterator.next().value.x, 1);
conn1.close();

// A live reader would hold a SHARED lock and make this write fail with SQLITE_BUSY.
const [conn2] = await connect(path);
t.notThrows(() => conn2.exec("INSERT INTO t VALUES (3)"));
t.throws(() => iterator.next(), {
instanceOf: TypeError,
message: "The database connection is not open",
});
conn2.close();
// Fails with EBUSY on Windows if the database file is still open.
fs.unlinkSync(path);
});

test.serial("Timeout option", async (t) => {
const timeout = 1000;
const path = genDatabaseFilename();
Expand Down
4 changes: 3 additions & 1 deletion promise.js
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,9 @@ class Statement {
function wrappedIter(it) {
return {
next() {
return it.next();
return it.next().catch((err) => {
throw convertError(err);
});
},
return(value) {
if (typeof it.close === "function") {
Expand Down
Loading
Loading