SQLite

View Ticket
Login
Ticket Hash: df678d738adb24e9d7f5beff3a89928161c072fc
Title: Cache corruption following IO error on systems supporting atomic-write
Status: Closed Type: Code_Defect
Severity: Critical Priority: Immediate
Subsystem: Pager Resolution: Fixed
Last Modified: 2012-12-18 14:04:30
Version Found In:
Description:
If SQLite is compiled with the SQLITE_ENABLE_ATOMIC_WRITE symbol defined and a custom VFS indicates that it is supported, SQLite writes transactions that modify a single database page on disk without creating a journal file. This is safe, since the system guarantees that single write required is atomic.

For such systems, SQLite creates an "in-memory journal" when a write transaction is opened. After the second database page is modified within the cache, a real journal file is created on disk and the contents of the in-memory journal file written to it. Processing then continues as per normal. Or, if the transaction never modifies a second page, the journal file is never created on disk.

If the user opens a write transaction and then issues a ROLLBACK command after modifying a single page, the contents of the in-memory journal are used to restore the page-cache to its original state.

However, due to a bug, if an IO or disk full error occurs while transferring the contents of the in-memory journal to disk, SQLite is not using the contents of the in-memory journal to restore the in-memory cache. Instead, it is attempting to use the contents of the file on disk (which was not written correctly - if at all). This means the single page that was modified in the cache is not being rolled back - cache corruption.

For example, the INSERT statement in the SQL below writes to two database pages - one in the table b-tree and the other in the index. Internally, SQLite first modifies the table and stores the old page image in the in-memory journal. Before modifying the index, it attempts to write the contents of the in-memory journal out to disk. If it encounters a disk-full error while doing so, the subsequent rollback does not restore the contents of the table (due to the bug), leaving the connection with a corrupt cache.

  CREATE TABLE t1(a, b);
  CREATE INDEX i1 ON t1(a, b);
  INSERT INTO t1 VALUES(1, 2);