From 23772d4413da46641b40218b3b20e5451dd32367 Mon Sep 17 00:00:00 2001 From: everpcpc Date: Sun, 23 Apr 2023 17:37:53 +0800 Subject: [PATCH] feat(cli): support multi line and output format & add test for ontime200 (#83) * fix(driver): message for unimplemented in FlightSQL * feat(cli): support multi line & add test for ontime200 * fix(cli): format progress message * fix: output csv for ontime * feat(cli): support multiple output --- cli/Cargo.toml | 4 +- cli/src/config.rs | 14 +++ cli/src/display.rs | 88 ++++++++------ cli/src/main.rs | 13 +- cli/src/session.rs | 109 ++++++++++------- cli/test.sh | 4 +- cli/tests/data/ontime.sql | 111 ++++++++++++++++++ cli/tests/data/ontime_200.csv.gz | Bin 0 -> 8327 bytes cli/tests/http/01-load_stdin.sh | 2 +- ...1-load_file.result => 02-load_file.result} | 0 .../http/{01-load_file.sh => 02-load_file.sh} | 2 +- cli/tests/http/03-load_file_gzip.result | 2 + cli/tests/http/03-load_file_gzip.sh | 17 +++ driver/Cargo.toml | 2 +- driver/src/flight_sql.rs | 2 +- 15 files changed, 274 insertions(+), 96 deletions(-) create mode 100644 cli/tests/data/ontime.sql create mode 100644 cli/tests/data/ontime_200.csv.gz rename cli/tests/http/{01-load_file.result => 02-load_file.result} (100%) rename cli/tests/http/{01-load_file.sh => 02-load_file.sh} (82%) create mode 100644 cli/tests/http/03-load_file_gzip.result create mode 100644 cli/tests/http/03-load_file_gzip.sh diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 7bb46d7ae..de34a034f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bendsql" -version = "0.2.7" +version = "0.3.0" edition = "2021" license = "Apache-2.0" description = "Databend Native Command Line Tool" @@ -16,7 +16,7 @@ chrono = { version = "0.4.24", default-features = false, features = ["clock"] } clap = { version = "4.1.0", features = ["derive", "env"] } comfy-table = "6.1.4" csv = "1.2.1" -databend-driver = { path = "../driver", version = "0.2.17", features = ["rustls", "flight-sql"] } +databend-driver = { path = "../driver", version = "0.2.18", features = ["rustls", "flight-sql"] } futures = { version = "0.3", default-features = false, features = ["alloc"] } humantime-serde = "1.1.1" indicatif = "0.17.3" diff --git a/cli/src/config.rs b/cli/src/config.rs index 7811ab0e9..0b51a2b80 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -16,6 +16,7 @@ use std::{collections::BTreeMap, path::Path}; +use clap::ValueEnum; use serde::Deserialize; #[derive(Clone, Debug, Default, Deserialize)] @@ -32,6 +33,17 @@ pub struct Settings { pub display_pretty_sql: bool, pub prompt: String, pub progress_color: String, + pub output_format: OutputFormat, + /// Show progress [bar] when executing queries. + /// Only works in non-interactive mode. + pub show_progress: bool, +} + +#[derive(ValueEnum, Clone, Debug, PartialEq, Deserialize)] +pub enum OutputFormat { + Table, + CSV, + TSV, } #[derive(Clone, Debug, Deserialize)] @@ -72,6 +84,8 @@ impl Default for Settings { display_pretty_sql: true, progress_color: "cyan".to_string(), prompt: "bendsql> ".to_string(), + output_format: OutputFormat::Table, + show_progress: false, } } } diff --git a/cli/src/display.rs b/cli/src/display.rs index b1e8bbc2c..88556247b 100644 --- a/cli/src/display.rs +++ b/cli/src/display.rs @@ -25,7 +25,11 @@ use tokio::time::Instant; use indicatif::{HumanBytes, ProgressBar, ProgressState, ProgressStyle}; -use crate::{ast::format_query, config::Settings, helper::CliHelper}; +use crate::{ + ast::format_query, + config::{OutputFormat, Settings}, + helper::CliHelper, +}; #[async_trait::async_trait] pub trait ChunkDisplay { @@ -97,9 +101,10 @@ impl<'a> ChunkDisplay for ReplDisplay<'a> { if let Some(pb) = self.progress.take() { pb.finish_and_clear(); } - print_rows(self.schema.clone(), &rows, self.settings)?; - - println!(); + if !rows.is_empty() { + println!("{}", create_table(self.schema.clone(), &rows)?); + println!(); + } let rows_str = if self.rows > 1 { "rows" } else { "row" }; println!( @@ -123,8 +128,8 @@ impl<'a> ChunkDisplay for ReplDisplay<'a> { } pub struct FormatDisplay<'a> { - _settings: &'a Settings, - _schema: SchemaRef, + settings: &'a Settings, + schema: SchemaRef, data: RowProgressIterator, rows: usize, @@ -140,8 +145,8 @@ impl<'a> FormatDisplay<'a> { data: RowProgressIterator, ) -> Self { Self { - _settings: settings, - _schema: schema, + settings, + schema, data, rows: 0, _progress: None, @@ -167,13 +172,32 @@ impl<'a> ChunkDisplay for FormatDisplay<'a> { } } } - let mut wtr = csv::WriterBuilder::new() - .delimiter(b'\t') - .quote_style(csv::QuoteStyle::Necessary) - .from_writer(std::io::stdout()); - for row in rows { - let values: Vec = row.values().iter().map(|v| v.to_string()).collect(); - wtr.write_record(values)?; + if rows.is_empty() { + return Ok(()); + } + match self.settings.output_format { + OutputFormat::Table => { + println!("{}", create_table(self.schema.clone(), &rows)?); + } + OutputFormat::CSV => { + let mut wtr = csv::WriterBuilder::new() + .quote_style(csv::QuoteStyle::Necessary) + .from_writer(std::io::stdout()); + for row in rows { + let record = row.into_iter().map(|v| v.to_string()).collect::>(); + wtr.write_record(record)?; + } + } + OutputFormat::TSV => { + let mut wtr = csv::WriterBuilder::new() + .delimiter(b'\t') + .quote_style(csv::QuoteStyle::Necessary) + .from_writer(std::io::stdout()); + for row in rows { + let record = row.into_iter().map(|v| v.to_string()).collect::>(); + wtr.write_record(record)?; + } + } } Ok(()) } @@ -183,36 +207,26 @@ impl<'a> ChunkDisplay for FormatDisplay<'a> { } } -fn print_rows(schema: SchemaRef, results: &[Row], _settings: &Settings) -> Result<()> { - if !results.is_empty() { - println!("{}", create_table(schema, results)?); - } - Ok(()) -} - fn format_read_progress(progress: &QueryProgress, elapsed: f64) -> String { - let mut s = String::new(); - s.push_str(&format!( - "{} rows, {} processed, ({} rows/s, {}/s)", + format!( + "Processing {}/{} ({} rows/s), {}/{} ({}/s)", + humanize_count(progress.read_rows as f64), humanize_count(progress.total_rows as f64), + humanize_count(progress.read_rows as f64 / elapsed), + HumanBytes(progress.read_bytes as u64), HumanBytes(progress.total_bytes as u64), - humanize_count(progress.total_rows as f64 / elapsed), - HumanBytes((progress.total_bytes as f64 / elapsed) as u64) - )); - s + HumanBytes((progress.read_bytes as f64 / elapsed) as u64) + ) } -// TODO:(everpcpc) -fn _format_write_progress(progress: &QueryProgress, elapsed: f64) -> String { - let mut s = String::new(); - s.push_str(&format!( - "{} rows, {} written, ({} rows/s., {}/s.)", +pub fn format_write_progress(progress: &QueryProgress, elapsed: f64) -> String { + format!( + "Written {} ({} rows/s), {} ({}/s)", humanize_count(progress.write_rows as f64), - HumanBytes(progress.write_bytes as u64), humanize_count(progress.write_rows as f64 / elapsed), + HumanBytes(progress.write_bytes as u64), HumanBytes((progress.write_bytes as f64 / elapsed) as u64) - )); - s + ) } fn display_read_progress(pb: Option, current: &QueryProgress) -> ProgressBar { diff --git a/cli/src/main.rs b/cli/src/main.rs index 0e331ba6e..9e524e630 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -24,12 +24,12 @@ use std::collections::BTreeMap; use anyhow::{anyhow, Result}; use clap::{CommandFactory, Parser, ValueEnum}; -use config::Config; +use config::{Config, OutputFormat}; /// Supported file format and options: /// https://databend.rs/doc/sql-reference/file-format-options #[derive(ValueEnum, Clone, Debug, PartialEq)] -enum InputFormat { +pub enum InputFormat { CSV, TSV, NDJSON, @@ -71,13 +71,6 @@ impl InputFormat { } } -#[derive(ValueEnum, Clone, Debug, PartialEq)] -enum OutputFormat { - Table, - CSV, - TSV, -} - #[derive(Debug, Parser, PartialEq)] // disable default help flag since it would conflict with --host #[command(author, version, about, disable_help_flag = true)] @@ -235,6 +228,8 @@ pub async fn main() -> Result<()> { conn_args.get_dsn()? } }; + config.settings.output_format = args.output; + config.settings.show_progress = args.progress; let is_repl = atty::is(atty::Stream::Stdin) && !args.non_interactive && args.query.is_none(); let mut session = session::Session::try_new(dsn, config.settings, is_repl).await?; diff --git a/cli/src/session.rs b/cli/src/session.rs index 017fb5d75..18495a4c6 100644 --- a/cli/src/session.rs +++ b/cli/src/session.rs @@ -29,7 +29,7 @@ use tokio::time::Instant; use crate::ast::{TokenKind, Tokenizer}; use crate::config::Settings; -use crate::display::{ChunkDisplay, FormatDisplay, ReplDisplay}; +use crate::display::{format_write_progress, ChunkDisplay, FormatDisplay, ReplDisplay}; use crate::helper::CliHelper; pub struct Session { @@ -39,6 +39,7 @@ pub struct Session { settings: Settings, prompt: String, + query: Option, } impl Session { @@ -69,6 +70,7 @@ impl Session { is_repl, settings, prompt, + query: None, }) } @@ -81,7 +83,6 @@ impl Session { } pub async fn handle_repl(&mut self) { - let mut query = "".to_owned(); let config = Builder::new() .completion_prompt_limit(5) .completion_type(CompletionType::Circular) @@ -92,13 +93,33 @@ impl Session { rl.load_history(&get_history_path()).ok(); loop { - match rl.readline(&self.prompt) { - Ok(line) if line.starts_with("--") => { - continue; - } + let prompt = if self.query.is_none() { + &self.prompt + } else { + " -> " + }; + match rl.readline(prompt) { Ok(line) => { - let line = line.trim_end(); - query.push_str(&line.replace("\\\n", "")); + if let Some(query) = self.append_query(&line) { + let _ = rl.add_history_entry(&query); + match self.handle_query(true, &query).await { + Ok(true) => { + break; + } + Ok(false) => {} + Err(e) => { + if e.to_string().contains("Unauthenticated") { + if let Err(e) = self.reconnect().await { + eprintln!("Reconnect error: {}", e); + } else if let Err(e) = self.handle_query(true, &query).await { + eprintln!("{}", e); + } + } else { + eprintln!("{}", e); + } + } + } + } } Err(e) => match e { ReadlineError::Io(err) => { @@ -113,44 +134,43 @@ impl Session { _ => {} }, } - if !query.is_empty() { - let _ = rl.add_history_entry(query.trim_end()); - match self.handle_query(true, &query).await { - Ok(true) => { - break; - } - Ok(false) => {} - Err(e) => { - if e.to_string().contains("Unauthenticated") { - if let Err(e) = self.reconnect().await { - eprintln!("Reconnect error: {}", e); - } else if let Err(e) = self.handle_query(true, &query).await { - eprintln!("{}", e); - } - } else { - eprintln!("{}", e); - } - } - } - } - query.clear(); } - println!("Bye"); let _ = rl.save_history(&get_history_path()); } pub async fn handle_stdin(&mut self) { let mut lines = std::io::stdin().lock().lines(); - // TODO support multi line while let Some(Ok(line)) = lines.next() { - let line = line.trim_end(); - if line.is_empty() { - continue; + if let Some(query) = self.append_query(&line) { + if let Err(e) = self.handle_query(false, &query).await { + eprintln!("{}", e); + } } - if let Err(e) = self.handle_query(false, line).await { - eprintln!("{}", e); + } + } + + fn append_query(&mut self, line: &str) -> Option { + let line = line.trim(); + if line.is_empty() { + return None; + } + if line.starts_with("--") { + return None; + } + let query = match self.query.take() { + Some(mut query) => { + query.push(' '); + query.push_str(&line.replace("\\\n", "")); + query } + None => line.to_string(), + }; + if query.ends_with(';') { + Some(query) + } else { + self.query = Some(query); + None } } @@ -160,9 +180,7 @@ impl Session { } let start = Instant::now(); - let kind = QueryKind::from(query); - match kind { QueryKind::Update => { let affected = self.conn.exec(query).await?; @@ -222,19 +240,26 @@ impl Session { pub async fn stream_load_file( &mut self, query: &str, - file: &Path, + file_path: &Path, options: BTreeMap<&str, &str>, ) -> Result<()> { - let _start = Instant::now(); - let file = File::open(file).await?; + let start = Instant::now(); + let file = File::open(file_path).await?; let metadata = file.metadata().await?; - let _progress = self + let progress = self .conn .stream_load(query, Box::new(file), metadata.len(), Some(options), None) .await?; // TODO:(everpcpc) show progress + if self.settings.show_progress { + eprintln!( + "==> Stream Loaded {}:\n {}", + file_path.display(), + format_write_progress(&progress, start.elapsed().as_secs_f64()) + ); + } Ok(()) } diff --git a/cli/test.sh b/cli/test.sh index 90386537e..9e0617ebb 100755 --- a/cli/test.sh +++ b/cli/test.sh @@ -36,7 +36,7 @@ for tf in cli/tests/*.{sql,sh}; do bash "${tf}" >"cli/tests/${suite}.output" 2>&1 elif [[ $tf == *.sql ]]; then suite=$(basename "${tf}" | sed -e 's#.sql##') - "${BENDSQL}" <"${tf}" >"cli/tests/${suite}.output" 2>&1 + "${BENDSQL}" --output tsv <"${tf}" >"cli/tests/${suite}.output" 2>&1 fi diff "cli/tests/${suite}.output" "cli/tests/${suite}.result" done @@ -50,7 +50,7 @@ for tf in cli/tests/"$TEST_HANDLER"/*.{sql,sh}; do bash "${tf}" >"cli/tests/${TEST_HANDLER}/${suite}.output" 2>&1 elif [[ $tf == *.sql ]]; then suite=$(basename "${tf}" | sed -e 's#.sql##') - "${BENDSQL}" <"${tf}" >"cli/tests/${TEST_HANDLER}/${suite}.output" 2>&1 + "${BENDSQL}" --output tsv <"${tf}" >"cli/tests/${TEST_HANDLER}/${suite}.output" 2>&1 fi diff "cli/tests/${TEST_HANDLER}/${suite}.output" "cli/tests/${TEST_HANDLER}/${suite}.result" done diff --git a/cli/tests/data/ontime.sql b/cli/tests/data/ontime.sql new file mode 100644 index 000000000..a5e636f32 --- /dev/null +++ b/cli/tests/data/ontime.sql @@ -0,0 +1,111 @@ +CREATE TABLE test_ontime ( + Year SMALLINT UNSIGNED, + Quarter TINYINT UNSIGNED, + Month TINYINT UNSIGNED, + DayofMonth TINYINT UNSIGNED, + DayOfWeek TINYINT UNSIGNED, + FlightDate DATE, + Reporting_Airline VARCHAR, + DOT_ID_Reporting_Airline INT, + IATA_CODE_Reporting_Airline VARCHAR, + Tail_Number VARCHAR, + Flight_Number_Reporting_Airline VARCHAR, + OriginAirportID INT, + OriginAirportSeqID INT, + OriginCityMarketID INT, + Origin VARCHAR, + OriginCityName VARCHAR, + OriginState VARCHAR, + OriginStateFips VARCHAR, + OriginStateName VARCHAR, + OriginWac INT, + DestAirportID INT, + DestAirportSeqID INT, + DestCityMarketID INT, + Dest VARCHAR, + DestCityName VARCHAR, + DestState VARCHAR, + DestStateFips VARCHAR, + DestStateName VARCHAR, + DestWac INT, + CRSDepTime INT, + DepTime INT, + DepDelay INT, + DepDelayMinutes INT, + DepDel15 INT, + DepartureDelayGroups VARCHAR, + DepTimeBlk VARCHAR, + TaxiOut INT, + WheelsOff INT, + WheelsOn INT, + TaxiIn INT, + CRSArrTime INT, + ArrTime INT, + ArrDelay INT, + ArrDelayMinutes INT, + ArrDel15 INT, + ArrivalDelayGroups INT, + ArrTimeBlk VARCHAR, + Cancelled TINYINT UNSIGNED, + CancellationCode VARCHAR, + Diverted TINYINT UNSIGNED, + CRSElapsedTime INT, + ActualElapsedTime INT, + AirTime INT, + Flights INT, + Distance INT, + DistanceGroup TINYINT UNSIGNED, + CarrierDelay INT, + WeatherDelay INT, + NASDelay INT, + SecurityDelay INT, + LateAircraftDelay INT, + FirstDepTime VARCHAR, + TotalAddGTime VARCHAR, + LongestAddGTime VARCHAR, + DivAirportLandings VARCHAR, + DivReachedDest VARCHAR, + DivActualElapsedTime VARCHAR, + DivArrDelay VARCHAR, + DivDistance VARCHAR, + Div1Airport VARCHAR, + Div1AirportID INT, + Div1AirportSeqID INT, + Div1WheelsOn VARCHAR, + Div1TotalGTime VARCHAR, + Div1LongestGTime VARCHAR, + Div1WheelsOff VARCHAR, + Div1TailNum VARCHAR, + Div2Airport VARCHAR, + Div2AirportID INT, + Div2AirportSeqID INT, + Div2WheelsOn VARCHAR, + Div2TotalGTime VARCHAR, + Div2LongestGTime VARCHAR, + Div2WheelsOff VARCHAR, + Div2TailNum VARCHAR, + Div3Airport VARCHAR, + Div3AirportID INT, + Div3AirportSeqID INT, + Div3WheelsOn VARCHAR, + Div3TotalGTime VARCHAR, + Div3LongestGTime VARCHAR, + Div3WheelsOff VARCHAR, + Div3TailNum VARCHAR, + Div4Airport VARCHAR, + Div4AirportID INT, + Div4AirportSeqID INT, + Div4WheelsOn VARCHAR, + Div4TotalGTime VARCHAR, + Div4LongestGTime VARCHAR, + Div4WheelsOff VARCHAR, + Div4TailNum VARCHAR, + Div5Airport VARCHAR, + Div5AirportID INT, + Div5AirportSeqID INT, + Div5WheelsOn VARCHAR, + Div5TotalGTime VARCHAR, + Div5LongestGTime VARCHAR, + Div5WheelsOff VARCHAR, + Div5TailNum VARCHAR +); diff --git a/cli/tests/data/ontime_200.csv.gz b/cli/tests/data/ontime_200.csv.gz new file mode 100644 index 0000000000000000000000000000000000000000..6951fa6aefa551de8845705657bc8655863f929b GIT binary patch literal 8327 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 R?1{qa{}09}KUlY70RY-zz^4EJ literal 0 HcmV?d00001 diff --git a/cli/tests/http/01-load_stdin.sh b/cli/tests/http/01-load_stdin.sh index 8da5fa4f4..e8e745cd5 100644 --- a/cli/tests/http/01-load_stdin.sh +++ b/cli/tests/http/01-load_stdin.sh @@ -10,4 +10,4 @@ SQL ${BENDSQL} --query='INSERT INTO test_books VALUES;' --format=csv --data=@- >, _copy_options: Option>, ) -> Result { - unimplemented!() + unimplemented!("stream_load is not supported in FlightSQL") } }