Journals Beyond the Basics in SQLite
Source: Dev.to
Introduction
Hello, I’m Maneshwar. I’m currently building FreeDevTools online – a free, open‑source hub that gathers all developer tools, cheat codes, and TL;DRs in one place. No more endless searching across the web!
Yesterday
We explored rollback journals – before‑images, segments, headers, and why SQLite can survive crashes without ever corrupting the database file.
Today
We complete the legacy journaling picture by covering two lesser‑known but crucial components:
- Statement journal – rolls back a single failing statement.
- Master journal – coordinates multi‑database transactions.
Together they show how SQLite guarantees correctness not only across crashes but also across statement failures and multi‑database commits. This wraps up the storage + journaling chapter.
Tomorrow
We’ll move up a layer into transaction management and locking.
Statement Journal – Rolling Back a Single Statement
A statement journal exists for one very specific reason:
To undo the partial effects of a single SQL statement that fails midway.
This occurs when an INSERT, UPDATE, or DELETE touches multiple rows and a constraint violation or trigger exception aborts the statement. The surrounding user transaction must stay active, while the offending statement is rolled back. That’s where the statement journal comes in.
What the Statement Journal Is (and Is Not)
| ✅ What it is | ❌ What it is not |
|---|---|
| A separate rollback‑journal file | Not used for crash recovery |
Stored as a temporary file (random name like etilqs_*) | No segment headers |
Lives in the system temp directory (e.g., /tmp) | No checksums |
| Exists only during statement execution | Does not persist across crashes |
| Deleted immediately after the statement finishes | All metadata (e.g., nRec, DB size at start) is kept in memory, not on disk |
Statement Journal Lifecycle
The key idea: SQLite can roll back a single statement without aborting the surrounding user transaction.

Interaction with SAVEPOINT
SAVEPOINTs add a layer of nesting:
- To support nested savepoints, SQLite retains statement journals until the corresponding savepoint is released or the outer transaction commits.
- This design gives fine‑grained rollback semantics while keeping the API simple for users.
Multi‑Database Transactions and the Problem of Atomicity
SQLite can attach multiple databases to a single connection:
ATTACH 'db2.sqlite' AS db2;
A single transaction may now modify main, db2, or any other attached database. Each attached database has its own rollback journal and commits independently. Without coordination, this would break atomicity—the “all‑or‑nothing” guarantee of a transaction.
Master Journal – Coordinating Atomic Commits
To preserve atomicity across databases, SQLite creates a master journal that records the names of all individual rollback journals involved in the transaction. The commit process works like this:
- Write the master journal (listing all child journals).
- Flush the master journal to disk.
- Commit each attached database’s individual journal.
- Delete the master journal once all commits succeed.
If any step fails, SQLite can roll back all databases using the information stored in the master journal, ensuring the transaction is truly atomic.
Result: Even with many attached databases, a transaction either fully succeeds or fully rolls back, preserving data integrity.
Visual Summary

TL;DR
| Feature | Purpose | Persistence |
|---|---|---|
| Statement journal | Roll back a single failing statement without aborting the surrounding transaction | Temporary, in‑memory metadata; deleted after statement |
| Master journal | Ensure atomic commits across multiple attached databases | Persistent until the whole transaction either commits or rolls back |
Working with Multiple Databases, Transactions, and SQLite Internals
Athreya (aka Maneshwar) • Jan 6
Tags: #webdev #programming #database #architecture
The Master Journal: Coordinating Child Journals
To make a multi‑DB transaction globally atomic, SQLite introduces the master journal.
Important Properties
- Created only when committing – deleted once the commit finishes.
- Never created for transaction aborts.
- Contains no page images – it stores only the names of child rollback journals.
Each rollback journal that participates in the transaction becomes a child journal.
Master Journal File
- Lives in the same directory as the main database.
- Name format:
-mj(random suffix avoids name collisions).
What the Master Journal Contains
The master journal stores full UTF‑8 paths of all child rollback journals, separated by a NUL (\0) character. No page data, just coordination metadata.
How Child Journals Reference the Master Journal
At commit time each child rollback journal appends a master‑journal record written at a sector boundary. The record contains:
- Length of the master‑journal name.
- Checksum of the name.
- The name itself (UTF‑8).
- A forbidden page number (the lock‑byte page).
This two‑way linkage guarantees correctness during recovery.

Why the Master Journal Exists
If SQLite crashes during a commit, recovery proceeds as follows:
- Each child journal is examined.
- Child journals point to the master journal, which tells which journals belong together.
- Only when all child journals are safely committed is the transaction considered complete.
Thus the master journal preserves global atomicity across databases.
Forbidden Page Number (Lock‑Byte Page)
Each master‑journal record includes a forbidden page number that refers to the lock‑byte page. This page:
- Is reserved and never written.
- Exists to handle differences between Windows and POSIX file‑locking semantics.
- SQLite deliberately avoids touching it.
Legacy Journaling Architecture (Big Picture)
| Journal Type | Purpose |
|---|---|
| Rollback | Crash recovery |
| Statement | Single‑statement rollback |
| Master | Multi‑DB atomicity |

Summary
- SQLite stores data and metadata in a single file composed of fixed‑size pages.
- Page 1 anchors the database and holds global metadata.
- Free pages are tracked via a freelist.
- Legacy journaling uses three journal types: rollback, statement, and master.
- Rollback journals guarantee crash safety.
- Statement journals guarantee statement‑level correctness.
- Master journals guarantee global atomicity across databases.
The storage and journaling story ends here.
Next up: runtime behavior – transaction types, locking modes, and the distinction between shared, reserved, and exclusive locks. We’ll move from “bytes on disk” to “concurrency in memory”.
My SQLite experiments and hands‑on examples live here:
lovestaco/sqlite – sqlite‑examples
References
- SQLite Database System: Design and Implementation – Sibsankar Haldar.
- FreeDevTools – a collection of useful development utilities.
👉 Check out FreeDevTools:
Any feedback or contributions are welcome!
⭐ Star the project on GitHub.