1use std::fmt;
13
14use crate::error::{Error, Result};
15
16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub struct Field {
19 pub name: String,
21 pub ty: LogicalType,
23 pub not_null: bool,
31}
32
33impl Field {
34 pub fn new(name: impl Into<String>, ty: LogicalType) -> Self {
36 Self { name: name.into(), ty, not_null: false }
37 }
38
39 pub fn required(name: impl Into<String>, ty: LogicalType) -> Self {
41 Self { name: name.into(), ty, not_null: true }
42 }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Hash)]
52#[non_exhaustive]
53pub enum LogicalType {
54 Null,
56 Boolean,
58 TinyInt,
60 SmallInt,
62 Integer,
64 BigInt,
66 HugeInt,
68 UTinyInt,
70 USmallInt,
72 UInteger,
74 UBigInt,
76 UHugeInt,
78 Float,
80 Double,
82 Decimal {
84 width: u8,
86 scale: u8,
88 },
89 Varchar,
91 Blob,
93 Bit,
95 Uuid,
97 Date,
99 Time,
101 TimeTz,
103 Timestamp,
105 TimestampS,
107 TimestampMs,
109 TimestampNs,
111 TimestampTz,
113 Interval,
115 List(Box<LogicalType>),
117 Array(Box<LogicalType>, u32),
119 Struct(Vec<Field>),
121 Map(Box<LogicalType>, Box<LogicalType>),
123 Union(Vec<Field>),
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
132#[non_exhaustive]
133pub enum PhysicalType {
134 Bool,
136 Int8,
138 Int16,
140 Int32,
142 Int64,
144 Int128,
146 UInt8,
148 UInt16,
150 UInt32,
152 UInt64,
154 UInt128,
156 Float32,
158 Float64,
160 Interval,
162 Varlen,
164 List,
166 Array,
168 Struct,
170 Empty,
172}
173
174impl LogicalType {
175 pub fn decimal(width: u8, scale: u8) -> Result<Self> {
182 if width == 0 || width > MAX_DECIMAL_WIDTH {
183 return Err(Error::binder(format!("Width must be between 1 and {MAX_DECIMAL_WIDTH}!")));
184 }
185 if scale > width {
186 return Err(Error::binder(format!(
187 "Scale cannot be bigger than width, {scale} is bigger than {width}"
188 )));
189 }
190 Ok(Self::Decimal { width, scale })
191 }
192
193 #[must_use]
195 pub fn list(element: Self) -> Self {
196 Self::List(Box::new(element))
197 }
198
199 #[must_use]
201 pub fn array(element: Self, length: u32) -> Self {
202 Self::Array(Box::new(element), length)
203 }
204
205 #[must_use]
207 pub fn map(key: Self, value: Self) -> Self {
208 Self::Map(Box::new(key), Box::new(value))
209 }
210
211 #[must_use]
213 pub fn physical(&self) -> PhysicalType {
214 match self {
215 Self::Null => PhysicalType::Empty,
216 Self::Boolean => PhysicalType::Bool,
217 Self::TinyInt => PhysicalType::Int8,
218 Self::SmallInt => PhysicalType::Int16,
219 Self::Integer | Self::Date => PhysicalType::Int32,
220 Self::BigInt
221 | Self::Time
222 | Self::TimeTz
223 | Self::Timestamp
224 | Self::TimestampS
225 | Self::TimestampMs
226 | Self::TimestampNs
227 | Self::TimestampTz => PhysicalType::Int64,
228 Self::HugeInt | Self::Uuid => PhysicalType::Int128,
229 Self::UTinyInt => PhysicalType::UInt8,
230 Self::USmallInt => PhysicalType::UInt16,
231 Self::UInteger => PhysicalType::UInt32,
232 Self::UBigInt => PhysicalType::UInt64,
233 Self::UHugeInt => PhysicalType::UInt128,
234 Self::Float => PhysicalType::Float32,
235 Self::Double => PhysicalType::Float64,
236 Self::Decimal { width, .. } => match width {
240 0..=4 => PhysicalType::Int16,
241 5..=9 => PhysicalType::Int32,
242 10..=18 => PhysicalType::Int64,
243 _ => PhysicalType::Int128,
244 },
245 Self::Varchar | Self::Blob | Self::Bit => PhysicalType::Varlen,
246 Self::Interval => PhysicalType::Interval,
247 Self::List(_) | Self::Map(_, _) => PhysicalType::List,
250 Self::Array(_, _) => PhysicalType::Array,
251 Self::Struct(_) | Self::Union(_) => PhysicalType::Struct,
252 }
253 }
254
255 #[must_use]
257 pub fn is_numeric(&self) -> bool {
258 self.is_integer() || matches!(self, Self::Float | Self::Double | Self::Decimal { .. })
259 }
260
261 #[must_use]
263 pub fn is_integer(&self) -> bool {
264 matches!(
265 self,
266 Self::TinyInt
267 | Self::SmallInt
268 | Self::Integer
269 | Self::BigInt
270 | Self::HugeInt
271 | Self::UTinyInt
272 | Self::USmallInt
273 | Self::UInteger
274 | Self::UBigInt
275 | Self::UHugeInt
276 )
277 }
278
279 #[must_use]
286 pub fn decimal_shape(&self) -> Option<(u8, u8)> {
287 match self {
288 Self::Decimal { width, scale } => Some((*width, *scale)),
289 other if other.is_integer() => Some((decimal_digits(other), 0)),
290 _ => None,
291 }
292 }
293
294 #[must_use]
296 pub fn is_temporal(&self) -> bool {
297 matches!(
298 self,
299 Self::Date
300 | Self::Time
301 | Self::TimeTz
302 | Self::Timestamp
303 | Self::TimestampS
304 | Self::TimestampMs
305 | Self::TimestampNs
306 | Self::TimestampTz
307 | Self::Interval
308 )
309 }
310
311 #[must_use]
316 pub fn is_nested(&self) -> bool {
317 matches!(
318 self,
319 Self::List(_) | Self::Array(_, _) | Self::Struct(_) | Self::Map(_, _) | Self::Union(_)
320 )
321 }
322
323 #[must_use]
345 pub fn promote(&self, other: &Self) -> Option<Self> {
346 if self == other {
347 return Some(self.clone());
348 }
349 match (self, other) {
350 (Self::Null, ty) | (ty, Self::Null) => Some(ty.clone()),
351 (Self::List(left), Self::List(right)) => Some(Self::list(left.promote(right)?)),
352 _ if self.is_numeric() && other.is_numeric() => {
353 Some(promote_numeric(self.clone(), other.clone()))
354 }
355 _ if self.is_temporal() && other.is_temporal() => {
358 match (rank_temporal(self), rank_temporal(other)) {
359 (Some(left), Some(right)) => {
360 Some(if left >= right { self.clone() } else { other.clone() })
361 }
362 _ => None,
363 }
364 }
365 _ => None,
366 }
367 }
368
369 #[must_use]
371 pub fn children(&self) -> Vec<Self> {
372 match self {
373 Self::List(inner) | Self::Array(inner, _) => vec![inner.as_ref().clone()],
374 Self::Map(key, value) => vec![key.as_ref().clone(), value.as_ref().clone()],
375 Self::Struct(fields) | Self::Union(fields) => {
376 fields.iter().map(|f| f.ty.clone()).collect()
377 }
378 _ => Vec::new(),
379 }
380 }
381
382 pub fn parse(text: &str) -> Result<Self> {
389 let tokens = lex(text)?;
390 let mut parser = TypeParser { tokens: &tokens, position: 0 };
391 let ty = parser.parse_type()?;
392 if parser.position != parser.tokens.len() {
393 return Err(Error::parser(format!("Type \"{text}\" has trailing text")));
394 }
395 Ok(ty)
396 }
397}
398
399pub const MAX_DECIMAL_WIDTH: u8 = 38;
402
403impl fmt::Display for LogicalType {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 match self {
406 Self::Null => f.write_str("\"NULL\""),
407 Self::Boolean => f.write_str("BOOLEAN"),
408 Self::TinyInt => f.write_str("TINYINT"),
409 Self::SmallInt => f.write_str("SMALLINT"),
410 Self::Integer => f.write_str("INTEGER"),
411 Self::BigInt => f.write_str("BIGINT"),
412 Self::HugeInt => f.write_str("HUGEINT"),
413 Self::UTinyInt => f.write_str("UTINYINT"),
414 Self::USmallInt => f.write_str("USMALLINT"),
415 Self::UInteger => f.write_str("UINTEGER"),
416 Self::UBigInt => f.write_str("UBIGINT"),
417 Self::UHugeInt => f.write_str("UHUGEINT"),
418 Self::Float => f.write_str("FLOAT"),
419 Self::Double => f.write_str("DOUBLE"),
420 Self::Decimal { width, scale } => write!(f, "DECIMAL({width},{scale})"),
421 Self::Varchar => f.write_str("VARCHAR"),
422 Self::Blob => f.write_str("BLOB"),
423 Self::Bit => f.write_str("BIT"),
424 Self::Uuid => f.write_str("UUID"),
425 Self::Date => f.write_str("DATE"),
426 Self::Time => f.write_str("TIME"),
427 Self::TimeTz => f.write_str("TIME WITH TIME ZONE"),
428 Self::Timestamp => f.write_str("TIMESTAMP"),
429 Self::TimestampS => f.write_str("TIMESTAMP_S"),
430 Self::TimestampMs => f.write_str("TIMESTAMP_MS"),
431 Self::TimestampNs => f.write_str("TIMESTAMP_NS"),
432 Self::TimestampTz => f.write_str("TIMESTAMP WITH TIME ZONE"),
433 Self::Interval => f.write_str("INTERVAL"),
434 Self::List(inner) => write!(f, "{inner}[]"),
435 Self::Array(inner, length) => write!(f, "{inner}[{length}]"),
436 Self::Map(key, value) => write!(f, "MAP({key}, {value})"),
437 Self::Struct(fields) => write_fields(f, "STRUCT", fields),
438 Self::Union(fields) => write_fields(f, "UNION", fields),
439 }
440 }
441}
442
443fn rank_numeric(ty: &LogicalType) -> u8 {
450 match ty {
451 LogicalType::TinyInt => 1,
452 LogicalType::UTinyInt => 2,
453 LogicalType::SmallInt => 3,
454 LogicalType::USmallInt => 4,
455 LogicalType::Integer => 5,
456 LogicalType::UInteger => 6,
457 LogicalType::BigInt => 7,
458 LogicalType::UBigInt => 8,
459 LogicalType::HugeInt => 9,
460 LogicalType::UHugeInt => 10,
461 LogicalType::Decimal { .. } => 11,
462 LogicalType::Float => 12,
463 LogicalType::Double => 13,
464 _ => 0,
465 }
466}
467
468fn integer_shape(ty: &LogicalType) -> (bool, u8) {
470 match ty {
471 LogicalType::TinyInt => (true, 8),
472 LogicalType::SmallInt => (true, 16),
473 LogicalType::Integer => (true, 32),
474 LogicalType::BigInt => (true, 64),
475 LogicalType::HugeInt => (true, 128),
476 LogicalType::UTinyInt => (false, 8),
477 LogicalType::USmallInt => (false, 16),
478 LogicalType::UInteger => (false, 32),
479 LogicalType::UBigInt => (false, 64),
480 _ => (false, 128),
481 }
482}
483
484fn integer_of(signed: bool, bits: u8) -> Option<LogicalType> {
486 Some(match (signed, bits) {
487 (true, 8) => LogicalType::TinyInt,
488 (true, 16) => LogicalType::SmallInt,
489 (true, 32) => LogicalType::Integer,
490 (true, 64) => LogicalType::BigInt,
491 (true, 128) => LogicalType::HugeInt,
492 (false, 8) => LogicalType::UTinyInt,
493 (false, 16) => LogicalType::USmallInt,
494 (false, 32) => LogicalType::UInteger,
495 (false, 64) => LogicalType::UBigInt,
496 (false, 128) => LogicalType::UHugeInt,
497 _ => return None,
498 })
499}
500
501fn promote_integers(left: &LogicalType, right: &LogicalType) -> LogicalType {
509 let (left_signed, left_bits) = integer_shape(left);
510 let (right_signed, right_bits) = integer_shape(right);
511 if left_signed == right_signed {
512 return if left_bits >= right_bits { left.clone() } else { right.clone() };
513 }
514 let (signed_bits, unsigned_bits) =
515 if left_signed { (left_bits, right_bits) } else { (right_bits, left_bits) };
516 let wanted = signed_bits.max(unsigned_bits.saturating_mul(2));
517 integer_of(true, wanted).unwrap_or(LogicalType::Double)
518}
519
520fn promote_numeric(left: LogicalType, right: LogicalType) -> LogicalType {
522 if let (
526 LogicalType::Decimal { width: left_width, scale: left_scale },
527 LogicalType::Decimal { width: right_width, scale: right_scale },
528 ) = (&left, &right)
529 {
530 let scale = (*left_scale).max(*right_scale);
531 let integral =
532 left_width.saturating_sub(*left_scale).max(right_width.saturating_sub(*right_scale));
533 let width = integral.saturating_add(scale).min(MAX_DECIMAL_WIDTH);
534 return LogicalType::Decimal { width, scale: scale.min(width) };
535 }
536 let widened = match (&left, &right) {
539 (LogicalType::Decimal { width, scale }, other)
540 | (other, LogicalType::Decimal { width, scale })
541 if other.is_integer() =>
542 {
543 let needed = decimal_digits(other).saturating_add(*scale).min(MAX_DECIMAL_WIDTH);
544 Some(LogicalType::Decimal { width: (*width).max(needed), scale: *scale })
545 }
546 _ => None,
547 };
548 if let Some(ty) = widened {
549 return ty;
550 }
551 if left.is_integer() && right.is_integer() {
552 return promote_integers(&left, &right);
553 }
554 if rank_numeric(&left) >= rank_numeric(&right) { left } else { right }
555}
556
557fn decimal_digits(ty: &LogicalType) -> u8 {
566 match ty {
567 LogicalType::TinyInt | LogicalType::UTinyInt => 3,
568 LogicalType::SmallInt | LogicalType::USmallInt => 5,
569 LogicalType::Integer | LogicalType::UInteger => 10,
570 LogicalType::BigInt => 19,
571 LogicalType::UBigInt => 20,
572 _ => MAX_DECIMAL_WIDTH,
573 }
574}
575
576fn rank_temporal(ty: &LogicalType) -> Option<u8> {
582 match ty {
583 LogicalType::Date => Some(1),
584 LogicalType::TimestampS => Some(2),
585 LogicalType::TimestampMs => Some(3),
586 LogicalType::Timestamp => Some(4),
587 LogicalType::TimestampNs => Some(5),
588 LogicalType::TimestampTz => Some(6),
589 _ => None,
590 }
591}
592
593fn write_fields(f: &mut fmt::Formatter<'_>, keyword: &str, fields: &[Field]) -> fmt::Result {
594 f.write_str(keyword)?;
595 f.write_str("(")?;
596 for (index, field) in fields.iter().enumerate() {
597 if index > 0 {
598 f.write_str(", ")?;
599 }
600 write_identifier(f, &field.name)?;
601 write!(f, " {}", field.ty)?;
602 }
603 f.write_str(")")
604}
605
606fn write_identifier(f: &mut fmt::Formatter<'_>, name: &str) -> fmt::Result {
608 let plain = !name.is_empty()
609 && name.chars().next().is_some_and(|c| c.is_ascii_alphabetic() || c == '_')
610 && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_');
611 if plain {
612 f.write_str(name)
613 } else {
614 f.write_str("\"")?;
615 for c in name.chars() {
616 if c == '"' {
617 f.write_str("\"\"")?;
618 } else {
619 write!(f, "{c}")?;
620 }
621 }
622 f.write_str("\"")
623 }
624}
625
626#[derive(Debug, Clone, PartialEq, Eq)]
627enum Token {
628 Word(String),
629 Quoted(String),
630 Number(u32),
631 LeftParen,
632 RightParen,
633 LeftBracket,
634 RightBracket,
635 Comma,
636}
637
638fn lex(text: &str) -> Result<Vec<Token>> {
639 let mut tokens = Vec::new();
640 let chars: Vec<char> = text.chars().collect();
641 let mut i = 0;
642 while i < chars.len() {
643 let c = chars[i];
644 match c {
645 c if c.is_whitespace() => i += 1,
646 '(' => {
647 tokens.push(Token::LeftParen);
648 i += 1;
649 }
650 ')' => {
651 tokens.push(Token::RightParen);
652 i += 1;
653 }
654 '[' => {
655 tokens.push(Token::LeftBracket);
656 i += 1;
657 }
658 ']' => {
659 tokens.push(Token::RightBracket);
660 i += 1;
661 }
662 ',' => {
663 tokens.push(Token::Comma);
664 i += 1;
665 }
666 '"' => {
667 let mut name = String::new();
668 i += 1;
669 loop {
670 let Some(&c) = chars.get(i) else {
671 return Err(Error::parser(format!(
672 "Type \"{text}\" has an unterminated quoted name"
673 )));
674 };
675 i += 1;
676 if c == '"' {
677 if chars.get(i) == Some(&'"') {
678 name.push('"');
679 i += 1;
680 continue;
681 }
682 break;
683 }
684 name.push(c);
685 }
686 tokens.push(Token::Quoted(name));
687 }
688 c if c.is_ascii_digit() => {
689 let start = i;
690 while chars.get(i).is_some_and(char::is_ascii_digit) {
691 i += 1;
692 }
693 let digits: String = chars[start..i].iter().collect();
694 let number = digits.parse::<u32>().map_err(|_| {
695 Error::parser(format!("Type \"{text}\" has a number that is too large"))
696 })?;
697 tokens.push(Token::Number(number));
698 }
699 c if c.is_alphabetic() || c == '_' => {
700 let start = i;
701 while chars.get(i).is_some_and(|&c| c.is_alphanumeric() || c == '_') {
702 i += 1;
703 }
704 tokens.push(Token::Word(chars[start..i].iter().collect()));
705 }
706 other => {
707 return Err(Error::parser(format!(
708 "Type \"{text}\" has an unexpected character {other:?}"
709 )));
710 }
711 }
712 }
713 Ok(tokens)
714}
715
716struct TypeParser<'a> {
717 tokens: &'a [Token],
718 position: usize,
719}
720
721impl TypeParser<'_> {
722 fn peek(&self) -> Option<&Token> {
723 self.tokens.get(self.position)
724 }
725
726 fn eat(&mut self, token: &Token) -> bool {
727 if self.peek() == Some(token) {
728 self.position += 1;
729 true
730 } else {
731 false
732 }
733 }
734
735 fn eat_word(&mut self, word: &str) -> bool {
737 match self.peek() {
738 Some(Token::Word(found)) if found.eq_ignore_ascii_case(word) => {
739 self.position += 1;
740 true
741 }
742 _ => false,
743 }
744 }
745
746 fn parse_type(&mut self) -> Result<LogicalType> {
747 let mut ty = self.parse_base()?;
748 loop {
750 if !self.eat(&Token::LeftBracket) {
751 break;
752 }
753 if let Some(&Token::Number(length)) = self.peek() {
754 self.position += 1;
755 expect(self.eat(&Token::RightBracket), "]")?;
756 ty = LogicalType::array(ty, length);
757 } else {
758 expect(self.eat(&Token::RightBracket), "]")?;
759 ty = LogicalType::list(ty);
760 }
761 }
762 Ok(ty)
763 }
764
765 fn parse_base(&mut self) -> Result<LogicalType> {
766 let word = match self.peek().cloned() {
769 Some(Token::Word(word) | Token::Quoted(word)) => {
770 self.position += 1;
771 word
772 }
773 _ => return Err(Error::parser("Expected a type name".to_string())),
774 };
775 let upper = word.to_ascii_uppercase();
776
777 match upper.as_str() {
778 "STRUCT" | "ROW" => return self.parse_fields().map(LogicalType::Struct),
779 "UNION" => return self.parse_fields().map(LogicalType::Union),
780 "MAP" => {
781 expect(self.eat(&Token::LeftParen), "(")?;
782 let key = self.parse_type()?;
783 expect(self.eat(&Token::Comma), ",")?;
784 let value = self.parse_type()?;
785 expect(self.eat(&Token::RightParen), ")")?;
786 return Ok(LogicalType::map(key, value));
787 }
788 "DECIMAL" | "NUMERIC" | "DEC" => {
789 if !self.eat(&Token::LeftParen) {
790 return LogicalType::decimal(18, 3);
793 }
794 let width = self.parse_number()?;
795 let scale = if self.eat(&Token::Comma) { self.parse_number()? } else { 0 };
796 expect(self.eat(&Token::RightParen), ")")?;
797 let narrow = |n: u32| u8::try_from(n).unwrap_or(u8::MAX);
798 return LogicalType::decimal(narrow(width), narrow(scale));
799 }
800 "DOUBLE" => {
803 self.eat_word("PRECISION");
804 return Ok(LogicalType::Double);
805 }
806 "CHARACTER" => {
807 self.eat_word("VARYING");
808 self.eat_length_modifier()?;
809 return Ok(LogicalType::Varchar);
810 }
811 "TIME" | "TIMESTAMP" => {
812 let with_zone = self.eat_time_zone_suffix();
813 return Ok(match (upper.as_str(), with_zone) {
814 ("TIME", false) => LogicalType::Time,
815 ("TIME", true) => LogicalType::TimeTz,
816 (_, false) => LogicalType::Timestamp,
817 (_, true) => LogicalType::TimestampTz,
818 });
819 }
820 _ => {}
821 }
822
823 self.eat_length_modifier()?;
826 alias(&upper).ok_or_else(|| Error::parser(format!("Unrecognized type name \"{word}\"")))
827 }
828
829 fn eat_time_zone_suffix(&mut self) -> bool {
831 let start = self.position;
832 let with = if self.eat_word("WITH") {
833 true
834 } else if self.eat_word("WITHOUT") {
835 false
836 } else {
837 return false;
838 };
839 if self.eat_word("TIME") && self.eat_word("ZONE") {
840 with
841 } else {
842 self.position = start;
843 false
844 }
845 }
846
847 fn eat_length_modifier(&mut self) -> Result<()> {
848 if self.eat(&Token::LeftParen) {
849 self.parse_number()?;
850 expect(self.eat(&Token::RightParen), ")")?;
851 }
852 Ok(())
853 }
854
855 fn parse_fields(&mut self) -> Result<Vec<Field>> {
856 expect(self.eat(&Token::LeftParen), "(")?;
857 let mut fields = Vec::new();
858 if self.eat(&Token::RightParen) {
859 return Ok(fields);
860 }
861 loop {
862 let name = match self.peek().cloned() {
863 Some(Token::Word(name) | Token::Quoted(name)) => {
864 self.position += 1;
865 name
866 }
867 _ => return Err(Error::parser("Expected a field name".to_string())),
868 };
869 let ty = self.parse_type()?;
870 fields.push(Field::new(name, ty));
871 if self.eat(&Token::Comma) {
872 continue;
873 }
874 expect(self.eat(&Token::RightParen), ")")?;
875 return Ok(fields);
876 }
877 }
878
879 fn parse_number(&mut self) -> Result<u32> {
880 match self.peek() {
881 Some(&Token::Number(n)) => {
882 self.position += 1;
883 Ok(n)
884 }
885 _ => Err(Error::parser("Expected a number".to_string())),
886 }
887 }
888}
889
890fn expect(matched: bool, what: &str) -> Result<()> {
891 if matched { Ok(()) } else { Err(Error::parser(format!("Expected \"{what}\""))) }
892}
893
894fn alias(upper: &str) -> Option<LogicalType> {
899 Some(match upper {
900 "NULL" => LogicalType::Null,
901 "BOOLEAN" | "BOOL" | "LOGICAL" => LogicalType::Boolean,
902 "TINYINT" | "INT1" => LogicalType::TinyInt,
903 "SMALLINT" | "INT2" | "SHORT" => LogicalType::SmallInt,
904 "INTEGER" | "INT" | "INT4" | "SIGNED" => LogicalType::Integer,
905 "BIGINT" | "INT8" | "LONG" => LogicalType::BigInt,
906 "HUGEINT" | "INT128" => LogicalType::HugeInt,
907 "UTINYINT" | "UINT1" => LogicalType::UTinyInt,
908 "USMALLINT" | "UINT2" => LogicalType::USmallInt,
909 "UINTEGER" | "UINT4" => LogicalType::UInteger,
910 "UBIGINT" | "UINT8" => LogicalType::UBigInt,
911 "UHUGEINT" | "UINT128" => LogicalType::UHugeInt,
912 "FLOAT" | "FLOAT4" | "REAL" => LogicalType::Float,
913 "FLOAT8" => LogicalType::Double,
914 "VARCHAR" | "CHAR" | "BPCHAR" | "TEXT" | "STRING" => LogicalType::Varchar,
915 "BLOB" | "BYTEA" | "BINARY" | "VARBINARY" => LogicalType::Blob,
916 "BIT" | "BITSTRING" => LogicalType::Bit,
917 "UUID" | "GUID" => LogicalType::Uuid,
918 "DATE" => LogicalType::Date,
919 "TIMETZ" => LogicalType::TimeTz,
920 "DATETIME" => LogicalType::Timestamp,
921 "TIMESTAMP_S" | "TIMESTAMP_SEC" | "TIMESTAMP_SECONDS" => LogicalType::TimestampS,
922 "TIMESTAMP_MS" | "TIMESTAMP_MILLISECONDS" => LogicalType::TimestampMs,
923 "TIMESTAMP_NS" | "TIMESTAMP_NANOSECONDS" => LogicalType::TimestampNs,
924 "TIMESTAMPTZ" => LogicalType::TimestampTz,
925 "INTERVAL" => LogicalType::Interval,
926 _ => return None,
927 })
928}
929
930#[cfg(test)]
931mod promotion_tests {
932 use super::LogicalType;
933
934 #[test]
935 fn a_type_promotes_with_itself_to_itself() {
936 for ty in [
937 LogicalType::Integer,
938 LogicalType::Varchar,
939 LogicalType::Boolean,
940 LogicalType::Struct(vec![]),
941 ] {
942 assert_eq!(ty.promote(&ty), Some(ty.clone()), "{ty} does not promote with itself");
943 }
944 }
945
946 #[test]
947 fn null_takes_the_other_type() {
948 assert_eq!(LogicalType::Null.promote(&LogicalType::Varchar), Some(LogicalType::Varchar));
949 assert_eq!(LogicalType::Date.promote(&LogicalType::Null), Some(LogicalType::Date));
950 assert_eq!(LogicalType::Null.promote(&LogicalType::Null), Some(LogicalType::Null));
951 }
952
953 #[test]
954 fn the_wider_number_wins() {
955 assert_eq!(
956 LogicalType::Integer.promote(&LogicalType::SmallInt),
957 Some(LogicalType::Integer)
958 );
959 assert_eq!(LogicalType::Integer.promote(&LogicalType::Double), Some(LogicalType::Double));
960 assert_eq!(LogicalType::Float.promote(&LogicalType::Double), Some(LogicalType::Double));
961 }
962
963 #[test]
966 fn signed_and_unsigned_widen_rather_than_reinterpret() {
967 assert_eq!(LogicalType::Integer.promote(&LogicalType::UInteger), Some(LogicalType::BigInt));
968 assert_eq!(
969 LogicalType::TinyInt.promote(&LogicalType::UTinyInt),
970 Some(LogicalType::SmallInt)
971 );
972 assert_eq!(LogicalType::BigInt.promote(&LogicalType::UBigInt), Some(LogicalType::HugeInt));
973 }
974
975 #[test]
976 fn promotion_does_not_care_which_side_a_type_is_on() {
977 let types = [
978 LogicalType::TinyInt,
979 LogicalType::UInteger,
980 LogicalType::BigInt,
981 LogicalType::Double,
982 LogicalType::Decimal { width: 10, scale: 2 },
983 LogicalType::Null,
984 LogicalType::Varchar,
985 LogicalType::Date,
986 LogicalType::Timestamp,
987 ];
988 for left in &types {
989 for right in &types {
990 assert_eq!(
991 left.promote(right),
992 right.promote(left),
993 "{left} and {right} promote differently depending on the order"
994 );
995 }
996 }
997 }
998
999 #[test]
1000 fn a_decimal_keeps_room_for_both_halves() {
1001 let left = LogicalType::Decimal { width: 5, scale: 4 };
1002 let right = LogicalType::Decimal { width: 5, scale: 1 };
1003 assert_eq!(left.promote(&right), Some(LogicalType::Decimal { width: 8, scale: 4 }));
1004 }
1005
1006 #[test]
1007 fn an_integer_next_to_a_decimal_widens_the_decimal() {
1008 let decimal = LogicalType::Decimal { width: 5, scale: 2 };
1009 assert_eq!(
1010 decimal.promote(&LogicalType::Integer),
1011 Some(LogicalType::Decimal { width: 12, scale: 2 })
1012 );
1013 }
1014
1015 #[test]
1020 fn a_bigint_leaves_room_for_one_digit_fewer_than_a_ubigint() {
1021 let decimal = LogicalType::Decimal { width: 4, scale: 2 };
1022 assert_eq!(
1023 decimal.promote(&LogicalType::BigInt),
1024 Some(LogicalType::Decimal { width: 21, scale: 2 })
1025 );
1026 assert_eq!(
1027 decimal.promote(&LogicalType::UBigInt),
1028 Some(LogicalType::Decimal { width: 22, scale: 2 })
1029 );
1030 assert_eq!(decimal.promote(&LogicalType::Integer), decimal.promote(&LogicalType::UInteger));
1031 }
1032
1033 #[test]
1034 fn a_date_and_a_timestamp_meet_at_the_timestamp() {
1035 assert_eq!(
1036 LogicalType::Date.promote(&LogicalType::Timestamp),
1037 Some(LogicalType::Timestamp)
1038 );
1039 assert_eq!(
1040 LogicalType::TimestampS.promote(&LogicalType::TimestampNs),
1041 Some(LogicalType::TimestampNs)
1042 );
1043 }
1044
1045 #[test]
1048 fn types_that_do_not_meet_say_so() {
1049 assert_eq!(LogicalType::Timestamp.promote(&LogicalType::Interval), None);
1050 assert_eq!(LogicalType::Integer.promote(&LogicalType::Varchar), None);
1051 assert_eq!(LogicalType::Boolean.promote(&LogicalType::Integer), None);
1052 }
1053
1054 #[test]
1055 fn a_list_promotes_by_its_element() {
1056 let left = LogicalType::list(LogicalType::Integer);
1057 let right = LogicalType::list(LogicalType::BigInt);
1058 assert_eq!(left.promote(&right), Some(LogicalType::list(LogicalType::BigInt)));
1059 assert_eq!(left.promote(&LogicalType::list(LogicalType::Varchar)), None);
1060 }
1061}
1062
1063#[cfg(test)]
1064mod tests {
1065 use super::{Field, LogicalType, PhysicalType};
1066
1067 fn every_type() -> Vec<LogicalType> {
1070 vec![
1071 LogicalType::Null,
1072 LogicalType::Boolean,
1073 LogicalType::TinyInt,
1074 LogicalType::SmallInt,
1075 LogicalType::Integer,
1076 LogicalType::BigInt,
1077 LogicalType::HugeInt,
1078 LogicalType::UTinyInt,
1079 LogicalType::USmallInt,
1080 LogicalType::UInteger,
1081 LogicalType::UBigInt,
1082 LogicalType::UHugeInt,
1083 LogicalType::Float,
1084 LogicalType::Double,
1085 LogicalType::Decimal { width: 18, scale: 3 },
1086 LogicalType::Decimal { width: 38, scale: 0 },
1087 LogicalType::Varchar,
1088 LogicalType::Blob,
1089 LogicalType::Bit,
1090 LogicalType::Uuid,
1091 LogicalType::Date,
1092 LogicalType::Time,
1093 LogicalType::TimeTz,
1094 LogicalType::Timestamp,
1095 LogicalType::TimestampS,
1096 LogicalType::TimestampMs,
1097 LogicalType::TimestampNs,
1098 LogicalType::TimestampTz,
1099 LogicalType::Interval,
1100 LogicalType::list(LogicalType::Integer),
1101 LogicalType::list(LogicalType::list(LogicalType::Varchar)),
1102 LogicalType::array(LogicalType::Double, 3),
1103 LogicalType::map(LogicalType::Varchar, LogicalType::Integer),
1104 LogicalType::Struct(vec![
1105 Field::new("a", LogicalType::Integer),
1106 Field::new("b", LogicalType::list(LogicalType::Varchar)),
1107 ]),
1108 LogicalType::Union(vec![
1109 Field::new("num", LogicalType::Integer),
1110 Field::new("str", LogicalType::Varchar),
1111 ]),
1112 ]
1113 }
1114
1115 #[test]
1116 fn every_type_survives_being_printed_and_read_back() {
1117 for ty in every_type() {
1121 let printed = ty.to_string();
1122 let parsed = LogicalType::parse(&printed)
1123 .unwrap_or_else(|e| panic!("{printed} did not parse back: {e}"));
1124 assert_eq!(parsed, ty, "{printed} parsed to something else");
1125 }
1126 }
1127
1128 #[test]
1129 fn a_field_name_that_needs_quoting_gets_quoted() {
1130 let ty = LogicalType::Struct(vec![
1131 Field::new("plain", LogicalType::Integer),
1132 Field::new("has space", LogicalType::Integer),
1133 Field::new("has\"quote", LogicalType::Integer),
1134 Field::new("2leading", LogicalType::Integer),
1135 ]);
1136 assert_eq!(
1137 ty.to_string(),
1138 "STRUCT(plain INTEGER, \"has space\" INTEGER, \"has\"\"quote\" INTEGER, \
1139 \"2leading\" INTEGER)"
1140 );
1141 assert_eq!(LogicalType::parse(&ty.to_string()).unwrap(), ty);
1142 }
1143
1144 #[test]
1145 fn the_duckdb_aliases_resolve() {
1146 let cases = [
1147 ("int4", LogicalType::Integer),
1148 ("INT", LogicalType::Integer),
1149 ("signed", LogicalType::Integer),
1150 ("int8", LogicalType::BigInt),
1151 ("float4", LogicalType::Float),
1152 ("float8", LogicalType::Double),
1153 ("double precision", LogicalType::Double),
1154 ("text", LogicalType::Varchar),
1155 ("varchar(10)", LogicalType::Varchar),
1156 ("character varying(255)", LogicalType::Varchar),
1157 ("bool", LogicalType::Boolean),
1158 ("datetime", LogicalType::Timestamp),
1159 ("numeric(9, 2)", LogicalType::Decimal { width: 9, scale: 2 }),
1160 ("decimal", LogicalType::Decimal { width: 18, scale: 3 }),
1161 ("timestamp without time zone", LogicalType::Timestamp),
1162 ("timestamp with time zone", LogicalType::TimestampTz),
1163 ("time with time zone", LogicalType::TimeTz),
1164 ];
1165 for (text, expected) in cases {
1166 assert_eq!(LogicalType::parse(text).unwrap(), expected, "{text}");
1167 }
1168 }
1169
1170 #[test]
1171 fn list_and_array_suffixes_bind_left_to_right() {
1172 assert_eq!(
1173 LogicalType::parse("INTEGER[][3]").unwrap(),
1174 LogicalType::array(LogicalType::list(LogicalType::Integer), 3)
1175 );
1176 assert_eq!(
1177 LogicalType::parse("STRUCT(a INT)[]").unwrap(),
1178 LogicalType::list(LogicalType::Struct(vec![Field::new("a", LogicalType::Integer)]))
1179 );
1180 }
1181
1182 #[test]
1183 fn a_decimal_is_stored_in_the_narrowest_integer_that_holds_it() {
1184 assert_eq!(LogicalType::decimal(4, 2).unwrap().physical(), PhysicalType::Int16);
1185 assert_eq!(LogicalType::decimal(9, 2).unwrap().physical(), PhysicalType::Int32);
1186 assert_eq!(LogicalType::decimal(18, 2).unwrap().physical(), PhysicalType::Int64);
1187 assert_eq!(LogicalType::decimal(38, 2).unwrap().physical(), PhysicalType::Int128);
1188 }
1189
1190 #[test]
1191 fn a_decimal_outside_the_bounds_is_rejected_rather_than_clamped() {
1192 assert!(LogicalType::decimal(0, 0).is_err());
1193 assert!(LogicalType::decimal(39, 0).is_err());
1194 assert!(LogicalType::decimal(4, 5).is_err());
1195 assert!(LogicalType::parse("DECIMAL(39,0)").is_err());
1196 }
1197
1198 #[test]
1199 fn a_date_and_an_integer_share_a_layout_and_not_a_meaning() {
1200 assert_eq!(LogicalType::Date.physical(), LogicalType::Integer.physical());
1201 assert_ne!(LogicalType::Date, LogicalType::Integer);
1202 assert!(LogicalType::Date.is_temporal());
1203 assert!(!LogicalType::Date.is_numeric());
1204 }
1205
1206 #[test]
1207 fn nesting_reports_its_children_in_child_column_order() {
1208 let ty = LogicalType::map(LogicalType::Varchar, LogicalType::Integer);
1209 assert!(ty.is_nested());
1210 assert_eq!(ty.children(), vec![LogicalType::Varchar, LogicalType::Integer]);
1211 assert_eq!(LogicalType::Integer.children(), Vec::new());
1212 }
1213
1214 #[test]
1215 fn text_that_is_not_a_type_is_rejected_with_the_word_that_broke_it() {
1216 let error = LogicalType::parse("INTEGRE").unwrap_err();
1217 assert!(error.message().contains("INTEGRE"), "{error}");
1218 assert!(LogicalType::parse("INTEGER JUNK").is_err());
1219 assert!(LogicalType::parse("STRUCT(a)").is_err());
1220 assert!(LogicalType::parse("").is_err());
1221 }
1222}