Bun sqlite3 transaction rollback does not revert schema changes after implicit commit
0 reputation · 22 Jan 2024, 07:21 UTC
0 reputation · 22 Jan 2024, 07:21 UTC
When using Bun's built‑in sqlite module, developers often wrap schema migrations in an explicit transaction expecting that a ROLLBACK will undo any CREATE, ALTER, or DROP statements if something goes wrong.
However, SQLite issues an implicit commit for DDL statements, which ends the open transaction before the ROLLBACK is processed, leaving the schema change persisted. The Bun documentation does not currently highlight this behavior, creating uncertainty about whether transaction‑based rollback is safe for schema modifications.
Should Bun emit a runtime warning when DDL is executed inside an explicit transaction? Would a helper API that detects unsafe DDL be beneficial? Could the sqlite module automatically isolate DDL using savepoints to allow rollback?
31275 reputation · 22 Jan 2024, 13:17 UTC
In SQLite, CREATE, ALTER, and DROP are fully transactional. A ROLLBACK inside an open transaction will undo those schema changes. If you see the schema persisting after a rollback in Bun, the transaction was not actually active when the DDL ran, or it ran on a different connection. The Bun module does not need a blanket warning – the issue is usually in how the transaction is started or scoped.
db.transaction() helper creates a new connection for the callback. If the DDL is executed outside that callback, it runs in autocommit mode.db.exec() call can open its own connection if not passed the same Connection object. Schema changes are visible immediately on the main database file.VACUUM, ATTACH) force an implicit commit. Standard DDL does not.Standard SQLite DDL is transactional. A quick test in plain SQLite:
sqlite3 test.db
BEGIN;
CREATE TABLE t(id INTEGER PRIMARY KEY);
ROLLBACK;
SELECT name FROM sqlite_master WHERE type='table';
-- No rows returned – the table was rolled back.
Thus, the failure to revert schema changes is not a SQLite bug but a usage issue.
await db.transaction(async (trx) => {
// All statements here share the same connection
await trx.exec('CREATE TABLE foo(id INTEGER)');
// ...
});db.exec() after the transaction callback returns.await trx.exec('INSERT INTO foo(id) VALUES(1)');sqlite3 directly to rule out Bun‑specific behavior.VACUUM) that might be executed inadvertently.Because SQLite already handles transactional DDL, a blanket runtime warning would be misleading. A better approach is:
db.transaction() when executed on the same connection.SAVEPOINT, it would be unnecessary for standard DDL but could help with nested transaction semantics.Do you use Bun’s db.transaction() helper, or are you manually issuing BEGIN / COMMIT statements? The answer determines whether the transaction is truly active for your DDL.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.