Indexes: add index / drop index, persistence, display (ADR-0025)
Implement ADR-0025 — indexes as a DSL DDL feature. - Grammar: `add index [as <name>] on <T> (<cols>)`, `drop index <name>` / `drop index on <T> (<cols>)`, plus a `--cascade` flag on `drop column`. - db.rs: index operations over the engine's native index catalog (no metadata table). The rebuild-table primitive now captures and recreates indexes, so `change column` and the relationship operations no longer silently drop them. - `drop column` refuses an indexed column unless `--cascade`, which drops the covering indexes and reports each. - Persistence: additive `indexes:` list in `project.yaml` (version unchanged); round-trips through rebuild/export/import. - Display: an `Indexes:` section in the structure view and a nested tables/indexes items panel (S2). Reconciles requirements.md (C3 index portion, S2 satisfied) and CLAUDE.md. 1038 tests passing (+31), clippy clean.
This commit is contained in:
+30
-11
@@ -40,11 +40,15 @@ pub struct SchemaCache {
|
||||
pub tables: Vec<String>,
|
||||
pub columns: Vec<String>,
|
||||
pub relationships: Vec<String>,
|
||||
pub indexes: Vec<String>,
|
||||
/// Per-table column metadata with user-facing types
|
||||
/// (ADR-0024 §Phase D). Keyed by table name; lookup is
|
||||
/// case-insensitive in `columns_for_table` so the walker
|
||||
/// can resolve `Customers` regardless of how it was typed.
|
||||
pub table_columns: std::collections::HashMap<String, Vec<TableColumn>>,
|
||||
/// Per-table user index names (ADR-0025). Keyed by table
|
||||
/// name; drives the nested tables/indexes items panel (S2).
|
||||
pub table_indexes: std::collections::HashMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
/// One column's user-facing type info, scoped to a table
|
||||
@@ -65,6 +69,7 @@ impl SchemaCache {
|
||||
IdentSource::Tables => &self.tables,
|
||||
IdentSource::Columns => &self.columns,
|
||||
IdentSource::Relationships => &self.relationships,
|
||||
IdentSource::Indexes => &self.indexes,
|
||||
IdentSource::NewName | IdentSource::Types | IdentSource::Free => &[],
|
||||
}
|
||||
}
|
||||
@@ -816,16 +821,23 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_candidate_position_offers_column_and_one_to_n() {
|
||||
fn multi_candidate_position_offers_add_subcommands() {
|
||||
// After `add ` the parser expects `column` (for
|
||||
// `add column ...`) and `1` (the opener for
|
||||
// `add column ...`), `index` (for `add index ...`,
|
||||
// ADR-0025), and `1` (the opener for
|
||||
// `add 1:n relationship ...`). The completion engine
|
||||
// surfaces both: `column` straight from the keyword
|
||||
// expected-set, and `1:n` as a composite literal
|
||||
// candidate so the user can Tab through to the
|
||||
// relationship form without knowing the surface syntax.
|
||||
// sections keyword candidates (`column`, `index`)
|
||||
// ahead of the `1:n` composite literal, so the literal
|
||||
// sorts last even though `add 1:n` is declared second.
|
||||
let cs = cands("add ", 4);
|
||||
assert_eq!(cs, vec!["column".to_string(), "1:n".to_string()]);
|
||||
assert_eq!(
|
||||
cs,
|
||||
vec![
|
||||
"column".to_string(),
|
||||
"index".to_string(),
|
||||
"1:n".to_string(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1039,7 +1051,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_offers_three_alternatives_alphabetised() {
|
||||
fn drop_offers_all_four_subcommands() {
|
||||
// `drop` branches: column / relationship / table / index
|
||||
// (ADR-0025). Candidates follow grammar declaration
|
||||
// order, so `index` — added last — appears last.
|
||||
let cs = cands("drop ", 5);
|
||||
assert_eq!(
|
||||
cs,
|
||||
@@ -1047,6 +1062,7 @@ mod tests {
|
||||
"column".to_string(),
|
||||
"relationship".to_string(),
|
||||
"table".to_string(),
|
||||
"index".to_string(),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -1593,13 +1609,16 @@ mod tests {
|
||||
c.sort_by(|a, b| a.text.cmp(&b.text));
|
||||
c
|
||||
}
|
||||
// `add ` exposes `column` and `1:n` — alphabetic ranker
|
||||
// flips them.
|
||||
// `add ` exposes `column`, `1:n` and `index` — the
|
||||
// alphabetic ranker reorders them.
|
||||
let cache = SchemaCache::default();
|
||||
let comp = candidates_at_cursor_with("add ", 4, &cache, alphabetic_ranker)
|
||||
.expect("some completion");
|
||||
let texts: Vec<String> = comp.candidates.into_iter().map(|c| c.text).collect();
|
||||
assert_eq!(texts, vec!["1:n".to_string(), "column".to_string()]);
|
||||
assert_eq!(
|
||||
texts,
|
||||
vec!["1:n".to_string(), "column".to_string(), "index".to_string()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user