1use std::fmt;
12
13use crate::types::LogicalType;
14
15#[derive(Debug, Clone, PartialEq)]
22#[non_exhaustive]
23pub enum Value {
24 Null,
26 Boolean(bool),
28 TinyInt(i8),
30 SmallInt(i16),
32 Integer(i32),
34 BigInt(i64),
36 HugeInt(i128),
38 UTinyInt(u8),
40 USmallInt(u16),
42 UInteger(u32),
44 UBigInt(u64),
46 UHugeInt(u128),
48 Float(f32),
50 Double(f64),
52 Decimal {
54 unscaled: i128,
56 width: u8,
58 scale: u8,
60 },
61 Varchar(String),
63 Blob(Vec<u8>),
65 Date(i32),
67 Time(i64),
69 Timestamp(i64),
71 Interval {
77 months: i32,
79 days: i32,
81 micros: i64,
83 },
84 List {
86 element: LogicalType,
88 values: Vec<Value>,
90 },
91 Struct(Vec<(String, Value)>),
93}
94
95impl Value {
96 #[must_use]
107 pub fn footprint(&self) -> usize {
108 size_of::<Self>() + self.heap()
109 }
110
111 fn heap(&self) -> usize {
113 match self {
114 Self::Varchar(text) => text.capacity(),
115 Self::Blob(bytes) => bytes.capacity(),
116 Self::List { values, .. } => {
117 values.capacity() * size_of::<Self>() + values.iter().map(Self::heap).sum::<usize>()
118 }
119 Self::Struct(fields) => {
120 fields.capacity() * size_of::<(String, Self)>()
121 + fields
122 .iter()
123 .map(|(name, value)| name.capacity() + value.heap())
124 .sum::<usize>()
125 }
126 _ => 0,
127 }
128 }
129
130 #[must_use]
132 pub fn is_null(&self) -> bool {
133 matches!(self, Self::Null)
134 }
135
136 #[must_use]
138 pub fn logical_type(&self) -> LogicalType {
139 match self {
140 Self::Null => LogicalType::Null,
141 Self::Boolean(_) => LogicalType::Boolean,
142 Self::TinyInt(_) => LogicalType::TinyInt,
143 Self::SmallInt(_) => LogicalType::SmallInt,
144 Self::Integer(_) => LogicalType::Integer,
145 Self::BigInt(_) => LogicalType::BigInt,
146 Self::HugeInt(_) => LogicalType::HugeInt,
147 Self::UTinyInt(_) => LogicalType::UTinyInt,
148 Self::USmallInt(_) => LogicalType::USmallInt,
149 Self::UInteger(_) => LogicalType::UInteger,
150 Self::UBigInt(_) => LogicalType::UBigInt,
151 Self::UHugeInt(_) => LogicalType::UHugeInt,
152 Self::Float(_) => LogicalType::Float,
153 Self::Double(_) => LogicalType::Double,
154 Self::Decimal { width, scale, .. } => {
155 LogicalType::Decimal { width: *width, scale: *scale }
156 }
157 Self::Varchar(_) => LogicalType::Varchar,
158 Self::Blob(_) => LogicalType::Blob,
159 Self::Date(_) => LogicalType::Date,
160 Self::Time(_) => LogicalType::Time,
161 Self::Timestamp(_) => LogicalType::Timestamp,
162 Self::Interval { .. } => LogicalType::Interval,
163 Self::List { element, .. } => LogicalType::list(element.clone()),
164 Self::Struct(fields) => LogicalType::Struct(
165 fields
166 .iter()
167 .map(|(name, value)| crate::types::Field::new(name, value.logical_type()))
168 .collect(),
169 ),
170 }
171 }
172
173 #[must_use]
179 pub fn as_i64(&self) -> Option<i64> {
180 match *self {
181 Self::TinyInt(v) => Some(i64::from(v)),
182 Self::SmallInt(v) => Some(i64::from(v)),
183 Self::Integer(v) => Some(i64::from(v)),
184 Self::BigInt(v) => Some(v),
185 Self::UTinyInt(v) => Some(i64::from(v)),
186 Self::USmallInt(v) => Some(i64::from(v)),
187 Self::UInteger(v) => Some(i64::from(v)),
188 Self::UBigInt(v) => i64::try_from(v).ok(),
189 Self::HugeInt(v) => i64::try_from(v).ok(),
190 Self::UHugeInt(v) => i64::try_from(v).ok(),
191 _ => None,
192 }
193 }
194
195 #[must_use]
197 pub fn as_bool(&self) -> Option<bool> {
198 match *self {
199 Self::Boolean(v) => Some(v),
200 _ => None,
201 }
202 }
203
204 #[must_use]
206 pub fn as_str(&self) -> Option<&str> {
207 match self {
208 Self::Varchar(v) => Some(v),
209 _ => None,
210 }
211 }
212}
213
214impl fmt::Display for Value {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 match self {
217 Self::Null => f.write_str("NULL"),
218 Self::Boolean(v) => f.write_str(if *v { "true" } else { "false" }),
219 Self::TinyInt(v) => write!(f, "{v}"),
220 Self::SmallInt(v) => write!(f, "{v}"),
221 Self::Integer(v) => write!(f, "{v}"),
222 Self::BigInt(v) => write!(f, "{v}"),
223 Self::HugeInt(v) => write!(f, "{v}"),
224 Self::UTinyInt(v) => write!(f, "{v}"),
225 Self::USmallInt(v) => write!(f, "{v}"),
226 Self::UInteger(v) => write!(f, "{v}"),
227 Self::UBigInt(v) => write!(f, "{v}"),
228 Self::UHugeInt(v) => write!(f, "{v}"),
229 Self::Float(v) => write_float(f, *v),
230 Self::Double(v) => write_float(f, *v),
231 Self::Decimal { unscaled, scale, .. } => write_decimal(f, *unscaled, *scale),
232 Self::Varchar(v) => f.write_str(v),
233 Self::Blob(v) => write_blob(f, v),
234 Self::Date(v) => write_date(f, *v),
235 Self::Time(v) => write_time(f, *v),
236 Self::Timestamp(v) => write_timestamp(f, *v),
237 Self::Interval { months, days, micros } => write_interval(f, *months, *days, *micros),
238 Self::List { values, .. } => {
239 f.write_str("[")?;
240 for (index, value) in values.iter().enumerate() {
241 if index > 0 {
242 f.write_str(", ")?;
243 }
244 write!(f, "{value}")?;
245 }
246 f.write_str("]")
247 }
248 Self::Struct(fields) => {
249 f.write_str("{")?;
250 for (index, (name, value)) in fields.iter().enumerate() {
251 if index > 0 {
252 f.write_str(", ")?;
253 }
254 write!(f, "'{name}': {value}")?;
255 }
256 f.write_str("}")
257 }
258 }
259 }
260}
261
262trait Real: Copy + fmt::Display + fmt::LowerExp {
269 fn is_nan(self) -> bool;
270 fn is_infinite(self) -> bool;
271 fn is_sign_negative(self) -> bool;
272}
273
274impl Real for f32 {
275 fn is_nan(self) -> bool {
276 Self::is_nan(self)
277 }
278
279 fn is_infinite(self) -> bool {
280 Self::is_infinite(self)
281 }
282
283 fn is_sign_negative(self) -> bool {
284 Self::is_sign_negative(self)
285 }
286}
287
288impl Real for f64 {
289 fn is_nan(self) -> bool {
290 Self::is_nan(self)
291 }
292
293 fn is_infinite(self) -> bool {
294 Self::is_infinite(self)
295 }
296
297 fn is_sign_negative(self) -> bool {
298 Self::is_sign_negative(self)
299 }
300}
301
302fn write_float<T: Real>(f: &mut fmt::Formatter<'_>, value: T) -> fmt::Result {
312 if value.is_nan() {
317 return f.write_str(if value.is_sign_negative() { "-nan" } else { "nan" });
318 }
319 if value.is_infinite() {
320 return f.write_str(if value.is_sign_negative() { "-inf" } else { "inf" });
321 }
322 let scientific = format!("{value:e}");
323 let (mantissa, exponent) = scientific.split_once('e').unwrap_or((scientific.as_str(), "0"));
324 let exponent: i32 = exponent.parse().unwrap_or(0);
325 if (-4..16).contains(&exponent) {
326 let text = format!("{value}");
327 if text.contains('.') {
328 return f.write_str(&text);
329 }
330 return write!(f, "{text}.0");
331 }
332 let sign = if exponent < 0 { '-' } else { '+' };
333 write!(f, "{mantissa}e{sign}{:02}", exponent.abs())
334}
335
336fn write_decimal(f: &mut fmt::Formatter<'_>, unscaled: i128, scale: u8) -> fmt::Result {
337 if scale == 0 {
338 return write!(f, "{unscaled}");
339 }
340 let negative = unscaled < 0;
341 let digits = unscaled.unsigned_abs().to_string();
343 let scale = usize::from(scale);
344 let (whole, fraction) = if digits.len() > scale {
345 let split = digits.len() - scale;
346 (digits[..split].to_string(), digits[split..].to_string())
347 } else {
348 ("0".to_string(), format!("{:0>scale$}", digits))
349 };
350 if negative {
351 f.write_str("-")?;
352 }
353 write!(f, "{whole}.{fraction}")
354}
355
356fn write_blob(f: &mut fmt::Formatter<'_>, bytes: &[u8]) -> fmt::Result {
363 for &byte in bytes {
364 if (byte.is_ascii_graphic() || byte == b' ') && !matches!(byte, b'\\' | b'\'' | b'"') {
365 write!(f, "{}", byte as char)?;
366 } else {
367 write!(f, "\\x{byte:02X}")?;
368 }
369 }
370 Ok(())
371}
372
373#[must_use]
380pub fn civil_from_days(days: i32) -> (i32, u32, u32) {
381 let z = i64::from(days) + 719_468;
382 let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
383 let day_of_era = z - era * 146_097;
384 let year_of_era =
385 (day_of_era - day_of_era / 1460 + day_of_era / 36_524 - day_of_era / 146_096) / 365;
386 let year = year_of_era + era * 400;
387 let day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
388 let shifted_month = (5 * day_of_year + 2) / 153;
389 let day = day_of_year - (153 * shifted_month + 2) / 5 + 1;
390 let month = if shifted_month < 10 { shifted_month + 3 } else { shifted_month - 9 };
391 let year = if month <= 2 { year + 1 } else { year };
392 #[expect(clippy::cast_possible_truncation, reason = "the ranges are 1 to 12 and 1 to 31")]
393 (year as i32, month as u32, day as u32)
394}
395
396#[must_use]
411pub fn interval_micros(months: i32, days: i32, micros: i64) -> i128 {
412 const MICROS_PER_DAY: i128 = 86_400 * 1_000_000;
413 const DAYS_PER_MONTH: i128 = 30;
414 (i128::from(months) * DAYS_PER_MONTH + i128::from(days)) * MICROS_PER_DAY + i128::from(micros)
415}
416
417#[must_use]
419pub fn days_from_civil(year: i32, month: u32, day: u32) -> i32 {
420 let year = i64::from(year) - i64::from(month <= 2);
421 let era = if year >= 0 { year } else { year - 399 } / 400;
422 let year_of_era = year - era * 400;
423 let month = i64::from(month);
424 let shifted_month = if month > 2 { month - 3 } else { month + 9 };
425 let day_of_year = (153 * shifted_month + 2) / 5 + i64::from(day) - 1;
426 let day_of_era = year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year;
427 #[expect(clippy::cast_possible_truncation, reason = "a date in i32 range stays in i32 range")]
428 ((era * 146_097 + day_of_era - 719_468) as i32)
429}
430
431fn write_date(f: &mut fmt::Formatter<'_>, days: i32) -> fmt::Result {
438 let (year, month, day) = civil_from_days(days);
439 if year <= 0 {
440 write!(f, "{:04}-{month:02}-{day:02} (BC)", 1 - year)
441 } else {
442 write!(f, "{year:04}-{month:02}-{day:02}")
443 }
444}
445
446fn write_time(f: &mut fmt::Formatter<'_>, micros: i64) -> fmt::Result {
447 let seconds = micros.div_euclid(1_000_000);
448 let fraction = micros.rem_euclid(1_000_000);
449 let (hours, minutes, seconds) = (seconds / 3600, (seconds / 60) % 60, seconds % 60);
450 write!(f, "{hours:02}:{minutes:02}:{seconds:02}")?;
451 if fraction != 0 {
452 let text = format!("{fraction:06}");
454 write!(f, ".{}", text.trim_end_matches('0'))?;
455 }
456 Ok(())
457}
458
459fn write_timestamp(f: &mut fmt::Formatter<'_>, micros: i64) -> fmt::Result {
460 const MICROS_PER_DAY: i64 = 86_400 * 1_000_000;
461 let days = micros.div_euclid(MICROS_PER_DAY);
462 let within_day = micros.rem_euclid(MICROS_PER_DAY);
463 let Ok(days) = i32::try_from(days) else {
464 return f.write_str("timestamp out of range");
465 };
466 write_date(f, days)?;
467 f.write_str(" ")?;
468 write_time(f, within_day)
469}
470
471fn write_interval(f: &mut fmt::Formatter<'_>, months: i32, days: i32, micros: i64) -> fmt::Result {
472 let mut wrote = false;
473 let space = |f: &mut fmt::Formatter<'_>, wrote: &mut bool| -> fmt::Result {
474 if *wrote {
475 f.write_str(" ")?;
476 }
477 *wrote = true;
478 Ok(())
479 };
480 let (years, rest_months) = (months / 12, months % 12);
481 if years != 0 {
482 space(f, &mut wrote)?;
483 write!(f, "{years} year{}", plural(years))?;
484 }
485 if rest_months != 0 {
486 space(f, &mut wrote)?;
487 write!(f, "{rest_months} month{}", plural(rest_months))?;
488 }
489 if days != 0 {
490 space(f, &mut wrote)?;
491 write!(f, "{days} day{}", plural(days))?;
492 }
493 if micros != 0 || !wrote {
494 space(f, &mut wrote)?;
495 if micros < 0 {
496 f.write_str("-")?;
497 }
498 write_time(f, micros.abs())?;
499 }
500 Ok(())
501}
502
503fn plural(n: i32) -> &'static str {
504 if n == 1 || n == -1 { "" } else { "s" }
505}
506
507#[cfg(test)]
508mod tests {
509 use super::{Value, civil_from_days, days_from_civil};
510 use crate::types::LogicalType;
511
512 #[test]
513 fn a_value_knows_its_own_type() {
514 assert_eq!(Value::Integer(1).logical_type(), LogicalType::Integer);
515 assert_eq!(Value::Null.logical_type(), LogicalType::Null);
516 let list = Value::List { element: LogicalType::Varchar, values: Vec::new() };
517 assert_eq!(list.logical_type(), LogicalType::list(LogicalType::Varchar));
520 }
521
522 #[test]
523 fn the_date_conversion_is_its_own_inverse() {
524 for days in days_from_civil(1600, 1, 1)..days_from_civil(2400, 1, 1) {
527 let (year, month, day) = civil_from_days(days);
528 assert_eq!(days_from_civil(year, month, day), days, "{year}-{month}-{day}");
529 }
530 }
531
532 #[test]
533 fn the_epoch_is_where_it_should_be() {
534 assert_eq!(days_from_civil(1970, 1, 1), 0);
535 assert_eq!(civil_from_days(0), (1970, 1, 1));
536 assert_eq!(Value::Date(0).to_string(), "1970-01-01");
537 assert_eq!(Value::Date(19_723).to_string(), "2024-01-01");
538 assert_eq!(Value::Date(19_737).to_string(), "2024-01-15");
539 }
540
541 #[test]
542 fn a_leap_day_is_a_day() {
543 assert_eq!(civil_from_days(days_from_civil(2024, 2, 29)), (2024, 2, 29));
544 assert_eq!(days_from_civil(1900, 3, 1) - days_from_civil(1900, 2, 28), 1);
547 assert_eq!(days_from_civil(2000, 3, 1) - days_from_civil(2000, 2, 28), 2);
548 }
549
550 #[test]
551 fn a_year_at_or_before_zero_prints_in_the_era_before_christ() {
552 let date = |year, month, day| Value::Date(days_from_civil(year, month, day)).to_string();
553 assert_eq!(date(1, 1, 1), "0001-01-01");
556 assert_eq!(date(0, 1, 1), "0001-01-01 (BC)");
557 assert_eq!(date(0, 12, 31), "0001-12-31 (BC)");
558 assert_eq!(date(-1, 1, 1), "0002-01-01 (BC)");
559 assert_eq!(date(-2020, 3, 4), "2021-03-04 (BC)");
560 let timestamp = |year, month, day| {
561 Value::Timestamp(i64::from(days_from_civil(year, month, day)) * 86_400 * 1_000_000)
562 .to_string()
563 };
564 assert_eq!(timestamp(0, 1, 1), "0001-01-01 (BC) 00:00:00");
565 }
566
567 #[test]
568 fn times_print_with_the_trailing_zeros_trimmed() {
569 assert_eq!(Value::Time(0).to_string(), "00:00:00");
570 assert_eq!(Value::Time(3_723_000_000).to_string(), "01:02:03");
571 assert_eq!(Value::Time(3_723_500_000).to_string(), "01:02:03.5");
572 assert_eq!(Value::Time(3_723_000_001).to_string(), "01:02:03.000001");
573 }
574
575 #[test]
576 fn a_timestamp_before_the_epoch_borrows_from_the_day() {
577 assert_eq!(Value::Timestamp(-1).to_string(), "1969-12-31 23:59:59.999999");
580 assert_eq!(Value::Timestamp(0).to_string(), "1970-01-01 00:00:00");
581 }
582
583 #[test]
584 fn a_decimal_prints_at_its_scale() {
585 let d = |unscaled, scale| Value::Decimal { unscaled, width: 18, scale }.to_string();
586 assert_eq!(d(1234, 2), "12.34");
587 assert_eq!(d(-1234, 2), "-12.34");
588 assert_eq!(d(5, 3), "0.005");
589 assert_eq!(d(-5, 3), "-0.005");
590 assert_eq!(d(1234, 0), "1234");
591 assert_eq!(d(1_000_000, 6), "1.000000");
592 }
593
594 #[test]
595 fn a_float_keeps_the_point_that_says_it_is_one() {
596 assert_eq!(Value::Double(1.0).to_string(), "1.0");
597 assert_eq!(Value::Double(-3.0).to_string(), "-3.0");
598 assert_eq!(Value::Double(1.5).to_string(), "1.5");
599 assert_eq!(Value::Double(-0.0).to_string(), "-0.0");
600 assert_eq!(Value::Float(0.5).to_string(), "0.5");
601 }
602
603 #[test]
604 fn a_float_is_printed_from_its_own_width_rather_than_widened_first() {
605 assert_eq!(Value::Float(0.1).to_string(), "0.1");
608 assert_eq!(Value::Float(1.0).to_string(), "1.0");
609 }
610
611 #[test]
612 fn a_float_switches_to_an_exponent_where_duckdb_switches() {
613 assert_eq!(Value::Double(1e15).to_string(), "1000000000000000.0");
614 assert_eq!(Value::Double(1e16).to_string(), "1e+16");
615 assert_eq!(Value::Double(1e20).to_string(), "1e+20");
616 assert_eq!(Value::Double(1e-4).to_string(), "0.0001");
617 assert_eq!(Value::Double(1e-5).to_string(), "1e-05");
618 assert_eq!(Value::Double(1.234_567_890_123_456_8e17).to_string(), "1.2345678901234568e+17");
619 }
620
621 #[test]
622 fn a_float_that_is_not_a_number_says_so_the_way_duckdb_says_it() {
623 assert_eq!(Value::Double(f64::INFINITY).to_string(), "inf");
624 assert_eq!(Value::Double(f64::NEG_INFINITY).to_string(), "-inf");
625 assert_eq!(Value::Double(f64::NAN).to_string(), "nan");
626 assert_eq!(Value::Double(-f64::NAN).to_string(), "-nan");
631 assert_eq!(Value::Float(-f32::NAN).to_string(), "-nan");
632 }
633
634 #[test]
635 fn an_interval_keeps_months_days_and_micros_apart() {
636 let i = |months, days, micros| Value::Interval { months, days, micros }.to_string();
637 assert_eq!(i(14, 3, 3_723_000_000), "1 year 2 months 3 days 01:02:03");
638 assert_eq!(i(1, 0, 0), "1 month");
639 assert_eq!(i(0, 0, 0), "00:00:00");
640 assert_eq!(i(0, 0, -1_000_000), "-00:00:01");
641 }
642
643 #[test]
644 fn a_blob_escapes_what_is_not_printable() {
645 assert_eq!(Value::Blob(b"ok".to_vec()).to_string(), "ok");
646 assert_eq!(Value::Blob(vec![0, 1, b'a']).to_string(), "\\x00\\x01a");
647 assert_eq!(Value::Blob(vec![0x7f, 0xff]).to_string(), "\\x7F\\xFF");
648 assert_eq!(Value::Blob(br#"'"\"#.to_vec()).to_string(), "\\x27\\x22\\x5C");
650 assert_eq!(Value::Blob(b" &`~".to_vec()).to_string(), " &`~");
651 }
652
653 #[test]
654 fn a_limit_that_does_not_fit_is_none_rather_than_clamped() {
655 assert_eq!(Value::Integer(5).as_i64(), Some(5));
656 assert_eq!(Value::UBigInt(u64::MAX).as_i64(), None);
657 assert_eq!(Value::Varchar("5".into()).as_i64(), None);
658 }
659
660 #[test]
661 fn a_footprint_is_the_value_plus_what_it_owns() {
662 let bare = Value::Integer(1).footprint();
663 assert_eq!(bare, size_of::<Value>(), "a number owns nothing");
664 assert_eq!(
665 Value::Boolean(true).footprint(),
666 bare,
667 "the enum is one width whatever is in it"
668 );
669 let text = "a string long enough to be on the heap in any implementation".to_string();
670 assert_eq!(Value::Varchar(text.clone()).footprint(), bare + text.capacity());
671 let list = Value::List {
672 element: LogicalType::Varchar,
673 values: vec![Value::Varchar(text.clone())],
674 };
675 assert_eq!(list.footprint(), bare + size_of::<Value>() + text.capacity());
679 }
680}