Track 1 implementation plus polish round. Parser (chumsky): - Grammar-based DSL producing a typed Command AST. - create table X with pk [name:type[,name:type...]] supports arbitrary names, any user type, compound PKs natively. Bare form errors with a friendly hint pointing at `with pk`. - add column to table X: Name (type); drop table X. - Required clauses use keyword grammar; -- reserved for opt-in flags (ADR-0009). Custom Rich reasons preferred when surfacing chumsky errors so unknown-type messages list valid alternatives. Database (ADR-0010, ADR-0012): - rusqlite + STRICT tables + foreign_keys=ON. - Dedicated worker thread; mpsc Request inbox, oneshot replies. - Typed DbError with friendly_message() hook for H1. - Internal __rdbms_playground_columns metadata table preserves user-facing types across schema reads, atomically maintained alongside DDL via Connection transactions. list_tables hides it via the new __rdbms_ internal-table convention. Types (ADR-0005, ADR-0011): - All ten user-facing types: text, int, real, decimal, bool, date, datetime, blob, serial, shortid. - Type::fk_target_type() for FK-side column-type rule (Serial->Int, ShortId->Text, others identity) -- foundation for the FK iteration. App / Runtime / UI: - update() stays pure-sync; runtime dispatches DSL via spawned tasks, results post back as AppEvent::Dsl*. - Items panel renders live tables list; output panel shows the user-facing structure of the current table after each DDL. - In-memory command history (Up/Down, draft preservation, consecutive-duplicate dedup) -- I2 partial. - Mouse capture removed; terminal native text selection restored (toggle approach revisited when scroll/click features land). Docs: - ADRs 0009 (DSL syntax conventions), 0010 (DB worker), 0011 (FK type compat), 0012 (internal metadata table). - requirements.md progress notes; new V4 entry for the scrollable session-log + inline rich rendering + Markdown export direction. Tests: 103 passing (91 lib + 12 integration), 0 skipped. Clippy clean with nursery enabled.
3.0 KiB
ADR-0011: Foreign-key column type compatibility
Status
Accepted
Context
Two of our user-facing types (per ADR-0005) carry insert-time auto-generation semantics that only apply on the primary-key side of a relationship:
serialisINTEGER PRIMARY KEYwith rowid-alias / auto-increment behaviour.shortidisTEXTpopulated client-side with a 10–12 char base58 random value when no value is supplied.
A foreign-key column referencing such a primary key does not
auto-generate values — it stores the looked-up value of the
primary key. Asking the user to declare the FK column with the
same user-facing type as the PK would be wrong: serial on the
FK side would imply the FK column has its own auto-increment
counter (it doesn't), and similar for shortid.
Without this rule, the future FK declaration grammar (C3, C4) would either generate incorrect DDL or rely on the user to remember to use a different type on the FK side — an easy foot-gun.
Decision
Define Type::fk_target_type(self) -> Type returning the
appropriate user-facing type for a foreign-key column that
references a primary key of self:
| User-facing type | fk_target_type() |
|---|---|
text |
text |
int |
int |
real |
real |
decimal |
decimal |
bool |
bool |
date |
date |
datetime |
datetime |
blob |
blob |
serial |
int |
shortid |
text |
All types other than serial and shortid are identity
mappings. The two exceptions strip the auto-generation
semantics by mapping to the underlying value type.
Consequences
- The future FK declaration grammar uses
fk_target_type()in one of two ways:- Auto-typing — when the FK column does not yet exist and
--create-fkis given (or whatever the equivalent flag becomes), the column is created withpk_column.ty.fk_target_type(). - Validation — when the FK column already exists, its
type is compared against
fk_target_type(). A mismatch yields a clear diagnostic ("Customers.id isserial; the FK column should beint, nottext"). This is the teaching moment ADR-0009's design philosophy targets.
- Auto-typing — when the FK column does not yet exist and
- Adding a new user-facing type forces an explicit decision
about its
fk_target_type(). The type system is therefore closed under FK declaration. - The advisory feedback module (foreshadowed in V4 and the
hint surface) can use the same mapping to surface
recommendations during command typing — e.g. "you used
serialfor an FK; conventionallyintis the right fit here." This is advice not gating, consistent with ADR-0009's separation of required grammar from optional guidance. fk_target_type()is implemented and tested before the FK declaration grammar lands, so the FK iteration is grammar work only.