explain: explain command end to end (ADR-0028 steps 2–3)
Add the `explain` prefix command — `explain show data`,
`explain update`, `explain delete` — from grammar through to a
rendered plan tree.
- Grammar: an `EXPLAIN` CommandNode whose shape is a Choice over
the three explainable query shapes, referenced (not
duplicated) through `Subgrammar`. `Command::Explain { query:
Box<Self> }`; `build_show_data` is extracted so the role-based
builders serve both standalone and explain-wrapped commands.
- Worker: SQL construction is split out of do_query_data /
do_update / do_delete into `build_*_sql`, so EXPLAIN QUERY
PLAN runs the exact same statement. `Request::ExplainPlan` /
`do_explain_plan` capture the plan; `QueryPlan` / `ExplainRow`
carry it back. EXPLAIN QUERY PLAN never executes, so
explaining update/delete changes nothing.
- Display SQL: the executed statement with `?N` parameters
inlined as standard-SQL literals via a quote-aware scan.
- Render: `render_explain_plan` draws the box-drawing plan tree
(plain output; ADR-0028 step 4 adds the styled tree).
- Catalog: `parse.usage.explain` and the `help.data.explain`
entry, so `explain` shows up in the in-app `help` listing.
1151 tests pass (+18); clippy clean.
This commit is contained in:
@@ -166,6 +166,16 @@ pub enum Command {
|
||||
Replay {
|
||||
path: String,
|
||||
},
|
||||
/// Capture and display the query plan for an explainable
|
||||
/// command without executing it (ADR-0028). The inner
|
||||
/// `Command` is an ordinary parsed `ShowData` / `Update` /
|
||||
/// `Delete`; the runtime recognizes the `Explain` wrapper
|
||||
/// and routes it to the plan path instead of normal
|
||||
/// execution. Because `EXPLAIN QUERY PLAN` never runs the
|
||||
/// statement, explaining a destructive command is safe.
|
||||
Explain {
|
||||
query: Box<Self>,
|
||||
},
|
||||
/// App-lifecycle command (per ADR-0003). These work in both
|
||||
/// simple and advanced modes; the dispatcher branches on the
|
||||
/// `Command::App(...)` variant before mode-specific routing.
|
||||
@@ -457,6 +467,7 @@ impl Command {
|
||||
Self::Delete { .. } => "delete from",
|
||||
Self::ShowData { .. } => "show data",
|
||||
Self::Replay { .. } => "replay",
|
||||
Self::Explain { .. } => "explain",
|
||||
Self::App(app) => match app {
|
||||
AppCommand::Quit => "quit",
|
||||
AppCommand::Help => "help",
|
||||
@@ -514,6 +525,9 @@ impl Command {
|
||||
// Replay isn't tied to a single table; the path is
|
||||
// the most identifying thing for log output.
|
||||
Self::Replay { path } => path,
|
||||
// Explain forwards to the wrapped query — the table
|
||||
// the plan is about is the inner command's table.
|
||||
Self::Explain { query } => query.target_table(),
|
||||
// App commands aren't tied to schema entities — the
|
||||
// verb is the most identifying thing. The
|
||||
// display_subject override below provides a richer
|
||||
|
||||
Reference in New Issue
Block a user