# ADR-0002: Database engine ## Status Accepted ## Context The application teaches relational database concepts. Requirements on the engine: - File-based, with a single binary file that can live in the project folder. - Real RDBMS feature set: typed columns, primary keys, foreign keys with referential integrity, indexes, query planner output we can expose to learners. - Industry adoption — students should leave with a skill that maps to something they will encounter again. - Embeddable from Rust without arcane build steps. Candidates considered: - **SQLite** — file-based, near-universal adoption, supports FK enforcement, has `EXPLAIN QUERY PLAN`, and since 3.37 supports `STRICT` tables which give proper type enforcement instead of SQLite's traditional loose type affinity. - **DuckDB** — file-based, modern, but analytical/columnar by design; would teach analytical instincts that mislead in transactional contexts. - **Embedded Postgres (pglite, pg_embed)** — closest to a "real" RDBMS, but adds friction and complicates packaging. ## Decision Use **SQLite** via the `rusqlite` crate. All tables are created as `STRICT` tables. Foreign-key enforcement is enabled per-connection (`PRAGMA foreign_keys = ON`). Simplified user-facing column types (see ADR-0005) are mapped to the underlying SQLite STRICT types at parse time. ## Consequences - Tight, low-friction packaging — `rusqlite` bundles SQLite. - `EXPLAIN QUERY PLAN` output is directly usable for the query analysis feature (ADR pending). - SQLite's `ALTER TABLE` is limited (e.g. type changes, some drops). Schema evolution will use the rebuild-table technique internally; this is hidden from users in simple mode and exposed honestly in advanced mode. - STRICT tables forbid the historical permissive typing students might encounter elsewhere — this is intentional and matches the pedagogical goal. - Booleans are stored as `INTEGER` (0/1) per SQLite's STRICT type set; the `bool` user-facing type maps to this and is rendered as `true`/`false` in result views.