diff --git a/README.md b/README.md index e44ba957..ec5048a8 100644 --- a/README.md +++ b/README.md @@ -144,15 +144,25 @@ prompt = ":) " | `replace_newline` | whether replace '\n' with '\\\n'. | -## Control commands in REPL +## Commands in REPL -We can use `.CMD_NAME VAL` to update the `Settings` above in runtime, example: +| Commands | Description | +|---|---| +| `!exit` | Exit bendsql | +| `!quit` | Exit bendsql | +| `!configs` | Show current settings | +| `!set` | Set settings | +| `!source file` | Source file and execute | + +## Setting commands in REPL + +We can use `!set CMD_NAME VAL` to update the `Settings` above in runtime, example: ``` ❯ bendsql -:) .display_pretty_sql false -:) .max_display_rows 10 -:) .expand auto +:) !set display_pretty_sql false +:) !set max_display_rows 10 +:) !set expand auto ``` ## DSN diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 4d42879d..41631b3b 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -42,6 +42,7 @@ toml = "0.8" tracing-appender = "0.2" unicode-segmentation = "1.10" url = { version = "2.5", default-features = false } +async-recursion = "1.1.0" [build-dependencies] vergen = { version = "8.2", features = ["build", "git", "gix"] } diff --git a/cli/src/session.rs b/cli/src/session.rs index e127f2df..f2c7ad22 100644 --- a/cli/src/session.rs +++ b/cli/src/session.rs @@ -19,6 +19,7 @@ use std::sync::Arc; use anyhow::anyhow; use anyhow::Result; +use async_recursion::async_recursion; use chrono::NaiveDateTime; use databend_driver::ServerStats; use databend_driver::{Client, Connection}; @@ -319,7 +320,7 @@ impl Session { } if self.query.is_empty() - && (line.starts_with('.') + && (line.starts_with('!') || line == "exit" || line == "quit" || line.to_uppercase().starts_with("PUT")) @@ -388,28 +389,16 @@ impl Session { queries } + #[async_recursion] pub async fn handle_query( &mut self, is_repl: bool, query: &str, ) -> Result> { let query = query.trim_end_matches(';').trim(); - if is_repl && (query == "exit" || query == "quit") { - return Ok(None); - } - if is_repl && query.starts_with('.') { - let query = query - .trim_start_matches('.') - .split_whitespace() - .collect::>(); - if query.len() != 2 { - return Err(anyhow!( - "Control command error, must be syntax of `.cmd_name cmd_value`." - )); - } - self.settings.inject_ctrl_cmd(query[0], query[1])?; - return Ok(Some(ServerStats::default())); + if is_repl && query.starts_with('!') { + return self.handle_commands(query).await; } let start = Instant::now(); @@ -466,6 +455,45 @@ impl Session { } } + #[async_recursion] + pub async fn handle_commands(&mut self, query: &str) -> Result> { + if query == "!exit" || query == "!quit" { + return Ok(None); + } + + match query { + "!exit" | "!quit" => { + return Ok(None); + } + "!configs" => { + println!("{:#?}", self.settings); + } + other => { + if other.starts_with("!set") { + let query = query[4..].split_whitespace().collect::>(); + if query.len() != 2 { + return Err(anyhow!( + "Control command error, must be syntax of `.cmd_name cmd_value`." + )); + } + self.settings.inject_ctrl_cmd(query[0], query[1])?; + } else if other.starts_with("!source") { + let query = query[7..].trim(); + let path = Path::new(query); + if !path.exists() { + return Err(anyhow!("File not found: {}", query)); + } + let file = std::fs::File::open(path)?; + let reader = std::io::BufReader::new(file); + self.handle_reader(reader).await?; + } else { + return Err(anyhow!("Unknown commands: {}", other)); + } + } + } + Ok(Some(ServerStats::default())) + } + pub async fn stream_load_stdin( &mut self, query: &str,