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:
+48
-1
@@ -46,10 +46,14 @@ pub enum Command {
|
||||
},
|
||||
/// Remove a column from a table. Refused if the column is
|
||||
/// part of the primary key or is involved in a declared
|
||||
/// relationship — drop the relationship first.
|
||||
/// relationship — drop the relationship first. Refused, too,
|
||||
/// when an index covers the column, unless `cascade` is set
|
||||
/// (the `--cascade` flag), in which case the covering
|
||||
/// indexes are dropped alongside the column (ADR-0025).
|
||||
DropColumn {
|
||||
table: String,
|
||||
column: String,
|
||||
cascade: bool,
|
||||
},
|
||||
/// Rename a column. SQLite handles cascading renames in
|
||||
/// FK references on other tables; the executor mirrors
|
||||
@@ -96,6 +100,19 @@ pub enum Command {
|
||||
DropRelationship {
|
||||
selector: RelationshipSelector,
|
||||
},
|
||||
/// Create an index on one or more columns of a table
|
||||
/// (ADR-0025). `name` is optional — when `None`, the
|
||||
/// executor auto-generates `<Table>_<col…>_idx`.
|
||||
AddIndex {
|
||||
name: Option<String>,
|
||||
table: String,
|
||||
columns: Vec<String>,
|
||||
},
|
||||
/// Drop an index by name, or by positional reference to its
|
||||
/// table and exact column set (ADR-0025).
|
||||
DropIndex {
|
||||
selector: IndexSelector,
|
||||
},
|
||||
/// Re-display a table's structure in the output. Doesn't
|
||||
/// change schema; useful when the user wants to look at a
|
||||
/// table they aren't currently DDL'ing on.
|
||||
@@ -253,6 +270,26 @@ impl std::fmt::Display for RelationshipSelector {
|
||||
}
|
||||
}
|
||||
|
||||
/// How a `drop index` command identifies the index to remove
|
||||
/// (ADR-0025). Both forms are accepted; the executor resolves to
|
||||
/// a single index.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum IndexSelector {
|
||||
Named { name: String },
|
||||
Columns { table: String, columns: Vec<String> },
|
||||
}
|
||||
|
||||
impl std::fmt::Display for IndexSelector {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Named { name } => write!(f, "{name}"),
|
||||
Self::Columns { table, columns } => {
|
||||
write!(f, "on {table} ({})", columns.join(", "))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Command {
|
||||
/// Short label for log output and result rendering.
|
||||
#[must_use]
|
||||
@@ -266,6 +303,8 @@ impl Command {
|
||||
Self::ChangeColumnType { .. } => "change column",
|
||||
Self::AddRelationship { .. } => "add relationship",
|
||||
Self::DropRelationship { .. } => "drop relationship",
|
||||
Self::AddIndex { .. } => "add index",
|
||||
Self::DropIndex { .. } => "drop index",
|
||||
Self::ShowTable { .. } => "show table",
|
||||
Self::Insert { .. } => "insert into",
|
||||
Self::Update { .. } => "update",
|
||||
@@ -318,6 +357,14 @@ impl Command {
|
||||
// is a sensible fallback for logging.
|
||||
RelationshipSelector::Named { name } => name,
|
||||
},
|
||||
Self::AddIndex { table, .. } => table,
|
||||
Self::DropIndex { selector } => match selector {
|
||||
IndexSelector::Columns { table, .. } => table,
|
||||
// A named drop doesn't name the table until the
|
||||
// executor resolves it; the index name is a
|
||||
// sensible fallback for logging.
|
||||
IndexSelector::Named { name } => name,
|
||||
},
|
||||
// Replay isn't tied to a single table; the path is
|
||||
// the most identifying thing for log output.
|
||||
Self::Replay { path } => path,
|
||||
|
||||
Reference in New Issue
Block a user