Does Expo automatically roll back SQLite schema changes on migration failure?
0 reputation · 04 Aug 2025, 06:05 UTC
0 reputation · 04 Aug 2025, 06:05 UTC
Goal: Verify whether the Expo SDK automatically rolls back SQLite schema alterations when a migration script encounters an error.
Constraint/uncertainty: The expo-sqlite module exposes the underlying SQLite database but does not include a migration helper; schema changes must be executed via raw SQL inside a transaction that the developer must manage manually, and the documentation does not specify a guaranteed rollback behavior for partial updates.
Does Expo's expo-sqlite provide a built‑in savepoint or automatic transaction wrapper that reverts schema changes on error?
If such a mechanism is absent, what pattern should developers follow to implement safe rollback logic for schema migrations?
34775 reputation · 04 Aug 2025, 15:27 UTC
No. Expo's expo-sqlite does not automatically roll back schema changes when a migration fails. If you execute each DDL statement individually with execAsync or runAsync, SQLite runs them in autocommit mode and persists them immediately. A later failure does not undo earlier CREATE TABLE, ALTER TABLE, or CREATE INDEX calls.
Confirmed behavior: The module provides withTransactionAsync (and withTransactionSync). These wrappers begin a transaction, commit when the callback resolves, and roll back when the callback throws. Because SQLite treats DDL as transactional, schema changes made inside the wrapper are reverted on error.
Likely explanation: Expo does not insert an implicit transaction around every database call. The absence of automatic rollback for individual statements is a consequence of exposing SQLite's default autocommit behavior rather than a deliberate migration framework.
PRAGMA user_version.withTransactionAsync and pass a single callback that runs every migration step in order.execAsync or runAsync. Do not call those methods outside the wrapper.PRAGMA user_version = <new_version>.await db.withTransactionAsync(async () => {
await db.execAsync(`CREATE TABLE ...`);
await db.execAsync(`ALTER TABLE ...`);
await db.execAsync(`PRAGMA user_version = 2`);
});
execAsync issued outside withTransactionAsync commits independently and cannot be rolled back by the surrounding transaction.withTransactionAsync blocks may use savepoints or may be rejected. Verify the semantics for your installed version before relying on nesting.VACUUM are not transactional and will not be undone by a rollback.To confirm the behavior in your project, create a table, then throw an error inside withTransactionAsync, and query sqlite_master to verify the table is absent. Repeat the same test without the wrapper to observe that the table persists.
Note: Because exact API availability and nesting semantics can change between Expo SDK releases, treat the above as a conservative pattern and check the expo-sqlite documentation for your installed version before shipping.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.