Files
rdbms-playground/docs/adr/0002-database-engine.md
T
claude@clouddev1 c3e5f9014f ADR-0017 + ADR-0002 amendment: type-change compatibility + engine-agnostic posture
Specifies the curated per-cell classification (clean /
lossy / incompatible) for column type changes, the static
transformer matrix (numeric chains, text↔structured types,
always-clean stringifications), and the PK / shortid /
uniqueness-bearing handling. Replaces the B2/C2
placeholder of "rely on engine STRICT and surface its
errors" with a learner-friendly model that:

* refuses incompatibles up-front,
* refuses lossy conversions by default with a re-run-with-
  --force-conversion hint,
* refines the PK refusal: an inbound-FK PK is only refused
  when the new type would change the FK target type
  (so `serial → int` and `shortid → text` on FK-referenced
  PKs are allowed; `int → text` etc. still refuse),
* adds a post-transformation uniqueness check for PK and
  shortid columns,
* uses the pretty-table renderer (ADR-0016) for all
  diagnostic row lists,
* emits a `[client-side] …` note in the success summary
  whenever the transformer rewrote any cell.

`--force-conversion` accepts loss; `--dont-convert` skips
the client-side layer entirely; mutually exclusive.

Forward-look: a future iteration may add resolution flags
(`--default 0`, `--on-incompatible '<value>'`).

Also amends ADR-0002 with a new "User-facing posture"
section cementing that the database engine choice is an
implementation detail and is never named in user-visible
strings. Adds a corresponding bullet to CLAUDE.md's
working-style rules so every session picks it up.

Implementation lands as a follow-up.
2026-05-08 10:53:20 +00:00

3.0 KiB

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.