c3e5f9014f
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.
81 lines
3.0 KiB
Markdown
81 lines
3.0 KiB
Markdown
# 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.
|