2g rework: address DA findings on type recovery + engine routing + UI
Three DA critiques from the Phase-2 verification flagged real gaps; this commit closes them. 1. Type recovery row-independence (critique #1). The all-10-types test left col_blob NULL because the DSL Value enum has no Blob variant. The DA flagged this as a potential row-dependence gap. Added `database_run_select_type_recovery_works_on_empty_table` that proves column-origin metadata works on Text AND Blob columns with zero rows, pinning the invariant. The all-types test now carries an explicit comment referencing it. 2. Engine.* pattern matching against real SQLite output (critique #2). The pre-rework tests fed `translate_generic` hand-coded strings; never verified that the pinned SQLite version actually produces those wordings. Added three engine-routing tests in `tests/sql_select.rs` that produce real engine errors via `run_select` and assert catalog routing. Aggregate-in-WHERE confirms end-to-end. GROUP-BY-required and scalar-subquery are SQLite-permissive (no real error on the natural triggers), so those tests verify the matcher doesn't false-positive on benign queries + that synthetic messages route correctly. 3. Manual TUI verification (critique #3) surfaced an additional gap: `App::input_validity_verdict()` was hard-coded silent in Advanced mode, so SQL predicate warnings emitted but never reached the [WRN] indicator. Wired the verdict through to the active effective mode; updated two pre-existing tests that pinned the now-superseded "silent in Advanced" behavior; added one new test confirming a SQL `LIKE`-on-numeric warning fires the indicator. Launched the TUI, typed a representative warning-triggering SELECT, confirmed SELECT/FROM/WHERE/LIKE highlight as keyword colour AND the [WRN] indicator appears. Test totals: 1441 → 1446 passing (+5). Clippy clean.
This commit is contained in:
+59
-15
@@ -385,19 +385,24 @@ impl App {
|
||||
/// which the DSL walker does not parse (ADR-0027 §7). A
|
||||
/// pure query the runtime calls once the typing debounce
|
||||
/// settles; the result is stored in `input_indicator`.
|
||||
///
|
||||
/// ADR-0032 §10.6 — the verdict reads the walker view of
|
||||
/// the *active* effective mode so a SQL form in Advanced
|
||||
/// mode lights up the same `[ERR]` / `[WRN]` indicator the
|
||||
/// DSL surface uses. Without this the SQL predicate
|
||||
/// warnings (ADR-0032 §11.6) would emit but never reach
|
||||
/// the validity indicator the user sees.
|
||||
#[must_use]
|
||||
pub fn input_validity_verdict(&self) -> Option<crate::dsl::walker::Severity> {
|
||||
if !matches!(self.effective_mode(), EffectiveMode::Simple) {
|
||||
return None;
|
||||
}
|
||||
// ADR-0030 §2: the indicator is shown only in plain
|
||||
// simple mode (the guard above), so the verdict reads
|
||||
// the simple-mode walker view — a SQL form lights up
|
||||
// ERROR via the walker's mode gate.
|
||||
let mode = match self.effective_mode() {
|
||||
EffectiveMode::Simple => Mode::Simple,
|
||||
EffectiveMode::AdvancedPersistent
|
||||
| EffectiveMode::AdvancedOneShot => Mode::Advanced,
|
||||
};
|
||||
crate::dsl::walker::input_verdict_in_mode(
|
||||
&self.input,
|
||||
Some(&self.schema_cache),
|
||||
Mode::Simple,
|
||||
mode,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3270,20 +3275,59 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn input_validity_verdict_silent_in_advanced_mode() {
|
||||
// Advanced mode is raw SQL — the DSL walker must not
|
||||
// pass a verdict on it (ADR-0027 §7).
|
||||
fn input_validity_verdict_fires_in_advanced_mode_for_incomplete_input() {
|
||||
// Updated per ADR-0032 §10.6 / §11.6 — Phase 2 wires
|
||||
// the SQL diagnostic surface (predicate warnings, etc.)
|
||||
// through to the validity indicator. Pre-Phase-2 the
|
||||
// verdict was silent in Advanced mode; now it reflects
|
||||
// the active-mode walker's verdict, mirroring Simple
|
||||
// mode's behaviour.
|
||||
let mut app = App::new();
|
||||
app.mode = Mode::Advanced;
|
||||
app.input = "create table".to_string();
|
||||
assert_eq!(app.input_validity_verdict(), None);
|
||||
// Incomplete-at-EOF maps to Error (same as in Simple).
|
||||
assert_eq!(
|
||||
app.input_validity_verdict(),
|
||||
Some(crate::dsl::walker::Severity::Error),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn input_validity_verdict_silent_for_colon_one_shot() {
|
||||
// A `:`-prefixed line is a one-shot advanced escape.
|
||||
fn input_validity_verdict_fires_for_colon_one_shot() {
|
||||
// A `:`-prefixed line is a one-shot advanced escape;
|
||||
// the verdict reads the advanced walker view, same as
|
||||
// a persistent-advanced session.
|
||||
let mut app = App::new();
|
||||
app.input = ":create table".to_string();
|
||||
assert_eq!(app.input_validity_verdict(), None);
|
||||
assert_eq!(
|
||||
app.input_validity_verdict(),
|
||||
Some(crate::dsl::walker::Severity::Error),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn input_validity_verdict_fires_warning_for_sql_predicate_in_advanced() {
|
||||
// ADR-0032 §11.6 — a SQL `LIKE`-on-numeric predicate
|
||||
// emits a Warning diagnostic. The validity indicator
|
||||
// now reflects that in Advanced mode.
|
||||
use crate::completion::TableColumn;
|
||||
use crate::dsl::types::Type;
|
||||
let mut app = App::new();
|
||||
app.mode = Mode::Advanced;
|
||||
app.schema_cache.tables.push("products".to_string());
|
||||
app.schema_cache.columns.push("price".to_string());
|
||||
app.schema_cache.table_columns.insert(
|
||||
"products".to_string(),
|
||||
vec![TableColumn {
|
||||
name: "price".to_string(),
|
||||
user_type: Type::Real,
|
||||
}],
|
||||
);
|
||||
app.input =
|
||||
"select * from products where price like 5".to_string();
|
||||
assert_eq!(
|
||||
app.input_validity_verdict(),
|
||||
Some(crate::dsl::walker::Severity::Warning),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user