# 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.

## User-facing posture

The choice of SQLite is an implementation detail. The
playground's user-facing surface — error messages, success
notes, help text, and any other string the user sees —
never names the underlying engine. "The database" or "the
engine" in the abstract is the canonical phrasing; the
specific product (SQLite, STRICT, rusqlite, PRAGMA) is
ADR-internal and code-comment vocabulary only.

Rationale: students should leave with knowledge that
generalises. Naming the engine in user-visible strings
ties their mental model to a specific product when the
lessons are about relational concepts. The friendly-error
layer (H1) wraps engine error text before surfacing;
advanced mode's SQL surface doesn't expose the engine name
either — the user writes SQL because SQL is the language,
not because SQLite is the engine.

This commitment is referenced from later ADRs (notably
ADR-0017 §6 on the client-side conversion note); when in
doubt about a user-facing string's wording, this section
governs.

## 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.
