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 Map {
111 key: Box<LogicalType>,
113 value: Box<LogicalType>,
115 entries: Vec<(Value, Value)>,
117 },
118}
119
120impl Value {
121 #[must_use]
127 pub fn map(key: LogicalType, value: LogicalType, entries: Vec<(Self, Self)>) -> Self {
128 Self::Map { key: Box::new(key), value: Box::new(value), entries }
129 }
130
131 #[must_use]
142 pub fn footprint(&self) -> usize {
143 size_of::<Self>() + self.heap()
144 }
145
146 fn heap(&self) -> usize {
148 match self {
149 Self::Varchar(text) => text.capacity(),
150 Self::Blob(bytes) => bytes.capacity(),
151 Self::List { values, .. } => {
152 values.capacity() * size_of::<Self>() + values.iter().map(Self::heap).sum::<usize>()
153 }
154 Self::Struct(fields) => {
155 fields.capacity() * size_of::<(String, Self)>()
156 + fields
157 .iter()
158 .map(|(name, value)| name.capacity() + value.heap())
159 .sum::<usize>()
160 }
161 Self::Map { entries, .. } => {
165 2 * size_of::<LogicalType>()
166 + entries.capacity() * size_of::<(Self, Self)>()
167 + entries.iter().map(|(key, value)| key.heap() + value.heap()).sum::<usize>()
168 }
169 _ => 0,
170 }
171 }
172
173 #[must_use]
175 pub fn is_null(&self) -> bool {
176 matches!(self, Self::Null)
177 }
178
179 #[must_use]
181 pub fn logical_type(&self) -> LogicalType {
182 match self {
183 Self::Null => LogicalType::Null,
184 Self::Boolean(_) => LogicalType::Boolean,
185 Self::TinyInt(_) => LogicalType::TinyInt,
186 Self::SmallInt(_) => LogicalType::SmallInt,
187 Self::Integer(_) => LogicalType::Integer,
188 Self::BigInt(_) => LogicalType::BigInt,
189 Self::HugeInt(_) => LogicalType::HugeInt,
190 Self::UTinyInt(_) => LogicalType::UTinyInt,
191 Self::USmallInt(_) => LogicalType::USmallInt,
192 Self::UInteger(_) => LogicalType::UInteger,
193 Self::UBigInt(_) => LogicalType::UBigInt,
194 Self::UHugeInt(_) => LogicalType::UHugeInt,
195 Self::Float(_) => LogicalType::Float,
196 Self::Double(_) => LogicalType::Double,
197 Self::Decimal { width, scale, .. } => {
198 LogicalType::Decimal { width: *width, scale: *scale }
199 }
200 Self::Varchar(_) => LogicalType::Varchar,
201 Self::Blob(_) => LogicalType::Blob,
202 Self::Date(_) => LogicalType::Date,
203 Self::Time(_) => LogicalType::Time,
204 Self::Timestamp(_) => LogicalType::Timestamp,
205 Self::Interval { .. } => LogicalType::Interval,
206 Self::List { element, .. } => LogicalType::list(element.clone()),
207 Self::Struct(fields) => LogicalType::Struct(
208 fields
209 .iter()
210 .map(|(name, value)| crate::types::Field::new(name, value.logical_type()))
211 .collect(),
212 ),
213 Self::Map { key, value, .. } => LogicalType::Map(key.clone(), value.clone()),
214 }
215 }
216
217 #[must_use]
223 pub fn as_i64(&self) -> Option<i64> {
224 match *self {
225 Self::TinyInt(v) => Some(i64::from(v)),
226 Self::SmallInt(v) => Some(i64::from(v)),
227 Self::Integer(v) => Some(i64::from(v)),
228 Self::BigInt(v) => Some(v),
229 Self::UTinyInt(v) => Some(i64::from(v)),
230 Self::USmallInt(v) => Some(i64::from(v)),
231 Self::UInteger(v) => Some(i64::from(v)),
232 Self::UBigInt(v) => i64::try_from(v).ok(),
233 Self::HugeInt(v) => i64::try_from(v).ok(),
234 Self::UHugeInt(v) => i64::try_from(v).ok(),
235 _ => None,
236 }
237 }
238
239 #[must_use]
241 pub fn as_bool(&self) -> Option<bool> {
242 match *self {
243 Self::Boolean(v) => Some(v),
244 _ => None,
245 }
246 }
247
248 #[must_use]
250 pub fn as_str(&self) -> Option<&str> {
251 match self {
252 Self::Varchar(v) => Some(v),
253 _ => None,
254 }
255 }
256}
257
258impl fmt::Display for Value {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 match self {
261 Self::Null => f.write_str("NULL"),
262 Self::Boolean(v) => f.write_str(if *v { "true" } else { "false" }),
263 Self::TinyInt(v) => write!(f, "{v}"),
264 Self::SmallInt(v) => write!(f, "{v}"),
265 Self::Integer(v) => write!(f, "{v}"),
266 Self::BigInt(v) => write!(f, "{v}"),
267 Self::HugeInt(v) => write!(f, "{v}"),
268 Self::UTinyInt(v) => write!(f, "{v}"),
269 Self::USmallInt(v) => write!(f, "{v}"),
270 Self::UInteger(v) => write!(f, "{v}"),
271 Self::UBigInt(v) => write!(f, "{v}"),
272 Self::UHugeInt(v) => write!(f, "{v}"),
273 Self::Float(v) => write_float(f, *v),
274 Self::Double(v) => write_float(f, *v),
275 Self::Decimal { unscaled, scale, .. } => write_decimal(f, *unscaled, *scale),
276 Self::Varchar(v) => f.write_str(v),
277 Self::Blob(v) => write_blob(f, v),
278 Self::Date(v) => write_date(f, *v),
279 Self::Time(v) => write_time(f, *v),
280 Self::Timestamp(v) => write_timestamp(f, *v),
281 Self::Interval { months, days, micros } => write_interval(f, *months, *days, *micros),
282 Self::List { values, .. } => {
283 f.write_str("[")?;
284 for (index, value) in values.iter().enumerate() {
285 if index > 0 {
286 f.write_str(", ")?;
287 }
288 write!(f, "{value}")?;
289 }
290 f.write_str("]")
291 }
292 Self::Struct(fields) => {
293 f.write_str("{")?;
294 for (index, (name, value)) in fields.iter().enumerate() {
295 if index > 0 {
296 f.write_str(", ")?;
297 }
298 write!(f, "'{name}': {value}")?;
299 }
300 f.write_str("}")
301 }
302 Self::Map { entries, .. } => {
306 f.write_str("{")?;
307 for (index, (key, value)) in entries.iter().enumerate() {
308 if index > 0 {
309 f.write_str(", ")?;
310 }
311 write!(f, "{key}={value}")?;
312 }
313 f.write_str("}")
314 }
315 }
316 }
317}
318
319trait Real: Copy + fmt::Display + fmt::LowerExp {
326 fn is_nan(self) -> bool;
327 fn is_infinite(self) -> bool;
328 fn is_sign_negative(self) -> bool;
329}
330
331impl Real for f32 {
332 fn is_nan(self) -> bool {
333 Self::is_nan(self)
334 }
335
336 fn is_infinite(self) -> bool {
337 Self::is_infinite(self)
338 }
339
340 fn is_sign_negative(self) -> bool {
341 Self::is_sign_negative(self)
342 }
343}
344
345impl Real for f64 {
346 fn is_nan(self) -> bool {
347 Self::is_nan(self)
348 }
349
350 fn is_infinite(self) -> bool {
351 Self::is_infinite(self)
352 }
353
354 fn is_sign_negative(self) -> bool {
355 Self::is_sign_negative(self)
356 }
357}
358
359fn write_float<T: Real>(f: &mut fmt::Formatter<'_>, value: T) -> fmt::Result {
369 if value.is_nan() {
374 return f.write_str(if value.is_sign_negative() { "-nan" } else { "nan" });
375 }
376 if value.is_infinite() {
377 return f.write_str(if value.is_sign_negative() { "-inf" } else { "inf" });
378 }
379 let scientific = format!("{value:e}");
380 let (mantissa, exponent) = scientific.split_once('e').unwrap_or((scientific.as_str(), "0"));
381 let exponent: i32 = exponent.parse().unwrap_or(0);
382 if (-4..16).contains(&exponent) {
383 let text = format!("{value}");
384 if text.contains('.') {
385 return f.write_str(&text);
386 }
387 return write!(f, "{text}.0");
388 }
389 let sign = if exponent < 0 { '-' } else { '+' };
390 write!(f, "{mantissa}e{sign}{:02}", exponent.abs())
391}
392
393fn write_decimal(f: &mut fmt::Formatter<'_>, unscaled: i128, scale: u8) -> fmt::Result {
394 if scale == 0 {
395 return write!(f, "{unscaled}");
396 }
397 let negative = unscaled < 0;
398 let digits = unscaled.unsigned_abs().to_string();
400 let scale = usize::from(scale);
401 let (whole, fraction) = if digits.len() > scale {
402 let split = digits.len() - scale;
403 (digits[..split].to_string(), digits[split..].to_string())
404 } else {
405 ("0".to_string(), format!("{:0>scale$}", digits))
406 };
407 if negative {
408 f.write_str("-")?;
409 }
410 write!(f, "{whole}.{fraction}")
411}
412
413fn write_blob(f: &mut fmt::Formatter<'_>, bytes: &[u8]) -> fmt::Result {
420 for &byte in bytes {
421 if (byte.is_ascii_graphic() || byte == b' ') && !matches!(byte, b'\\' | b'\'' | b'"') {
422 write!(f, "{}", byte as char)?;
423 } else {
424 write!(f, "\\x{byte:02X}")?;
425 }
426 }
427 Ok(())
428}
429
430#[must_use]
437pub fn civil_from_days(days: i32) -> (i32, u32, u32) {
438 let z = i64::from(days) + 719_468;
439 let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
440 let day_of_era = z - era * 146_097;
441 let year_of_era =
442 (day_of_era - day_of_era / 1460 + day_of_era / 36_524 - day_of_era / 146_096) / 365;
443 let year = year_of_era + era * 400;
444 let day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
445 let shifted_month = (5 * day_of_year + 2) / 153;
446 let day = day_of_year - (153 * shifted_month + 2) / 5 + 1;
447 let month = if shifted_month < 10 { shifted_month + 3 } else { shifted_month - 9 };
448 let year = if month <= 2 { year + 1 } else { year };
449 #[expect(clippy::cast_possible_truncation, reason = "the ranges are 1 to 12 and 1 to 31")]
450 (year as i32, month as u32, day as u32)
451}
452
453#[must_use]
468pub fn interval_micros(months: i32, days: i32, micros: i64) -> i128 {
469 const MICROS_PER_DAY: i128 = 86_400 * 1_000_000;
470 const DAYS_PER_MONTH: i128 = 30;
471 (i128::from(months) * DAYS_PER_MONTH + i128::from(days)) * MICROS_PER_DAY + i128::from(micros)
472}
473
474#[must_use]
476pub fn days_from_civil(year: i32, month: u32, day: u32) -> i32 {
477 let year = i64::from(year) - i64::from(month <= 2);
478 let era = if year >= 0 { year } else { year - 399 } / 400;
479 let year_of_era = year - era * 400;
480 let month = i64::from(month);
481 let shifted_month = if month > 2 { month - 3 } else { month + 9 };
482 let day_of_year = (153 * shifted_month + 2) / 5 + i64::from(day) - 1;
483 let day_of_era = year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year;
484 #[expect(clippy::cast_possible_truncation, reason = "a date in i32 range stays in i32 range")]
485 ((era * 146_097 + day_of_era - 719_468) as i32)
486}
487
488fn write_date(f: &mut fmt::Formatter<'_>, days: i32) -> fmt::Result {
495 let (year, month, day) = civil_from_days(days);
496 if year <= 0 {
497 write!(f, "{:04}-{month:02}-{day:02} (BC)", 1 - year)
498 } else {
499 write!(f, "{year:04}-{month:02}-{day:02}")
500 }
501}
502
503fn write_time(f: &mut fmt::Formatter<'_>, micros: i64) -> fmt::Result {
504 let seconds = micros.div_euclid(1_000_000);
505 let fraction = micros.rem_euclid(1_000_000);
506 let (hours, minutes, seconds) = (seconds / 3600, (seconds / 60) % 60, seconds % 60);
507 write!(f, "{hours:02}:{minutes:02}:{seconds:02}")?;
508 if fraction != 0 {
509 let text = format!("{fraction:06}");
511 write!(f, ".{}", text.trim_end_matches('0'))?;
512 }
513 Ok(())
514}
515
516fn write_timestamp(f: &mut fmt::Formatter<'_>, micros: i64) -> fmt::Result {
517 const MICROS_PER_DAY: i64 = 86_400 * 1_000_000;
518 let days = micros.div_euclid(MICROS_PER_DAY);
519 let within_day = micros.rem_euclid(MICROS_PER_DAY);
520 let Ok(days) = i32::try_from(days) else {
521 return f.write_str("timestamp out of range");
522 };
523 write_date(f, days)?;
524 f.write_str(" ")?;
525 write_time(f, within_day)
526}
527
528fn write_interval(f: &mut fmt::Formatter<'_>, months: i32, days: i32, micros: i64) -> fmt::Result {
529 let mut wrote = false;
530 let space = |f: &mut fmt::Formatter<'_>, wrote: &mut bool| -> fmt::Result {
531 if *wrote {
532 f.write_str(" ")?;
533 }
534 *wrote = true;
535 Ok(())
536 };
537 let (years, rest_months) = (months / 12, months % 12);
538 if years != 0 {
539 space(f, &mut wrote)?;
540 write!(f, "{years} year{}", plural(years))?;
541 }
542 if rest_months != 0 {
543 space(f, &mut wrote)?;
544 write!(f, "{rest_months} month{}", plural(rest_months))?;
545 }
546 if days != 0 {
547 space(f, &mut wrote)?;
548 write!(f, "{days} day{}", plural(days))?;
549 }
550 if micros != 0 || !wrote {
551 space(f, &mut wrote)?;
552 if micros < 0 {
553 f.write_str("-")?;
554 }
555 write_time(f, micros.abs())?;
556 }
557 Ok(())
558}
559
560fn plural(n: i32) -> &'static str {
561 if n == 1 || n == -1 { "" } else { "s" }
562}
563
564#[cfg(test)]
565mod tests {
566 use super::{Value, civil_from_days, days_from_civil};
567 use crate::types::LogicalType;
568
569 #[test]
570 fn a_value_knows_its_own_type() {
571 assert_eq!(Value::Integer(1).logical_type(), LogicalType::Integer);
572 assert_eq!(Value::Null.logical_type(), LogicalType::Null);
573 let list = Value::List { element: LogicalType::Varchar, values: Vec::new() };
574 assert_eq!(list.logical_type(), LogicalType::list(LogicalType::Varchar));
577 }
578
579 #[test]
580 fn the_date_conversion_is_its_own_inverse() {
581 for days in days_from_civil(1600, 1, 1)..days_from_civil(2400, 1, 1) {
584 let (year, month, day) = civil_from_days(days);
585 assert_eq!(days_from_civil(year, month, day), days, "{year}-{month}-{day}");
586 }
587 }
588
589 #[test]
590 fn the_epoch_is_where_it_should_be() {
591 assert_eq!(days_from_civil(1970, 1, 1), 0);
592 assert_eq!(civil_from_days(0), (1970, 1, 1));
593 assert_eq!(Value::Date(0).to_string(), "1970-01-01");
594 assert_eq!(Value::Date(19_723).to_string(), "2024-01-01");
595 assert_eq!(Value::Date(19_737).to_string(), "2024-01-15");
596 }
597
598 #[test]
599 fn a_leap_day_is_a_day() {
600 assert_eq!(civil_from_days(days_from_civil(2024, 2, 29)), (2024, 2, 29));
601 assert_eq!(days_from_civil(1900, 3, 1) - days_from_civil(1900, 2, 28), 1);
604 assert_eq!(days_from_civil(2000, 3, 1) - days_from_civil(2000, 2, 28), 2);
605 }
606
607 #[test]
608 fn a_year_at_or_before_zero_prints_in_the_era_before_christ() {
609 let date = |year, month, day| Value::Date(days_from_civil(year, month, day)).to_string();
610 assert_eq!(date(1, 1, 1), "0001-01-01");
613 assert_eq!(date(0, 1, 1), "0001-01-01 (BC)");
614 assert_eq!(date(0, 12, 31), "0001-12-31 (BC)");
615 assert_eq!(date(-1, 1, 1), "0002-01-01 (BC)");
616 assert_eq!(date(-2020, 3, 4), "2021-03-04 (BC)");
617 let timestamp = |year, month, day| {
618 Value::Timestamp(i64::from(days_from_civil(year, month, day)) * 86_400 * 1_000_000)
619 .to_string()
620 };
621 assert_eq!(timestamp(0, 1, 1), "0001-01-01 (BC) 00:00:00");
622 }
623
624 #[test]
625 fn times_print_with_the_trailing_zeros_trimmed() {
626 assert_eq!(Value::Time(0).to_string(), "00:00:00");
627 assert_eq!(Value::Time(3_723_000_000).to_string(), "01:02:03");
628 assert_eq!(Value::Time(3_723_500_000).to_string(), "01:02:03.5");
629 assert_eq!(Value::Time(3_723_000_001).to_string(), "01:02:03.000001");
630 }
631
632 #[test]
633 fn a_timestamp_before_the_epoch_borrows_from_the_day() {
634 assert_eq!(Value::Timestamp(-1).to_string(), "1969-12-31 23:59:59.999999");
637 assert_eq!(Value::Timestamp(0).to_string(), "1970-01-01 00:00:00");
638 }
639
640 #[test]
641 fn a_decimal_prints_at_its_scale() {
642 let d = |unscaled, scale| Value::Decimal { unscaled, width: 18, scale }.to_string();
643 assert_eq!(d(1234, 2), "12.34");
644 assert_eq!(d(-1234, 2), "-12.34");
645 assert_eq!(d(5, 3), "0.005");
646 assert_eq!(d(-5, 3), "-0.005");
647 assert_eq!(d(1234, 0), "1234");
648 assert_eq!(d(1_000_000, 6), "1.000000");
649 }
650
651 #[test]
652 fn a_float_keeps_the_point_that_says_it_is_one() {
653 assert_eq!(Value::Double(1.0).to_string(), "1.0");
654 assert_eq!(Value::Double(-3.0).to_string(), "-3.0");
655 assert_eq!(Value::Double(1.5).to_string(), "1.5");
656 assert_eq!(Value::Double(-0.0).to_string(), "-0.0");
657 assert_eq!(Value::Float(0.5).to_string(), "0.5");
658 }
659
660 #[test]
661 fn a_float_is_printed_from_its_own_width_rather_than_widened_first() {
662 assert_eq!(Value::Float(0.1).to_string(), "0.1");
665 assert_eq!(Value::Float(1.0).to_string(), "1.0");
666 }
667
668 #[test]
669 fn a_float_switches_to_an_exponent_where_duckdb_switches() {
670 assert_eq!(Value::Double(1e15).to_string(), "1000000000000000.0");
671 assert_eq!(Value::Double(1e16).to_string(), "1e+16");
672 assert_eq!(Value::Double(1e20).to_string(), "1e+20");
673 assert_eq!(Value::Double(1e-4).to_string(), "0.0001");
674 assert_eq!(Value::Double(1e-5).to_string(), "1e-05");
675 assert_eq!(Value::Double(1.234_567_890_123_456_8e17).to_string(), "1.2345678901234568e+17");
676 }
677
678 #[test]
679 fn a_float_that_is_not_a_number_says_so_the_way_duckdb_says_it() {
680 assert_eq!(Value::Double(f64::INFINITY).to_string(), "inf");
681 assert_eq!(Value::Double(f64::NEG_INFINITY).to_string(), "-inf");
682 assert_eq!(Value::Double(f64::NAN).to_string(), "nan");
683 assert_eq!(Value::Double(-f64::NAN).to_string(), "-nan");
688 assert_eq!(Value::Float(-f32::NAN).to_string(), "-nan");
689 }
690
691 #[test]
692 fn an_interval_keeps_months_days_and_micros_apart() {
693 let i = |months, days, micros| Value::Interval { months, days, micros }.to_string();
694 assert_eq!(i(14, 3, 3_723_000_000), "1 year 2 months 3 days 01:02:03");
695 assert_eq!(i(1, 0, 0), "1 month");
696 assert_eq!(i(0, 0, 0), "00:00:00");
697 assert_eq!(i(0, 0, -1_000_000), "-00:00:01");
698 }
699
700 #[test]
701 fn a_blob_escapes_what_is_not_printable() {
702 assert_eq!(Value::Blob(b"ok".to_vec()).to_string(), "ok");
703 assert_eq!(Value::Blob(vec![0, 1, b'a']).to_string(), "\\x00\\x01a");
704 assert_eq!(Value::Blob(vec![0x7f, 0xff]).to_string(), "\\x7F\\xFF");
705 assert_eq!(Value::Blob(br#"'"\"#.to_vec()).to_string(), "\\x27\\x22\\x5C");
707 assert_eq!(Value::Blob(b" &`~".to_vec()).to_string(), " &`~");
708 }
709
710 #[test]
711 fn a_limit_that_does_not_fit_is_none_rather_than_clamped() {
712 assert_eq!(Value::Integer(5).as_i64(), Some(5));
713 assert_eq!(Value::UBigInt(u64::MAX).as_i64(), None);
714 assert_eq!(Value::Varchar("5".into()).as_i64(), None);
715 }
716
717 #[test]
718 fn a_footprint_is_the_value_plus_what_it_owns() {
719 let bare = Value::Integer(1).footprint();
720 assert_eq!(bare, size_of::<Value>(), "a number owns nothing");
721 assert_eq!(
722 Value::Boolean(true).footprint(),
723 bare,
724 "the enum is one width whatever is in it"
725 );
726 let text = "a string long enough to be on the heap in any implementation".to_string();
727 assert_eq!(Value::Varchar(text.clone()).footprint(), bare + text.capacity());
728 let list = Value::List {
729 element: LogicalType::Varchar,
730 values: vec![Value::Varchar(text.clone())],
731 };
732 assert_eq!(list.footprint(), bare + size_of::<Value>() + text.capacity());
736 }
737
738 #[test]
742 fn a_value_is_sixty_four_bytes_and_a_map_did_not_widen_it() {
743 assert_eq!(size_of::<Value>(), 64);
744 let entries = vec![(Value::Varchar("a".to_string()), Value::Varchar("b".to_string()))];
745 let map = Value::map(LogicalType::Varchar, LogicalType::Varchar, entries);
746 assert_eq!(
749 map.footprint(),
750 size_of::<Value>() + 2 * size_of::<LogicalType>() + 2 * size_of::<Value>() + 2
751 );
752 assert_eq!(
753 map.logical_type(),
754 LogicalType::map(LogicalType::Varchar, LogicalType::Varchar)
755 );
756 }
757}