feat: ADR-0035 4c — DROP TABLE [IF EXISTS]
Add advanced-mode SQL `DROP TABLE [IF EXISTS] <name>` -> SqlDropTable, executing through the existing do_drop_table (cascade / inbound- relationship refusal / metadata cleanup) — full parity with the simple `drop table`. The only new behaviour is `IF EXISTS` as a no-op-with-note: a new DropOutcome::Skipped mirroring CreateOutcome::Skipped (journalled, no snapshot), rendered via a new ddl.drop_skipped_absent note + DslDropSkipped event. - Grammar: SQL_DROP_TABLE node (entry `drop`, shape `table [if exists] <name> [;]`), registered Advanced. SQL-first dispatch: `drop table T` -> SqlDropTable in advanced; `drop column`/`relationship`/`index`/ `constraint` fall back to the simple `drop` node (and still execute). - Worker: Request::SqlDropTable + db.sql_drop_table; the if-exists-and- absent arm journals + replies Skipped without a snapshot, else snapshot_then(do_drop_table) -> Dropped. - Completion: advanced `drop ` now surfaces the SQL `table` (the shared-entry-word behaviour from `create`); test split into simple (full DSL list) + advanced (SQL surface). Known shared-entry-word completion unevenness (advanced `drop ` offers only `table`; partial `drop rel` returns an empty list) deferred to 4i (merge candidate sets for shared entry words) along with a flagged user request to visually distinguish simple- vs advanced-mode completions in the hint UI — tracked in ADR §13 4i (d)/(e), the 4c plan, and the completion test. The DSL drops still parse + execute via fallback. 10 new tests (parse/builder + Tier-3: drop existing + one-undo-step + restore, IF EXISTS skip + journal, plain-absent error, inbound refusal). Docs: ADR-0035 Status/§13, README, requirements.md Q1. Tests: 1805 passing, 0 failing, 1 ignored. Clippy clean.
This commit is contained in:
@@ -477,6 +477,15 @@ enum Request {
|
||||
source: Option<String>,
|
||||
reply: oneshot::Sender<Result<(), DbError>>,
|
||||
},
|
||||
/// Advanced-mode SQL `DROP TABLE [IF EXISTS]` (ADR-0035 §4, 4c).
|
||||
/// Executes through `do_drop_table`; `if_exists` turns an absent
|
||||
/// table into a no-op (`DropOutcome::Skipped`, no snapshot).
|
||||
SqlDropTable {
|
||||
name: String,
|
||||
if_exists: bool,
|
||||
source: Option<String>,
|
||||
reply: oneshot::Sender<Result<DropOutcome, DbError>>,
|
||||
},
|
||||
AddColumn {
|
||||
table: String,
|
||||
column: ColumnSpec,
|
||||
@@ -865,6 +874,26 @@ impl Database {
|
||||
recv.await.map_err(|_| DbError::WorkerGone)?
|
||||
}
|
||||
|
||||
/// Advanced-mode SQL `DROP TABLE [IF EXISTS]` (ADR-0035 §4, 4c).
|
||||
/// Returns whether the table was dropped or skipped (the `IF EXISTS`
|
||||
/// no-op on an absent table).
|
||||
pub async fn sql_drop_table(
|
||||
&self,
|
||||
name: String,
|
||||
if_exists: bool,
|
||||
source: Option<String>,
|
||||
) -> Result<DropOutcome, DbError> {
|
||||
let (reply, recv) = oneshot::channel();
|
||||
self.send(Request::SqlDropTable {
|
||||
name,
|
||||
if_exists,
|
||||
source,
|
||||
reply,
|
||||
})
|
||||
.await?;
|
||||
recv.await.map_err(|_| DbError::WorkerGone)?
|
||||
}
|
||||
|
||||
pub async fn add_column(
|
||||
&self,
|
||||
table: String,
|
||||
@@ -1765,6 +1794,31 @@ fn handle_request(
|
||||
do_drop_table(conn, persistence, source.as_deref(), &name)
|
||||
});
|
||||
}
|
||||
Request::SqlDropTable {
|
||||
name,
|
||||
if_exists,
|
||||
source,
|
||||
reply,
|
||||
} => {
|
||||
// `IF EXISTS` on an absent table is a no-op: reply `Skipped`
|
||||
// and take **no** snapshot (nothing to undo). The submitted
|
||||
// line is still journalled — like the `CREATE TABLE IF NOT
|
||||
// EXISTS` skip and other no-ops (ADR-0034). ADR-0035 §4.
|
||||
if if_exists && !user_table_exists(conn, &name).unwrap_or(false) {
|
||||
let result = (|| {
|
||||
if let (Some(p), Some(text)) = (persistence, source.as_deref()) {
|
||||
p.append_history(text).map_err(DbError::from_persistence)?;
|
||||
}
|
||||
Ok(DropOutcome::Skipped)
|
||||
})();
|
||||
let _ = reply.send(result);
|
||||
} else {
|
||||
snapshot_then(snap, batch, conn, source.as_deref(), reply, || {
|
||||
do_drop_table(conn, persistence, source.as_deref(), &name)
|
||||
.map(|()| DropOutcome::Dropped)
|
||||
});
|
||||
}
|
||||
}
|
||||
Request::AddColumn {
|
||||
table,
|
||||
column,
|
||||
@@ -2633,6 +2687,18 @@ pub enum CreateOutcome {
|
||||
Skipped(TableDescription),
|
||||
}
|
||||
|
||||
/// The result of an advanced-mode SQL `DROP TABLE` (ADR-0035 §4, 4c).
|
||||
///
|
||||
/// Either the table was dropped, or `IF EXISTS` matched no table and
|
||||
/// the statement was a no-op that drives the "doesn't exist — skipped"
|
||||
/// note. Carries no payload — the runtime renders the note from the
|
||||
/// command's table name.
|
||||
#[derive(Debug)]
|
||||
pub enum DropOutcome {
|
||||
Dropped,
|
||||
Skipped,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn do_create_table(
|
||||
conn: &Connection,
|
||||
|
||||
Reference in New Issue
Block a user