1use std::fmt;
4
5use crate::sql_value::SqlValue;
6
7fn format_f64(n: f64) -> String {
13 if n.is_nan() {
14 return "NaN".to_string();
15 }
16 if n.is_infinite() {
19 return if n > 0.0 { "Inf".to_string() } else { "-Inf".to_string() };
20 }
21
22 let n = if n == 0.0 { 0.0 } else { n };
24
25 let abs_n = n.abs();
26
27 if abs_n >= 1e15 || (abs_n < 1e-4 && abs_n != 0.0) {
31 let s = format!("{:.14e}", n);
33 return format_scientific_sqlite(&s);
34 }
35
36 format_with_significant_digits(n, 15)
39}
40
41fn format_with_significant_digits(n: f64, sig_digits: usize) -> String {
45 if n == 0.0 {
46 return "0.0".to_string();
47 }
48
49 let log10_abs = n.abs().log10();
51 let integer_digits = if log10_abs >= 0.0 { log10_abs.floor() as i32 + 1 } else { 0 };
52 let decimal_places = (sig_digits as i32 - integer_digits).max(0) as usize;
53
54 let formatted = format!("{:.prec$}", n, prec = decimal_places);
56
57 if formatted.contains('.') {
59 let trimmed = formatted.trim_end_matches('0');
60 if trimmed.ends_with('.') {
61 format!("{}0", trimmed)
62 } else {
63 trimmed.to_string()
64 }
65 } else {
66 format!("{}.0", formatted)
68 }
69}
70
71fn format_scientific_sqlite(s: &str) -> String {
77 if let Some(e_pos) = s.find('e') {
80 let (mantissa, exp_part) = s.split_at(e_pos);
81 let exp_str = &exp_part[1..]; let mantissa = mantissa.trim_end_matches('0').trim_end_matches('.');
85
86 let (sign, exp_digits) = if let Some(stripped) = exp_str.strip_prefix('-') {
87 ("-", stripped)
88 } else if let Some(stripped) = exp_str.strip_prefix('+') {
89 ("+", stripped)
90 } else {
91 ("+", exp_str)
92 };
93
94 let exp_num: i32 = exp_digits.parse().unwrap_or(0);
96 format!("{}e{}{:02}", mantissa, sign, exp_num.abs())
97 } else {
98 s.to_string()
99 }
100}
101
102fn format_f32(n: f32) -> String {
107 if n.is_nan() {
108 return "NaN".to_string();
109 }
110 if n.is_infinite() {
113 return if n > 0.0 { "Inf".to_string() } else { "-Inf".to_string() };
114 }
115
116 let n = if n == 0.0 { 0.0 } else { n };
118
119 let abs_n = n.abs();
120
121 if abs_n >= 1e15 || (abs_n < 1e-4 && abs_n != 0.0) {
125 let s = format!("{:.14e}", n);
127 return format_scientific_sqlite(&s);
128 }
129
130 let mut buffer = ryu::Buffer::new();
132 let s = buffer.format(n);
133
134 s.to_string()
136}
137
138impl fmt::Display for SqlValue {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 match self {
142 SqlValue::Integer(i) => write!(f, "{}", i),
145 SqlValue::Smallint(i) => write!(f, "{}", i),
146 SqlValue::Bigint(i) => write!(f, "{}", i),
147 SqlValue::Unsigned(u) => write!(f, "{}", u),
148 SqlValue::Numeric(n) => write!(f, "{}", format_f64(*n)),
151 SqlValue::Float(n) => write!(f, "{}", format_f32(*n)),
152 SqlValue::Real(n) => write!(f, "{}", format_f64(*n)),
153 SqlValue::Double(n) => write!(f, "{}", format_f64(*n)),
154 SqlValue::Character(s) => write!(f, "{}", s),
155 SqlValue::Varchar(s) => write!(f, "{}", s),
156 SqlValue::Boolean(true) => write!(f, "TRUE"),
157 SqlValue::Boolean(false) => write!(f, "FALSE"),
158 SqlValue::Date(s) => write!(f, "{}", s),
159 SqlValue::Time(s) => write!(f, "{}", s),
160 SqlValue::Timestamp(s) => write!(f, "{}", s),
161 SqlValue::Interval(s) => write!(f, "{}", s),
162 SqlValue::Vector(v) => {
163 let formatted: Vec<String> = v.iter().map(|x| x.to_string()).collect();
165 write!(f, "[{}]", formatted.join(", "))
166 }
167 SqlValue::Blob(b) => {
168 if let Ok(s) = std::str::from_utf8(b) {
171 write!(f, "{}", s)
172 } else {
173 for byte in b {
175 write!(f, "{:02X}", byte)?;
176 }
177 Ok(())
178 }
179 }
180 SqlValue::Null => write!(f, "NULL"),
181 }
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn test_format_f64_helper() {
191 assert_eq!(format_f64(1.1), "1.1");
193 assert_eq!(format_f64(2.2), "2.2");
194 assert_eq!(format_f64(1.0), "1.0");
195 assert_eq!(format_f64(2.0), "2.0");
196 assert_eq!(format_f64(0.0), "0.0");
197 assert_eq!(format_f64(123.456), "123.456");
198 assert_eq!(format_f64(0.5), "0.5");
199 assert_eq!(format_f64(100.0), "100.0");
200 assert_eq!(format_f64(-4373.0), "-4373.0");
201 assert_eq!(format_f64(-4373.123), "-4373.123");
202 }
203
204 #[test]
205 fn test_format_f32_helper() {
206 assert_eq!(format_f32(1.1f32), "1.1");
209 assert_eq!(format_f32(2.2f32), "2.2");
210 assert_eq!(format_f32(1.0f32), "1.0");
211 assert_eq!(format_f32(0.0f32), "0.0");
212 assert_eq!(format_f32(123.456f32), "123.456");
213 assert_eq!(format_f32(0.5f32), "0.5");
214 assert_eq!(format_f32(100.0f32), "100.0");
215 assert_eq!(format_f32(-4373.0f32), "-4373.0");
216 }
217
218 #[test]
219 fn test_format_f64_scientific() {
220 assert_eq!(format_f64(1e15), "1e+15");
222 assert_eq!(format_f64(1e16), "1e+16");
223 assert_eq!(format_f64(0.00001), "1e-05");
225 assert_eq!(format_f64(1e-10), "1e-10");
226 }
227
228 #[test]
229 fn test_format_scientific_sqlite() {
230 assert_eq!(format_scientific_sqlite("1e15"), "1e+15");
232 assert_eq!(format_scientific_sqlite("1e5"), "1e+05");
233 assert_eq!(format_scientific_sqlite("1.5e-5"), "1.5e-05");
234 assert_eq!(format_scientific_sqlite("1.5e-15"), "1.5e-15");
235 assert_eq!(format_scientific_sqlite("9.22337203685478e18"), "9.22337203685478e+18");
236 }
237
238 #[test]
239 fn test_numeric_display_whole_numbers() {
240 assert_eq!(format!("{}", SqlValue::Numeric(32.0)), "32.0");
242 assert_eq!(format!("{}", SqlValue::Numeric(-4373.0)), "-4373.0");
243 assert_eq!(format!("{}", SqlValue::Numeric(0.0)), "0.0");
244 assert_eq!(format!("{}", SqlValue::Numeric(164.0)), "164.0");
245 }
246
247 #[test]
248 fn test_numeric_display_fractional() {
249 assert_eq!(format!("{}", SqlValue::Numeric(32.5)), "32.5");
251 assert_eq!(format!("{}", SqlValue::Numeric(-4373.123)), "-4373.123");
252 assert_eq!(format!("{}", SqlValue::Numeric(0.5)), "0.5");
253 assert_eq!(format!("{}", SqlValue::Numeric(1.1)), "1.1");
254 }
255
256 #[test]
257 fn test_numeric_display_special_values() {
258 assert_eq!(format!("{}", SqlValue::Numeric(f64::NAN)), "NaN");
260 assert_eq!(format!("{}", SqlValue::Numeric(f64::INFINITY)), "Inf");
262 assert_eq!(format!("{}", SqlValue::Numeric(f64::NEG_INFINITY)), "-Inf");
263 }
264
265 #[test]
266 fn test_float_display_whole_numbers() {
267 assert_eq!(format!("{}", SqlValue::Float(32.0)), "32.0");
269 assert_eq!(format!("{}", SqlValue::Float(-4373.0)), "-4373.0");
270 assert_eq!(format!("{}", SqlValue::Float(0.0)), "0.0");
271 assert_eq!(format!("{}", SqlValue::Float(127.75)), "127.75");
272 }
273
274 #[test]
275 fn test_real_display_fractional() {
276 assert_eq!(format!("{}", SqlValue::Real(32.5)), "32.5");
279 assert_eq!(format!("{}", SqlValue::Real(0.5)), "0.5");
280 assert_eq!(format!("{}", SqlValue::Real(1.1)), "1.1");
281 assert_eq!(format!("{}", SqlValue::Real(2.2)), "2.2");
282 }
283
284 #[test]
285 fn test_double_display_special_values() {
286 assert_eq!(format!("{}", SqlValue::Double(f64::NAN)), "NaN");
288 assert_eq!(format!("{}", SqlValue::Double(f64::INFINITY)), "Inf");
290 assert_eq!(format!("{}", SqlValue::Double(f64::NEG_INFINITY)), "-Inf");
291 assert_eq!(format!("{}", SqlValue::Double(123.45)), "123.45");
292 }
293
294 #[test]
295 fn test_format_f64_whole_numbers() {
296 assert_eq!(format_f64(45.0), "45.0");
298 assert_eq!(format_f64(100.0), "100.0");
299 assert_eq!(format_f64(0.0), "0.0");
300 assert_eq!(format_f64(45.5), "45.5");
301 assert_eq!(format_f64(123.456), "123.456");
302 }
303
304 #[test]
305 fn test_format_negative_zero() {
306 assert_eq!(format_f64(-0.0), "0.0");
308 assert_eq!(format_f32(-0.0f32), "0.0");
309 assert_eq!(format_f64(0.0 * -1.0), "0.0");
311 }
312
313 #[test]
314 fn test_blob_display_utf8() {
315 assert_eq!(
318 format!("{}", SqlValue::Blob(vec![0x61, 0x62, 0x63])),
319 "abc"
320 );
321 assert_eq!(
323 format!("{}", SqlValue::Blob(vec![0x68, 0x61, 0x72, 0x65])),
324 "hare"
325 );
326 assert_eq!(
328 format!("{}", SqlValue::Blob(vec![0x68, 0x65, 0x6c, 0x6c, 0x6f])),
329 "hello"
330 );
331 }
332
333 #[test]
334 fn test_blob_display_invalid_utf8() {
335 assert_eq!(
337 format!("{}", SqlValue::Blob(vec![0xFF, 0xFE])),
338 "FFFE"
339 );
340 assert_eq!(
342 format!("{}", SqlValue::Blob(vec![0x80, 0x81, 0x82])),
343 "808182"
344 );
345 }
346
347 #[test]
348 fn test_blob_display_empty() {
349 assert_eq!(format!("{}", SqlValue::Blob(vec![])), "");
351 }
352
353 #[test]
354 fn test_integer_display_no_scientific_notation() {
355 assert_eq!(format!("{}", SqlValue::Integer(i64::MAX)), "9223372036854775807");
358 assert_eq!(format!("{}", SqlValue::Bigint(i64::MAX)), "9223372036854775807");
359 assert_eq!(format!("{}", SqlValue::Integer(i64::MIN)), "-9223372036854775808");
360 assert_eq!(format!("{}", SqlValue::Integer(1_000_000_000_000_000)), "1000000000000000");
361 assert_eq!(format!("{}", SqlValue::Integer(999_999_999_999_999)), "999999999999999");
362 assert_eq!(format!("{}", SqlValue::Integer(10_000_000_000_000_000)), "10000000000000000");
363 }
364
365 #[test]
366 fn test_small_integer_display() {
367 assert_eq!(format!("{}", SqlValue::Integer(0)), "0");
369 assert_eq!(format!("{}", SqlValue::Integer(42)), "42");
370 assert_eq!(format!("{}", SqlValue::Integer(-100)), "-100");
371 assert_eq!(format!("{}", SqlValue::Integer(1_000_000)), "1000000");
372 assert_eq!(format!("{}", SqlValue::Bigint(123456789)), "123456789");
373 }
374
375 #[test]
376 fn test_unsigned_integer_display() {
377 assert_eq!(format!("{}", SqlValue::Unsigned(u64::MAX)), "18446744073709551615");
379 assert_eq!(format!("{}", SqlValue::Unsigned(1_000_000_000_000_000)), "1000000000000000");
380 assert_eq!(format!("{}", SqlValue::Unsigned(999_999_999_999_999)), "999999999999999");
381 }
382}