diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 9e2f5ba23..b9754102f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -25,7 +25,7 @@ clap = { version = "4.5", features = ["derive", "env"] } comfy-table = "7.1" csv = "1.3" ctrlc = { version = "3.4.6", features = ["termination"] } -databend-common-ast = "0.2.3" +databend-common-ast = "0.2.4" dirs = "5.0" fern = { version = "0.7", features = ["colored"] } indicatif = "0.18.3" diff --git a/cli/src/display.rs b/cli/src/display.rs index fd207aff1..87961633b 100644 --- a/cli/src/display.rs +++ b/cli/src/display.rs @@ -593,7 +593,7 @@ fn create_table( && results[0].values().iter().all(|v| { if matches!(v, Value::Number(_)) { let f: f64 = v.to_string().parse().unwrap(); - f >= 1_000_000f64 + (1_000_000f64..=1e+21f64).contains(&f) } else { false } @@ -720,6 +720,7 @@ fn format_table_style( ) -> Cell { let is_null = matches!(value, Value::Null); let is_string = matches!(value, Value::String(_)); + let is_number = matches!(value, Value::Number(_)); let mut value_str = value.to_string(); if is_string && quote_string { let mut escaped_value_str = String::with_capacity(value_str.len()); @@ -737,6 +738,9 @@ fn format_table_style( value_str = escaped_value_str; } value_str = truncate_string(value_str, max_col_width); + if !is_number { + value_str = truncate_string(value_str, max_col_width); + } if is_string && quote_string { value_str = format!("'{value_str}'"); } diff --git a/cli/tests/00-base.result b/cli/tests/00-base.result index 219cfe541..080ec661b 100644 --- a/cli/tests/00-base.result +++ b/cli/tests/00-base.result @@ -26,6 +26,7 @@ aa "def add(a, b): a + b" 3.00 3.00 0.0000000170141183460469231731687303715884105727000 -0.0000000170141183460469231731687303715884105727000 +1.0234567899999998e+81 1.2345678901234568e-11 NaN inf Asia/Shanghai 3 0 0.00 diff --git a/cli/tests/00-base.sql b/cli/tests/00-base.sql index f618dfa4a..f541cbd4d 100644 --- a/cli/tests/00-base.sql +++ b/cli/tests/00-base.sql @@ -47,6 +47,7 @@ select 'in comment block'; */ select 1.00 + 2.00, 3.00, 0.0000000170141183460469231731687303715884105727000, -0.0000000170141183460469231731687303715884105727000; +select 10.234567899999999e80, 1234567890123456789012345678901234567890123456789012345678901234567890.1e-80, CAST('NaN' AS FLOAT), CAST('inf' AS DOUBLE); select/*+ SET_VAR(timezone='Asia/Shanghai') */ timezone(); diff --git a/sql/Cargo.toml b/sql/Cargo.toml index f9a27a54f..51c58cca6 100644 --- a/sql/Cargo.toml +++ b/sql/Cargo.toml @@ -37,3 +37,4 @@ jiff = { workspace = true } serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = { version = "1.0", default-features = false, features = ["std", "raw_value"] } url = { version = "2.5", default-features = false } +zmij = "1.0" diff --git a/sql/src/cursor_ext/cursor_read_number_ext.rs b/sql/src/cursor_ext/cursor_read_number_ext.rs index 43f71fd43..2669bdb1f 100644 --- a/sql/src/cursor_ext/cursor_read_number_ext.rs +++ b/sql/src/cursor_ext/cursor_read_number_ext.rs @@ -99,6 +99,17 @@ pub fn collect_number(buffer: &[u8]) -> (usize, usize) { (index, effective) } +fn collect_special_float(buffer: &[u8]) -> Option { + const SPECIAL_FLOATS: [&[u8]; 5] = [b"infinity", b"-infinity", b"inf", b"-inf", b"nan"]; + + for literal in SPECIAL_FLOATS { + if buffer.len() >= literal.len() && buffer[..literal.len()].eq_ignore_ascii_case(literal) { + return Some(literal.len()); + } + } + None +} + #[inline] fn read_num_text_exact(buf: &[u8]) -> Result { match FromLexical::from_lexical(buf) { @@ -135,12 +146,17 @@ where fn read_float_text(&mut self) -> Result { let buf = self.fill_buf()?; - let (n_in, n_out) = collect_number(buf); - if n_in == 0 { - return Err(std::io::Error::new( - ErrorKind::InvalidData, - "Unable to parse float: provided text is not in a recognizable floating-point format.".to_string() - )); + let (mut n_in, mut n_out) = collect_number(buf); + if n_in == 0 || (n_in == 1 && buf.first() == Some(&b'-')) { + if let Some(n_special) = collect_special_float(buf) { + n_in = n_special; + n_out = n_special; + } else { + return Err(std::io::Error::new( + ErrorKind::InvalidData, + "Unable to parse float: provided text is not in a recognizable floating-point format.".to_string() + )); + } } let buf = self.fill_buf()?; let n = read_num_text_exact(&buf[..n_out])?; diff --git a/sql/src/value/format/display.rs b/sql/src/value/format/display.rs index fb1da48f3..8b6528325 100644 --- a/sql/src/value/format/display.rs +++ b/sql/src/value/format/display.rs @@ -36,8 +36,8 @@ impl std::fmt::Display for NumberValue { NumberValue::UInt16(i) => write!(f, "{i}"), NumberValue::UInt32(i) => write!(f, "{i}"), NumberValue::UInt64(i) => write!(f, "{i}"), - NumberValue::Float32(i) => write!(f, "{i}"), - NumberValue::Float64(i) => write!(f, "{i}"), + NumberValue::Float32(i) => display_float(*i, f), + NumberValue::Float64(i) => display_float(*i, f), NumberValue::Decimal64(v, s) => { write!(f, "{}", display_decimal_128(*v as i128, s.scale)) } @@ -143,11 +143,13 @@ impl Value { } Value::Vector(vals) => { write!(f, "[")?; + let mut buffer = zmij::Buffer::new(); for (i, val) in vals.iter().enumerate() { if i > 0 { write!(f, ",")?; } - write!(f, "{val}")?; + let s = buffer.format_finite(*val); + write!(f, "{s}")?; } write!(f, "]")?; Ok(()) @@ -156,6 +158,58 @@ impl Value { } } +trait FloatForDisplay: Copy { + fn is_nan(self) -> bool; + fn is_infinite(self) -> bool; + fn is_sign_negative(self) -> bool; + fn write_with(self, buf: &mut zmij::Buffer) -> &str; +} + +impl FloatForDisplay for f32 { + fn is_nan(self) -> bool { + f32::is_nan(self) + } + fn is_infinite(self) -> bool { + f32::is_infinite(self) + } + fn is_sign_negative(self) -> bool { + f32::is_sign_negative(self) + } + fn write_with(self, buf: &mut zmij::Buffer) -> &str { + buf.format_finite(self) + } +} + +impl FloatForDisplay for f64 { + fn is_nan(self) -> bool { + f64::is_nan(self) + } + fn is_infinite(self) -> bool { + f64::is_infinite(self) + } + fn is_sign_negative(self) -> bool { + f64::is_sign_negative(self) + } + fn write_with(self, buf: &mut zmij::Buffer) -> &str { + buf.format_finite(self) + } +} + +fn display_float(num: T, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if num.is_nan() { + write!(f, "NaN") + } else if num.is_infinite() { + if num.is_sign_negative() { + write!(f, "-inf") + } else { + write!(f, "inf") + } + } else { + let mut buffer = zmij::Buffer::new(); + write!(f, "{}", num.write_with(&mut buffer)) + } +} + pub fn display_decimal_128(num: i128, scale: u8) -> String { let mut buf = String::new(); if scale == 0 {