diff --git a/cli/src/config.rs b/cli/src/config.rs index b0ddc817..32dca152 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -43,6 +43,8 @@ pub struct Settings { /// Output format is set by the flag. #[serde(skip)] pub output_format: OutputFormat, + #[serde(skip)] + pub time: bool, } #[derive(ValueEnum, Clone, Debug, PartialEq, Deserialize)] @@ -50,6 +52,7 @@ pub enum OutputFormat { Table, CSV, TSV, + Null, } impl Settings { @@ -64,9 +67,11 @@ impl Settings { "table" => OutputFormat::Table, "csv" => OutputFormat::CSV, "tsv" => OutputFormat::TSV, + "null" => OutputFormat::Null, _ => return Err(anyhow!("Unknown output format: {}", cmd_value)), } } + "time" => self.time = cmd_value.parse()?, _ => return Err(anyhow!("Unknown command: {}", cmd_name)), } Ok(()) @@ -113,6 +118,7 @@ impl Default for Settings { prompt: "{user}@{host}> ".to_string(), output_format: OutputFormat::Table, show_progress: false, + time: false, } } } diff --git a/cli/src/display.rs b/cli/src/display.rs index c3e031ff..4b4ea56c 100644 --- a/cli/src/display.rs +++ b/cli/src/display.rs @@ -199,6 +199,11 @@ impl<'a> ChunkDisplay for FormatDisplay<'a> { wtr.write_record(record)?; } } + OutputFormat::Null => {} + } + + if self.settings.time { + eprintln!("{:.3}", self._start.elapsed().as_secs_f64()); } Ok(()) } diff --git a/cli/src/main.rs b/cli/src/main.rs index af467160..a4b0762f 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -20,7 +20,7 @@ mod display; mod helper; mod session; -use std::collections::BTreeMap; +use std::{collections::BTreeMap, io::stdin}; use anyhow::{anyhow, Result}; use clap::{CommandFactory, Parser, ValueEnum}; @@ -136,6 +136,9 @@ struct Args { #[clap(long, help = "Show progress for data loading in stderr")] progress: bool, + + #[clap(long, help = "Only show execution time without results")] + time: bool, } /// Parse a single key-value pair @@ -248,6 +251,7 @@ pub async fn main() -> Result<()> { } else { config.settings.output_format = OutputFormat::TSV; } + config.settings.time = args.time; let mut session = session::Session::try_new(dsn, config.settings, is_repl).await?; @@ -261,13 +265,11 @@ pub async fn main() -> Result<()> { if args.non_interactive { return Err(anyhow!("no query specified")); } - session.handle_stdin().await + session.handle_reader(stdin().lock()).await } Some(query) => match args.data { None => { - if let Err(e) = session.handle_query(false, &query).await { - eprintln!("{}", e); - } + session.handle_reader(std::io::Cursor::new(query)).await; } Some(data) => { let options = args.format.get_options(&args.format_opt); diff --git a/cli/src/session.rs b/cli/src/session.rs index 26d01c52..8b916349 100644 --- a/cli/src/session.rs +++ b/cli/src/session.rs @@ -13,6 +13,7 @@ // limitations under the License. use std::collections::BTreeMap; +use std::io::stdin; use std::io::BufRead; use std::path::Path; use std::sync::Arc; @@ -71,7 +72,7 @@ impl Session { if self.is_repl { self.handle_repl().await; } else { - self.handle_stdin().await; + self.handle_reader(stdin().lock()).await; } } @@ -144,8 +145,8 @@ impl Session { let _ = rl.save_history(&get_history_path()); } - pub async fn handle_stdin(&mut self) { - let mut lines = std::io::stdin().lock().lines(); + pub async fn handle_reader(&mut self, r: R) { + let mut lines = r.lines(); while let Some(Ok(line)) = lines.next() { let queries = self.append_query(&line); for query in queries { @@ -166,7 +167,7 @@ impl Session { } } - fn append_query(&mut self, line: &str) -> Vec { + pub fn append_query(&mut self, line: &str) -> Vec { let line = line.trim(); if line.is_empty() { return vec![];