CodeVetter · Repo Unpack · Behavior traces
sqlite/sqlite
Three flows carry most of the system's weight: library startup and connection open; the prepare/step/finalize query cycle including automatic recompilation on schema change; and the CLI's REPL loop from argv parsing through dot-command dispatch to statement execution. A fourth, the durability path, is worth tracing separately because commit behavior forks sharply between rollback-journal and WAL mode. Each trace below names the actual functions in call order as read from the sources.
- Connection open trace: `sqlite3_open_v2` → `openDatabase` → `sqlite3_initialize` → mutex decision → flag masking → `sqlite3MallocZero(sizeof(sqlite3))` → `sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE)` → `sqlite3_mutex_enter` → `db->eOpenState = SQLITE_STATE_BUSY` → … → `setupLookaside` → `sqlite3_wal_autocheckpoint` → `opendb_out` label, which closes and nulls the handle on OOM or marks it `SQLITE_STATE_SICK` on other errors.
- Prepare trace: `sqlite3_prepare_v2` → `sqlite3LockAndPrepare(db, zSql, nBytes, SQLITE_PREPARE_SAVESQL, 0, …)` → `sqlite3_mutex_enter(db->mutex)` → `sqlite3BtreeEnterAll(db)` → `sqlite3Prepare` (which memsets the `Parse` header/tail, links `sParse.pOuterParse`, and runs the parser) → retry loop → `sqlite3BtreeLeaveAll` → `sqlite3ApiExit` → `db->busyHandler.nBusy = 0`.
- `SQLITE_PREPARE_PERSISTENT` changes allocation strategy mid-prepare: `sqlite3Prepare` increments `sParse.disableLookaside` and invokes `DisableLookaside` so long-lived statements do not pin lookaside slabs.
- Step trace: `sqlite3_step` → `vdbeSafetyNotNull` → `sqlite3_mutex_enter` → inner `sqlite3Step` → (READY state) reset interrupt flag, bump `db->nVdbeActive`/`nVdbeWrite`/`nVdbeRead`, set `p->pc = 0`, move to `VDBE_RUN_STATE` → `sqlite3VdbeExec` (or `sqlite3VdbeList` when explaining) → on `SQLITE_DONE` with autocommit, `doWalCallbacks(db)`.
- Auto-reset trace: when `sqlite3_step` is called on a statement already in `VDBE_HALT_STATE`, `sqlite3Step` calls `sqlite3_reset()` and `goto restart_step` — behavior introduced in 3.7.0, with `SQLITE_OMIT_AUTORESET` restoring the pre-3.6.23 `SQLITE_MISUSE` return.
- Schema-change recovery trace: `sqlite3_step` loops while `sqlite3Step` returns `SQLITE_SCHEMA` (bounded by `SQLITE_MAX_SCHEMA_RETRY`), calling `sqlite3Reprepare(v)` → `sqlite3LockAndPrepare` on the saved SQL → `sqlite3VdbeSwap` → `sqlite3TransferBindings` → `sqlite3VdbeResetStepResult` → `sqlite3VdbeFinalize` of the throwaway VM, so bound parameters survive recompilation.
- VDBE entry trace: `sqlite3VdbeExec` asserts `VDBE_RUN_STATE`, takes B-Tree mutexes via `sqlite3VdbeEnter` when `p->lockMask` is non-zero, computes `nProgressLimit` from `db->nProgressOps`, checks `db->u1.isInterrupted` (jumping to `abort_due_to_interrupt`), then enters the opcode loop incrementing `nVmStep` per instruction.
- CLI startup trace: `main()` → `setvbuf(stderr,…,_IONBF)` → `isatty(0)`/`isatty(1)` to set `stdin_is_interactive`/`stdout_is_console` → `atexit(abnormalExit)` → optional `SQLITE_DEBUG_BREAK` debugger attach → `signal(SIGINT, interrupt_handler)`, `SIGTERM`/`SIGHUP` → `sqlite3_sourceid()` vs `SQLITE_SOURCE_ID` mismatch check that exits 1.
- `main_init()` runs before any SQLite use and configures the library globally: `sqlite3_config(SQLITE_CONFIG_LOG, shellLog, p)`, `verify_uninitialized()`, `sqlite3_config(SQLITE_CONFIG_URI, 1)` and `sqlite3_config(SQLITE_CONFIG_MULTITHREAD)` — so the CLI runs in multithread (not serialized) mode with URI filenames enabled.
- REPL trace: `process_input` → `one_input_line` → whitespace/`.`/`#` classification → `do_meta_command` for dot-commands (return code 2 means exit) → otherwise append to `zSql` → `sqlite3_complete` gate → `runOneSqlLine` → `shell_exec` → `sqlite3_prepare_v2`/step loop → QRF render.
- Startup also sources `~/.sqliterc` through the same `process_input` path unless `-noinit` was given, and exits 1 on error when `bail_on_error` is set — meaning the rc file is executed with full dot-command privileges before the user's first keystroke.
- Lazy-open trace: the shell does not open a database at startup; every dot-command that needs one calls `open_db(p, 0)` first (over 25 call sites), which is what makes `sqlite3` usable with no arguments and then `.open` later.
- Commit trace (WAL): `sqlite3BtreeCommitPhaseOne` → `sqlite3PagerCommitPhaseOne` → `pagerFlushOnCommit` check → `sqlite3PcacheDirtyList` → `pagerWalFrames` → `sqlite3PcacheCleanAll`, then `sqlite3BtreeCommitPhaseTwo` → `sqlite3PagerCommitPhaseTwo`.
- Commit trace (rollback journal): the same `sqlite3PagerCommitPhaseOne` falls into the non-WAL branch where `syncJournal()` and `pager_write_pagelist()` do the durable work, optionally short-circuited by `SQLITE_ENABLE_BATCH_ATOMIC_WRITE` when the device reports `SQLITE_IOCAP_BATCH_ATOMIC` and no super-journal is involved.