feat: ADR-0035 4d — CREATE [UNIQUE] INDEX / DROP INDEX
Advanced-mode SQL CREATE [UNIQUE] INDEX [IF NOT EXISTS] [<name>] ON <T> (cols) -> SqlCreateIndex and DROP INDEX [IF EXISTS] <name> -> SqlDropIndex, both reusing the ADR-0025 executors (do_add_index / do_drop_index), like 4c reused do_drop_table. - CREATE UNIQUE INDEX admitted in advanced mode (ADR-0025 Amendment 1): ADR-0025 deferred UNIQUE indexes for the simple-mode DSL, but advanced mode trusts the user like SQL does. Adds an additive IndexSchema.unique flag (project.yaml, serde-default, version stays 1); rebuild re-emits CREATE UNIQUE INDEX; the redundant-set guard keys on (columns, unique). Simple-mode `add unique index` stays deferred. - IF [NOT] EXISTS on both forms reuses the 4c no-op-with-note skip (journalled, not snapshotted) via CreateIndexOutcome / DropIndexOutcome. - Unnamed CREATE INDEX auto-named (ADR-0025 convention); the [UNIQUE] prefix is a concrete-keyword Choice and the optional name an on-led-first selector (the drop-index selector precedent) — trap-safe. - create/drop each gain a second advanced node; the existing all-candidates dispatch handles it (locked by parse tests). - Unique indexes marked [unique] in the structure view and items panel. - do_add_index refuses internal __rdbms_* tables as "no such table", closing a latent exposure on both the simple `add index` and the new SQL CREATE INDEX surfaces (ADR-0025 Amendment 1). Docs: ADR-0035 status + §13 4d + 4i; ADR-0025 Amendment 1; ADR README; requirements.md Q1/C3. Plan: docs/plans/20260525-adr-0035-sql-ddl-4d.md. Tests: 1834 passing / 0 failing / 0 skipped / 1 ignored; clippy clean.
This commit is contained in:
+55
-8
@@ -29,9 +29,9 @@ use crate::action::Action;
|
||||
use crate::app::App;
|
||||
use crate::cli::Args;
|
||||
use crate::db::{
|
||||
AddColumnResult, ChangeColumnTypeResult, CreateOutcome, DataResult, Database, DbError,
|
||||
DeleteResult, DropColumnResult, DropOutcome, InsertResult, QueryPlan, TableDescription,
|
||||
UpdateResult,
|
||||
AddColumnResult, ChangeColumnTypeResult, CreateIndexOutcome, CreateOutcome, DataResult,
|
||||
Database, DbError, DeleteResult, DropColumnResult, DropIndexOutcome, DropOutcome, InsertResult,
|
||||
QueryPlan, TableDescription, UpdateResult,
|
||||
};
|
||||
use crate::dsl::{Command, ColumnSpec};
|
||||
use crate::dsl::walker::Severity;
|
||||
@@ -1029,11 +1029,18 @@ async fn build_schema_cache(database: &Database) -> crate::completion::SchemaCac
|
||||
// walker falls back to the schemaless value-literal list.
|
||||
for name in cache.tables.clone() {
|
||||
if let Ok(desc) = database.describe_table(name.clone(), None).await {
|
||||
// Per-table index names for the items panel (S2,
|
||||
// ADR-0025). Captured before `desc.columns` is
|
||||
// Per-table indexes for the items panel (S2, ADR-0025).
|
||||
// Carry uniqueness so the panel can mark a UNIQUE index
|
||||
// (ADR-0035 §4d). Captured before `desc.columns` is
|
||||
// consumed below.
|
||||
let index_names: Vec<String> =
|
||||
desc.indexes.iter().map(|i| i.name.clone()).collect();
|
||||
let index_entries: Vec<crate::completion::IndexEntry> = desc
|
||||
.indexes
|
||||
.iter()
|
||||
.map(|i| crate::completion::IndexEntry {
|
||||
name: i.name.clone(),
|
||||
unique: i.unique,
|
||||
})
|
||||
.collect();
|
||||
let cols: Vec<TableColumn> = desc
|
||||
.columns
|
||||
.into_iter()
|
||||
@@ -1055,7 +1062,7 @@ async fn build_schema_cache(database: &Database) -> crate::completion::SchemaCac
|
||||
})
|
||||
.collect();
|
||||
cache.table_columns.insert(name.clone(), cols);
|
||||
cache.table_indexes.insert(name, index_names);
|
||||
cache.table_indexes.insert(name, index_entries);
|
||||
}
|
||||
}
|
||||
cache
|
||||
@@ -1261,6 +1268,15 @@ fn spawn_dsl_dispatch(
|
||||
Ok(CommandOutcome::SchemaDropSkipped) => AppEvent::DslDropSkipped {
|
||||
command: command.clone(),
|
||||
},
|
||||
Ok(CommandOutcome::SchemaDropIndexSkipped) => AppEvent::DslDropIndexSkipped {
|
||||
command: command.clone(),
|
||||
},
|
||||
Ok(CommandOutcome::SchemaCreateIndexSkipped(name)) => {
|
||||
AppEvent::DslCreateIndexSkipped {
|
||||
command: command.clone(),
|
||||
name,
|
||||
}
|
||||
}
|
||||
Ok(CommandOutcome::Query(data)) => AppEvent::DslDataSucceeded {
|
||||
command: command.clone(),
|
||||
data,
|
||||
@@ -1662,6 +1678,15 @@ enum CommandOutcome {
|
||||
/// (ADR-0035 §4, 4c). Carries no structure (there is none); the App
|
||||
/// renders the "doesn't exist — skipped" note from the command.
|
||||
SchemaDropSkipped,
|
||||
/// A SQL `DROP INDEX IF EXISTS` that matched no index — a no-op
|
||||
/// (ADR-0035 §4d). The App renders the "doesn't exist — skipped"
|
||||
/// note from the command's index name.
|
||||
SchemaDropIndexSkipped,
|
||||
/// A SQL `CREATE INDEX IF NOT EXISTS` that matched an existing index
|
||||
/// name — a no-op (ADR-0035 §4d). Carries the resolved index name
|
||||
/// (the auto-name is unknown to the command) for the "already exists
|
||||
/// — skipped" note.
|
||||
SchemaCreateIndexSkipped(String),
|
||||
Query(DataResult),
|
||||
QueryPlan(QueryPlan),
|
||||
Insert(InsertResult),
|
||||
@@ -2048,6 +2073,28 @@ async fn execute_command_typed(
|
||||
.drop_index(selector, src)
|
||||
.await
|
||||
.map(|d| CommandOutcome::Schema(Some(d))),
|
||||
Command::SqlDropIndex { name, if_exists } => database
|
||||
.sql_drop_index(name, if_exists, src)
|
||||
.await
|
||||
.map(|outcome| match outcome {
|
||||
// Auto-show the now de-indexed table (ADR-0014), unlike
|
||||
// SQL DROP TABLE whose table is gone.
|
||||
DropIndexOutcome::Dropped(d) => CommandOutcome::Schema(Some(d)),
|
||||
DropIndexOutcome::Skipped => CommandOutcome::SchemaDropIndexSkipped,
|
||||
}),
|
||||
Command::SqlCreateIndex {
|
||||
name,
|
||||
table,
|
||||
columns,
|
||||
unique,
|
||||
if_not_exists,
|
||||
} => database
|
||||
.sql_create_index(name, table, columns, unique, if_not_exists, src)
|
||||
.await
|
||||
.map(|outcome| match outcome {
|
||||
CreateIndexOutcome::Created(d) => CommandOutcome::Schema(Some(d)),
|
||||
CreateIndexOutcome::Skipped(n) => CommandOutcome::SchemaCreateIndexSkipped(n),
|
||||
}),
|
||||
Command::AddConstraint {
|
||||
table,
|
||||
column,
|
||||
|
||||
Reference in New Issue
Block a user