feat(seed): set override clause + column-fill (ADR-0048 Phase 2)

Build the two SD2 surfaces Phase 1 deferred:

- `set` override clause (D2): comma-separated per-column pins —
  `= 'v'` (fixed), `in ('a','b')` (pick-list), `as <generator>`
  (named), `between x and y` (range; numeric and quoted dates).
  Type-aware via the typed `current_column_value` slot; an override
  drops its column from the generic-fill advisory (D13). Folded from
  the flat matched path (build_seed_overrides) and applied to the
  per-column plan (apply_seed_overrides).
- `<table>.<column>` column-fill (D1 form 2): an UPDATE over existing
  rows. Refuses PK/autogen targets, empty-table no-op, FK-samples the
  parent, collision-free for UNIQUE/identifier targets, one undo step;
  `set` may only adjust the filled column.

Supporting work: KNOWN_GENERATORS vocabulary + generator_for_name
(src/seed/vocabulary.rs, D9); a range Generator + range_bounds_reason;
IdentSource::Generators and HighlightClass::Function; completion of the
generator vocabulary after `as` and the set/.col column slots; the
typing-time validity indicator for an unknown generator; help,
parse-error pedagogy rows, and the D13 advisory's Phase-2/3 wording.

A bounded override (fixed value / too-short pick-list) on a
single-column-UNIQUE target is a friendly error rather than a silent
uniqueness cap (post-implementation /runda finding, user-chosen).

Dates in the range form are quoted (no date-literal token exists);
ADR-0048 D2 amended accordingly. Both modes (D5); reproducible (D4).
This commit is contained in:
claude@clouddev1
2026-06-12 09:44:30 +00:00
parent 78c38e8b33
commit a12facc784
20 changed files with 1913 additions and 65 deletions
+46 -5
View File
@@ -402,14 +402,23 @@ pub enum Command {
filter: Option<Expr>,
limit: Option<u64>,
},
/// Populate a table with generated fake data (ADR-0048, SD1).
/// `count` defaults to 20 when omitted; `rng_seed` (from a future
/// `--seed <n>` flag) makes generation reproducible. Phase 1 is
/// whole-row generation; the `set` override clause and the
/// `<table>.<column>` column-fill form arrive in later phases.
/// Populate a table with generated fake data (ADR-0048, SD1/SD2).
/// `count` defaults to 20 when omitted; `rng_seed` (from the
/// `--seed <n>` flag) makes generation reproducible.
///
/// Phase 2 surfaces (ADR-0048 D1/D2):
/// - `target_column` is `Some` for the **column-fill** form
/// `seed <table>.<column>` — fill one column across the table's
/// *existing* rows (an UPDATE), rather than generating new rows.
/// - `overrides` carries the `set <col> …` clause: per-column pins
/// that take precedence over the heuristic generator (D2).
Seed {
table: String,
/// `Some(col)` → column-fill mode (UPDATE existing rows);
/// `None` → whole-row generation (INSERT new rows).
target_column: Option<String>,
count: Option<u64>,
overrides: Vec<SeedOverride>,
rng_seed: Option<u64>,
},
/// Replay a sequence of DSL commands from a file. Each line
@@ -647,6 +656,38 @@ impl RowFilter {
}
}
/// One `set <col> …` override on a `seed` command (ADR-0048 D2, Phase 2).
///
/// The user can pin a column's generated values to a constant, a
/// pick-list, an explicit named generator, or a range — overriding the
/// per-column heuristic the executor would otherwise pick. `column` is
/// the user-typed column name (validated against the table at execution,
/// like every other column slot).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SeedOverride {
pub column: String,
pub kind: SeedOverrideKind,
}
/// The four `set` override forms (ADR-0048 D2).
///
/// Values arrive as the DSL's `Value` (quoted text / unquoted number —
/// dates are quoted text per the D2 amendment); the `Generator` name is
/// a raw string validated at execution because `src/dsl` cannot depend
/// on `src/seed` (the curated vocabulary lives there).
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SeedOverrideKind {
/// `set status = 'pending'` — every row gets the constant.
Fixed(Value),
/// `set role in ('admin', 'editor')` — uniform pick from the list.
PickList(Vec<Value>),
/// `set work_addr as email` — force the named generator (D9).
Generator(String),
/// `set price between 10 and 100` — uniform in `[low, high]`;
/// numeric or (quoted) date bounds per the destination column type.
Range { low: Value, high: Value },
}
/// A complex WHERE expression (ADR-0026 §4).
///
/// Built by `grammar::expr::build_expr` from the flat