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]
263 pub fn physical_name(&self) -> String {
264 if let Some(width) = self.decimal_storage() {
265 return format!("DECIMAL({width})");
266 }
267 let name = match self {
268 Self::Boolean => "BOOL",
269 Self::TinyInt => "INT8",
270 Self::SmallInt => "INT16",
271 Self::Integer => "INT32",
272 Self::BigInt => "INT64",
273 Self::HugeInt => "INT128",
274 Self::UTinyInt => "UINT8",
275 Self::USmallInt => "UINT16",
276 Self::UInteger => "UINT32",
277 Self::UBigInt => "UINT64",
278 Self::UHugeInt => "UINT128",
279 other => return other.to_string(),
280 };
281 name.to_string()
282 }
283
284 #[must_use]
292 pub fn decimal_storage(&self) -> Option<u8> {
293 let Self::Decimal { width, .. } = self else {
294 return None;
295 };
296 Some(match width {
297 0..=4 => 4,
298 5..=9 => 9,
299 10..=18 => 18,
300 _ => MAX_DECIMAL_WIDTH,
301 })
302 }
303
304 #[must_use]
306 pub fn is_numeric(&self) -> bool {
307 self.is_integer() || matches!(self, Self::Float | Self::Double | Self::Decimal { .. })
308 }
309
310 #[must_use]
312 pub fn is_integer(&self) -> bool {
313 matches!(
314 self,
315 Self::TinyInt
316 | Self::SmallInt
317 | Self::Integer
318 | Self::BigInt
319 | Self::HugeInt
320 | Self::UTinyInt
321 | Self::USmallInt
322 | Self::UInteger
323 | Self::UBigInt
324 | Self::UHugeInt
325 )
326 }
327
328 #[must_use]
335 pub fn decimal_shape(&self) -> Option<(u8, u8)> {
336 match self {
337 Self::Decimal { width, scale } => Some((*width, *scale)),
338 other if other.is_integer() => Some((decimal_digits(other), 0)),
339 _ => None,
340 }
341 }
342
343 #[must_use]
345 pub fn is_temporal(&self) -> bool {
346 matches!(
347 self,
348 Self::Date
349 | Self::Time
350 | Self::TimeTz
351 | Self::Timestamp
352 | Self::TimestampS
353 | Self::TimestampMs
354 | Self::TimestampNs
355 | Self::TimestampTz
356 | Self::Interval
357 )
358 }
359
360 #[must_use]
365 pub fn is_nested(&self) -> bool {
366 matches!(
367 self,
368 Self::List(_) | Self::Array(_, _) | Self::Struct(_) | Self::Map(_, _) | Self::Union(_)
369 )
370 }
371
372 #[must_use]
394 pub fn promote(&self, other: &Self) -> Option<Self> {
395 if self == other {
396 return Some(self.clone());
397 }
398 match (self, other) {
399 (Self::Null, ty) | (ty, Self::Null) => Some(ty.clone()),
400 (Self::List(left), Self::List(right)) => Some(Self::list(left.promote(right)?)),
401 _ if self.is_numeric() && other.is_numeric() => {
402 Some(promote_numeric(self.clone(), other.clone()))
403 }
404 _ if self.is_temporal() && other.is_temporal() => {
407 match (rank_temporal(self), rank_temporal(other)) {
408 (Some(left), Some(right)) => {
409 Some(if left >= right { self.clone() } else { other.clone() })
410 }
411 _ => None,
412 }
413 }
414 _ => None,
415 }
416 }
417
418 #[must_use]
420 pub fn children(&self) -> Vec<Self> {
421 match self {
422 Self::List(inner) | Self::Array(inner, _) => vec![inner.as_ref().clone()],
423 Self::Map(key, value) => vec![key.as_ref().clone(), value.as_ref().clone()],
424 Self::Struct(fields) | Self::Union(fields) => {
425 fields.iter().map(|f| f.ty.clone()).collect()
426 }
427 _ => Vec::new(),
428 }
429 }
430
431 pub fn parse(text: &str) -> Result<Self> {
438 let tokens = lex(text)?;
439 let mut parser = TypeParser { tokens: &tokens, position: 0 };
440 let ty = parser.parse_type()?;
441 if parser.position != parser.tokens.len() {
442 return Err(Error::parser(format!("Type \"{text}\" has trailing text")));
443 }
444 Ok(ty)
445 }
446}
447
448pub const MAX_DECIMAL_WIDTH: u8 = 38;
451
452impl fmt::Display for LogicalType {
453 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454 match self {
455 Self::Null => f.write_str("\"NULL\""),
456 Self::Boolean => f.write_str("BOOLEAN"),
457 Self::TinyInt => f.write_str("TINYINT"),
458 Self::SmallInt => f.write_str("SMALLINT"),
459 Self::Integer => f.write_str("INTEGER"),
460 Self::BigInt => f.write_str("BIGINT"),
461 Self::HugeInt => f.write_str("HUGEINT"),
462 Self::UTinyInt => f.write_str("UTINYINT"),
463 Self::USmallInt => f.write_str("USMALLINT"),
464 Self::UInteger => f.write_str("UINTEGER"),
465 Self::UBigInt => f.write_str("UBIGINT"),
466 Self::UHugeInt => f.write_str("UHUGEINT"),
467 Self::Float => f.write_str("FLOAT"),
468 Self::Double => f.write_str("DOUBLE"),
469 Self::Decimal { width, scale } => write!(f, "DECIMAL({width},{scale})"),
470 Self::Varchar => f.write_str("VARCHAR"),
471 Self::Blob => f.write_str("BLOB"),
472 Self::Bit => f.write_str("BIT"),
473 Self::Uuid => f.write_str("UUID"),
474 Self::Date => f.write_str("DATE"),
475 Self::Time => f.write_str("TIME"),
476 Self::TimeTz => f.write_str("TIME WITH TIME ZONE"),
477 Self::Timestamp => f.write_str("TIMESTAMP"),
478 Self::TimestampS => f.write_str("TIMESTAMP_S"),
479 Self::TimestampMs => f.write_str("TIMESTAMP_MS"),
480 Self::TimestampNs => f.write_str("TIMESTAMP_NS"),
481 Self::TimestampTz => f.write_str("TIMESTAMP WITH TIME ZONE"),
482 Self::Interval => f.write_str("INTERVAL"),
483 Self::List(inner) => write!(f, "{inner}[]"),
484 Self::Array(inner, length) => write!(f, "{inner}[{length}]"),
485 Self::Map(key, value) => write!(f, "MAP({key}, {value})"),
486 Self::Struct(fields) => write_fields(f, "STRUCT", fields),
487 Self::Union(fields) => write_fields(f, "UNION", fields),
488 }
489 }
490}
491
492fn rank_numeric(ty: &LogicalType) -> u8 {
499 match ty {
500 LogicalType::TinyInt => 1,
501 LogicalType::UTinyInt => 2,
502 LogicalType::SmallInt => 3,
503 LogicalType::USmallInt => 4,
504 LogicalType::Integer => 5,
505 LogicalType::UInteger => 6,
506 LogicalType::BigInt => 7,
507 LogicalType::UBigInt => 8,
508 LogicalType::HugeInt => 9,
509 LogicalType::UHugeInt => 10,
510 LogicalType::Decimal { .. } => 11,
511 LogicalType::Float => 12,
512 LogicalType::Double => 13,
513 _ => 0,
514 }
515}
516
517fn integer_shape(ty: &LogicalType) -> (bool, u8) {
519 match ty {
520 LogicalType::TinyInt => (true, 8),
521 LogicalType::SmallInt => (true, 16),
522 LogicalType::Integer => (true, 32),
523 LogicalType::BigInt => (true, 64),
524 LogicalType::HugeInt => (true, 128),
525 LogicalType::UTinyInt => (false, 8),
526 LogicalType::USmallInt => (false, 16),
527 LogicalType::UInteger => (false, 32),
528 LogicalType::UBigInt => (false, 64),
529 _ => (false, 128),
530 }
531}
532
533fn integer_of(signed: bool, bits: u8) -> Option<LogicalType> {
535 Some(match (signed, bits) {
536 (true, 8) => LogicalType::TinyInt,
537 (true, 16) => LogicalType::SmallInt,
538 (true, 32) => LogicalType::Integer,
539 (true, 64) => LogicalType::BigInt,
540 (true, 128) => LogicalType::HugeInt,
541 (false, 8) => LogicalType::UTinyInt,
542 (false, 16) => LogicalType::USmallInt,
543 (false, 32) => LogicalType::UInteger,
544 (false, 64) => LogicalType::UBigInt,
545 (false, 128) => LogicalType::UHugeInt,
546 _ => return None,
547 })
548}
549
550fn promote_integers(left: &LogicalType, right: &LogicalType) -> LogicalType {
558 let (left_signed, left_bits) = integer_shape(left);
559 let (right_signed, right_bits) = integer_shape(right);
560 if left_signed == right_signed {
561 return if left_bits >= right_bits { left.clone() } else { right.clone() };
562 }
563 let (signed_bits, unsigned_bits) =
564 if left_signed { (left_bits, right_bits) } else { (right_bits, left_bits) };
565 let wanted = signed_bits.max(unsigned_bits.saturating_mul(2));
566 integer_of(true, wanted).unwrap_or(LogicalType::Double)
567}
568
569fn promote_numeric(left: LogicalType, right: LogicalType) -> LogicalType {
571 if let (
575 LogicalType::Decimal { width: left_width, scale: left_scale },
576 LogicalType::Decimal { width: right_width, scale: right_scale },
577 ) = (&left, &right)
578 {
579 let scale = (*left_scale).max(*right_scale);
580 let integral =
581 left_width.saturating_sub(*left_scale).max(right_width.saturating_sub(*right_scale));
582 let width = integral.saturating_add(scale).min(MAX_DECIMAL_WIDTH);
583 return LogicalType::Decimal { width, scale: scale.min(width) };
584 }
585 let widened = match (&left, &right) {
588 (LogicalType::Decimal { width, scale }, other)
589 | (other, LogicalType::Decimal { width, scale })
590 if other.is_integer() =>
591 {
592 let needed = decimal_digits(other).saturating_add(*scale).min(MAX_DECIMAL_WIDTH);
593 Some(LogicalType::Decimal { width: (*width).max(needed), scale: *scale })
594 }
595 _ => None,
596 };
597 if let Some(ty) = widened {
598 return ty;
599 }
600 if left.is_integer() && right.is_integer() {
601 return promote_integers(&left, &right);
602 }
603 if rank_numeric(&left) >= rank_numeric(&right) { left } else { right }
604}
605
606fn decimal_digits(ty: &LogicalType) -> u8 {
615 match ty {
616 LogicalType::TinyInt | LogicalType::UTinyInt => 3,
617 LogicalType::SmallInt | LogicalType::USmallInt => 5,
618 LogicalType::Integer | LogicalType::UInteger => 10,
619 LogicalType::BigInt => 19,
620 LogicalType::UBigInt => 20,
621 _ => MAX_DECIMAL_WIDTH,
622 }
623}
624
625fn rank_temporal(ty: &LogicalType) -> Option<u8> {
631 match ty {
632 LogicalType::Date => Some(1),
633 LogicalType::TimestampS => Some(2),
634 LogicalType::TimestampMs => Some(3),
635 LogicalType::Timestamp => Some(4),
636 LogicalType::TimestampNs => Some(5),
637 LogicalType::TimestampTz => Some(6),
638 _ => None,
639 }
640}
641
642fn write_fields(f: &mut fmt::Formatter<'_>, keyword: &str, fields: &[Field]) -> fmt::Result {
643 f.write_str(keyword)?;
644 f.write_str("(")?;
645 for (index, field) in fields.iter().enumerate() {
646 if index > 0 {
647 f.write_str(", ")?;
648 }
649 write_identifier(f, &field.name)?;
650 write!(f, " {}", field.ty)?;
651 }
652 f.write_str(")")
653}
654
655fn write_identifier(f: &mut fmt::Formatter<'_>, name: &str) -> fmt::Result {
657 let plain = !name.is_empty()
658 && name.chars().next().is_some_and(|c| c.is_ascii_alphabetic() || c == '_')
659 && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_');
660 if plain {
661 f.write_str(name)
662 } else {
663 f.write_str("\"")?;
664 for c in name.chars() {
665 if c == '"' {
666 f.write_str("\"\"")?;
667 } else {
668 write!(f, "{c}")?;
669 }
670 }
671 f.write_str("\"")
672 }
673}
674
675#[derive(Debug, Clone, PartialEq, Eq)]
676enum Token {
677 Word(String),
678 Quoted(String),
679 Number(u32),
680 LeftParen,
681 RightParen,
682 LeftBracket,
683 RightBracket,
684 Comma,
685}
686
687fn lex(text: &str) -> Result<Vec<Token>> {
688 let mut tokens = Vec::new();
689 let chars: Vec<char> = text.chars().collect();
690 let mut i = 0;
691 while i < chars.len() {
692 let c = chars[i];
693 match c {
694 c if c.is_whitespace() => i += 1,
695 '(' => {
696 tokens.push(Token::LeftParen);
697 i += 1;
698 }
699 ')' => {
700 tokens.push(Token::RightParen);
701 i += 1;
702 }
703 '[' => {
704 tokens.push(Token::LeftBracket);
705 i += 1;
706 }
707 ']' => {
708 tokens.push(Token::RightBracket);
709 i += 1;
710 }
711 ',' => {
712 tokens.push(Token::Comma);
713 i += 1;
714 }
715 '"' => {
716 let mut name = String::new();
717 i += 1;
718 loop {
719 let Some(&c) = chars.get(i) else {
720 return Err(Error::parser(format!(
721 "Type \"{text}\" has an unterminated quoted name"
722 )));
723 };
724 i += 1;
725 if c == '"' {
726 if chars.get(i) == Some(&'"') {
727 name.push('"');
728 i += 1;
729 continue;
730 }
731 break;
732 }
733 name.push(c);
734 }
735 tokens.push(Token::Quoted(name));
736 }
737 c if c.is_ascii_digit() => {
738 let start = i;
739 while chars.get(i).is_some_and(char::is_ascii_digit) {
740 i += 1;
741 }
742 let digits: String = chars[start..i].iter().collect();
743 let number = digits.parse::<u32>().map_err(|_| {
744 Error::parser(format!("Type \"{text}\" has a number that is too large"))
745 })?;
746 tokens.push(Token::Number(number));
747 }
748 c if c.is_alphabetic() || c == '_' => {
749 let start = i;
750 while chars.get(i).is_some_and(|&c| c.is_alphanumeric() || c == '_') {
751 i += 1;
752 }
753 tokens.push(Token::Word(chars[start..i].iter().collect()));
754 }
755 other => {
756 return Err(Error::parser(format!(
757 "Type \"{text}\" has an unexpected character {other:?}"
758 )));
759 }
760 }
761 }
762 Ok(tokens)
763}
764
765struct TypeParser<'a> {
766 tokens: &'a [Token],
767 position: usize,
768}
769
770impl TypeParser<'_> {
771 fn peek(&self) -> Option<&Token> {
772 self.tokens.get(self.position)
773 }
774
775 fn eat(&mut self, token: &Token) -> bool {
776 if self.peek() == Some(token) {
777 self.position += 1;
778 true
779 } else {
780 false
781 }
782 }
783
784 fn eat_word(&mut self, word: &str) -> bool {
786 match self.peek() {
787 Some(Token::Word(found)) if found.eq_ignore_ascii_case(word) => {
788 self.position += 1;
789 true
790 }
791 _ => false,
792 }
793 }
794
795 fn parse_type(&mut self) -> Result<LogicalType> {
796 let mut ty = self.parse_base()?;
797 loop {
799 if !self.eat(&Token::LeftBracket) {
800 break;
801 }
802 if let Some(&Token::Number(length)) = self.peek() {
803 self.position += 1;
804 expect(self.eat(&Token::RightBracket), "]")?;
805 ty = LogicalType::array(ty, length);
806 } else {
807 expect(self.eat(&Token::RightBracket), "]")?;
808 ty = LogicalType::list(ty);
809 }
810 }
811 Ok(ty)
812 }
813
814 fn parse_base(&mut self) -> Result<LogicalType> {
815 let word = match self.peek().cloned() {
818 Some(Token::Word(word) | Token::Quoted(word)) => {
819 self.position += 1;
820 word
821 }
822 _ => return Err(Error::parser("Expected a type name".to_string())),
823 };
824 let upper = word.to_ascii_uppercase();
825
826 match upper.as_str() {
827 "STRUCT" | "ROW" => return self.parse_fields().map(LogicalType::Struct),
828 "UNION" => return self.parse_fields().map(LogicalType::Union),
829 "MAP" => {
830 expect(self.eat(&Token::LeftParen), "(")?;
831 let key = self.parse_type()?;
832 expect(self.eat(&Token::Comma), ",")?;
833 let value = self.parse_type()?;
834 expect(self.eat(&Token::RightParen), ")")?;
835 return Ok(LogicalType::map(key, value));
836 }
837 "DECIMAL" | "NUMERIC" | "DEC" => {
838 if !self.eat(&Token::LeftParen) {
839 return LogicalType::decimal(18, 3);
842 }
843 let width = self.parse_number()?;
844 let scale = if self.eat(&Token::Comma) { self.parse_number()? } else { 0 };
845 expect(self.eat(&Token::RightParen), ")")?;
846 let narrow = |n: u32| u8::try_from(n).unwrap_or(u8::MAX);
847 return LogicalType::decimal(narrow(width), narrow(scale));
848 }
849 "DOUBLE" => {
852 self.eat_word("PRECISION");
853 return Ok(LogicalType::Double);
854 }
855 "CHARACTER" => {
856 self.eat_word("VARYING");
857 self.eat_length_modifier()?;
858 return Ok(LogicalType::Varchar);
859 }
860 "TIME" | "TIMESTAMP" => {
861 let with_zone = self.eat_time_zone_suffix();
862 return Ok(match (upper.as_str(), with_zone) {
863 ("TIME", false) => LogicalType::Time,
864 ("TIME", true) => LogicalType::TimeTz,
865 (_, false) => LogicalType::Timestamp,
866 (_, true) => LogicalType::TimestampTz,
867 });
868 }
869 _ => {}
870 }
871
872 self.eat_length_modifier()?;
875 alias(&upper).ok_or_else(|| Error::parser(format!("Unrecognized type name \"{word}\"")))
876 }
877
878 fn eat_time_zone_suffix(&mut self) -> bool {
880 let start = self.position;
881 let with = if self.eat_word("WITH") {
882 true
883 } else if self.eat_word("WITHOUT") {
884 false
885 } else {
886 return false;
887 };
888 if self.eat_word("TIME") && self.eat_word("ZONE") {
889 with
890 } else {
891 self.position = start;
892 false
893 }
894 }
895
896 fn eat_length_modifier(&mut self) -> Result<()> {
897 if self.eat(&Token::LeftParen) {
898 self.parse_number()?;
899 expect(self.eat(&Token::RightParen), ")")?;
900 }
901 Ok(())
902 }
903
904 fn parse_fields(&mut self) -> Result<Vec<Field>> {
905 expect(self.eat(&Token::LeftParen), "(")?;
906 let mut fields = Vec::new();
907 if self.eat(&Token::RightParen) {
908 return Ok(fields);
909 }
910 loop {
911 let name = match self.peek().cloned() {
912 Some(Token::Word(name) | Token::Quoted(name)) => {
913 self.position += 1;
914 name
915 }
916 _ => return Err(Error::parser("Expected a field name".to_string())),
917 };
918 let ty = self.parse_type()?;
919 fields.push(Field::new(name, ty));
920 if self.eat(&Token::Comma) {
921 continue;
922 }
923 expect(self.eat(&Token::RightParen), ")")?;
924 return Ok(fields);
925 }
926 }
927
928 fn parse_number(&mut self) -> Result<u32> {
929 match self.peek() {
930 Some(&Token::Number(n)) => {
931 self.position += 1;
932 Ok(n)
933 }
934 _ => Err(Error::parser("Expected a number".to_string())),
935 }
936 }
937}
938
939fn expect(matched: bool, what: &str) -> Result<()> {
940 if matched { Ok(()) } else { Err(Error::parser(format!("Expected \"{what}\""))) }
941}
942
943fn alias(upper: &str) -> Option<LogicalType> {
948 Some(match upper {
949 "NULL" => LogicalType::Null,
950 "BOOLEAN" | "BOOL" | "LOGICAL" => LogicalType::Boolean,
951 "TINYINT" | "INT1" => LogicalType::TinyInt,
952 "SMALLINT" | "INT2" | "SHORT" => LogicalType::SmallInt,
953 "INTEGER" | "INT" | "INT4" | "SIGNED" => LogicalType::Integer,
954 "BIGINT" | "INT8" | "LONG" => LogicalType::BigInt,
955 "HUGEINT" | "INT128" => LogicalType::HugeInt,
956 "UTINYINT" | "UINT1" => LogicalType::UTinyInt,
957 "USMALLINT" | "UINT2" => LogicalType::USmallInt,
958 "UINTEGER" | "UINT4" => LogicalType::UInteger,
959 "UBIGINT" | "UINT8" => LogicalType::UBigInt,
960 "UHUGEINT" | "UINT128" => LogicalType::UHugeInt,
961 "FLOAT" | "FLOAT4" | "REAL" => LogicalType::Float,
962 "FLOAT8" => LogicalType::Double,
963 "VARCHAR" | "CHAR" | "BPCHAR" | "TEXT" | "STRING" => LogicalType::Varchar,
964 "BLOB" | "BYTEA" | "BINARY" | "VARBINARY" => LogicalType::Blob,
965 "BIT" | "BITSTRING" => LogicalType::Bit,
966 "UUID" | "GUID" => LogicalType::Uuid,
967 "DATE" => LogicalType::Date,
968 "TIMETZ" => LogicalType::TimeTz,
969 "DATETIME" => LogicalType::Timestamp,
970 "TIMESTAMP_S" | "TIMESTAMP_SEC" | "TIMESTAMP_SECONDS" => LogicalType::TimestampS,
971 "TIMESTAMP_MS" | "TIMESTAMP_MILLISECONDS" => LogicalType::TimestampMs,
972 "TIMESTAMP_NS" | "TIMESTAMP_NANOSECONDS" => LogicalType::TimestampNs,
973 "TIMESTAMPTZ" => LogicalType::TimestampTz,
974 "INTERVAL" => LogicalType::Interval,
975 _ => return None,
976 })
977}
978
979#[cfg(test)]
980mod promotion_tests {
981 use super::LogicalType;
982
983 #[test]
984 fn a_type_promotes_with_itself_to_itself() {
985 for ty in [
986 LogicalType::Integer,
987 LogicalType::Varchar,
988 LogicalType::Boolean,
989 LogicalType::Struct(vec![]),
990 ] {
991 assert_eq!(ty.promote(&ty), Some(ty.clone()), "{ty} does not promote with itself");
992 }
993 }
994
995 #[test]
996 fn null_takes_the_other_type() {
997 assert_eq!(LogicalType::Null.promote(&LogicalType::Varchar), Some(LogicalType::Varchar));
998 assert_eq!(LogicalType::Date.promote(&LogicalType::Null), Some(LogicalType::Date));
999 assert_eq!(LogicalType::Null.promote(&LogicalType::Null), Some(LogicalType::Null));
1000 }
1001
1002 #[test]
1003 fn the_wider_number_wins() {
1004 assert_eq!(
1005 LogicalType::Integer.promote(&LogicalType::SmallInt),
1006 Some(LogicalType::Integer)
1007 );
1008 assert_eq!(LogicalType::Integer.promote(&LogicalType::Double), Some(LogicalType::Double));
1009 assert_eq!(LogicalType::Float.promote(&LogicalType::Double), Some(LogicalType::Double));
1010 }
1011
1012 #[test]
1015 fn signed_and_unsigned_widen_rather_than_reinterpret() {
1016 assert_eq!(LogicalType::Integer.promote(&LogicalType::UInteger), Some(LogicalType::BigInt));
1017 assert_eq!(
1018 LogicalType::TinyInt.promote(&LogicalType::UTinyInt),
1019 Some(LogicalType::SmallInt)
1020 );
1021 assert_eq!(LogicalType::BigInt.promote(&LogicalType::UBigInt), Some(LogicalType::HugeInt));
1022 }
1023
1024 #[test]
1025 fn promotion_does_not_care_which_side_a_type_is_on() {
1026 let types = [
1027 LogicalType::TinyInt,
1028 LogicalType::UInteger,
1029 LogicalType::BigInt,
1030 LogicalType::Double,
1031 LogicalType::Decimal { width: 10, scale: 2 },
1032 LogicalType::Null,
1033 LogicalType::Varchar,
1034 LogicalType::Date,
1035 LogicalType::Timestamp,
1036 ];
1037 for left in &types {
1038 for right in &types {
1039 assert_eq!(
1040 left.promote(right),
1041 right.promote(left),
1042 "{left} and {right} promote differently depending on the order"
1043 );
1044 }
1045 }
1046 }
1047
1048 #[test]
1049 fn a_decimal_keeps_room_for_both_halves() {
1050 let left = LogicalType::Decimal { width: 5, scale: 4 };
1051 let right = LogicalType::Decimal { width: 5, scale: 1 };
1052 assert_eq!(left.promote(&right), Some(LogicalType::Decimal { width: 8, scale: 4 }));
1053 }
1054
1055 #[test]
1056 fn an_integer_next_to_a_decimal_widens_the_decimal() {
1057 let decimal = LogicalType::Decimal { width: 5, scale: 2 };
1058 assert_eq!(
1059 decimal.promote(&LogicalType::Integer),
1060 Some(LogicalType::Decimal { width: 12, scale: 2 })
1061 );
1062 }
1063
1064 #[test]
1069 fn a_bigint_leaves_room_for_one_digit_fewer_than_a_ubigint() {
1070 let decimal = LogicalType::Decimal { width: 4, scale: 2 };
1071 assert_eq!(
1072 decimal.promote(&LogicalType::BigInt),
1073 Some(LogicalType::Decimal { width: 21, scale: 2 })
1074 );
1075 assert_eq!(
1076 decimal.promote(&LogicalType::UBigInt),
1077 Some(LogicalType::Decimal { width: 22, scale: 2 })
1078 );
1079 assert_eq!(decimal.promote(&LogicalType::Integer), decimal.promote(&LogicalType::UInteger));
1080 }
1081
1082 #[test]
1083 fn a_date_and_a_timestamp_meet_at_the_timestamp() {
1084 assert_eq!(
1085 LogicalType::Date.promote(&LogicalType::Timestamp),
1086 Some(LogicalType::Timestamp)
1087 );
1088 assert_eq!(
1089 LogicalType::TimestampS.promote(&LogicalType::TimestampNs),
1090 Some(LogicalType::TimestampNs)
1091 );
1092 }
1093
1094 #[test]
1097 fn types_that_do_not_meet_say_so() {
1098 assert_eq!(LogicalType::Timestamp.promote(&LogicalType::Interval), None);
1099 assert_eq!(LogicalType::Integer.promote(&LogicalType::Varchar), None);
1100 assert_eq!(LogicalType::Boolean.promote(&LogicalType::Integer), None);
1101 }
1102
1103 #[test]
1104 fn a_list_promotes_by_its_element() {
1105 let left = LogicalType::list(LogicalType::Integer);
1106 let right = LogicalType::list(LogicalType::BigInt);
1107 assert_eq!(left.promote(&right), Some(LogicalType::list(LogicalType::BigInt)));
1108 assert_eq!(left.promote(&LogicalType::list(LogicalType::Varchar)), None);
1109 }
1110}
1111
1112#[cfg(test)]
1113mod tests {
1114 use super::{Field, LogicalType, PhysicalType};
1115
1116 fn every_type() -> Vec<LogicalType> {
1119 vec![
1120 LogicalType::Null,
1121 LogicalType::Boolean,
1122 LogicalType::TinyInt,
1123 LogicalType::SmallInt,
1124 LogicalType::Integer,
1125 LogicalType::BigInt,
1126 LogicalType::HugeInt,
1127 LogicalType::UTinyInt,
1128 LogicalType::USmallInt,
1129 LogicalType::UInteger,
1130 LogicalType::UBigInt,
1131 LogicalType::UHugeInt,
1132 LogicalType::Float,
1133 LogicalType::Double,
1134 LogicalType::Decimal { width: 18, scale: 3 },
1135 LogicalType::Decimal { width: 38, scale: 0 },
1136 LogicalType::Varchar,
1137 LogicalType::Blob,
1138 LogicalType::Bit,
1139 LogicalType::Uuid,
1140 LogicalType::Date,
1141 LogicalType::Time,
1142 LogicalType::TimeTz,
1143 LogicalType::Timestamp,
1144 LogicalType::TimestampS,
1145 LogicalType::TimestampMs,
1146 LogicalType::TimestampNs,
1147 LogicalType::TimestampTz,
1148 LogicalType::Interval,
1149 LogicalType::list(LogicalType::Integer),
1150 LogicalType::list(LogicalType::list(LogicalType::Varchar)),
1151 LogicalType::array(LogicalType::Double, 3),
1152 LogicalType::map(LogicalType::Varchar, LogicalType::Integer),
1153 LogicalType::Struct(vec![
1154 Field::new("a", LogicalType::Integer),
1155 Field::new("b", LogicalType::list(LogicalType::Varchar)),
1156 ]),
1157 LogicalType::Union(vec![
1158 Field::new("num", LogicalType::Integer),
1159 Field::new("str", LogicalType::Varchar),
1160 ]),
1161 ]
1162 }
1163
1164 #[test]
1165 fn every_type_survives_being_printed_and_read_back() {
1166 for ty in every_type() {
1170 let printed = ty.to_string();
1171 let parsed = LogicalType::parse(&printed)
1172 .unwrap_or_else(|e| panic!("{printed} did not parse back: {e}"));
1173 assert_eq!(parsed, ty, "{printed} parsed to something else");
1174 }
1175 }
1176
1177 #[test]
1178 fn a_field_name_that_needs_quoting_gets_quoted() {
1179 let ty = LogicalType::Struct(vec![
1180 Field::new("plain", LogicalType::Integer),
1181 Field::new("has space", LogicalType::Integer),
1182 Field::new("has\"quote", LogicalType::Integer),
1183 Field::new("2leading", LogicalType::Integer),
1184 ]);
1185 assert_eq!(
1186 ty.to_string(),
1187 "STRUCT(plain INTEGER, \"has space\" INTEGER, \"has\"\"quote\" INTEGER, \
1188 \"2leading\" INTEGER)"
1189 );
1190 assert_eq!(LogicalType::parse(&ty.to_string()).unwrap(), ty);
1191 }
1192
1193 #[test]
1194 fn the_duckdb_aliases_resolve() {
1195 let cases = [
1196 ("int4", LogicalType::Integer),
1197 ("INT", LogicalType::Integer),
1198 ("signed", LogicalType::Integer),
1199 ("int8", LogicalType::BigInt),
1200 ("float4", LogicalType::Float),
1201 ("float8", LogicalType::Double),
1202 ("double precision", LogicalType::Double),
1203 ("text", LogicalType::Varchar),
1204 ("varchar(10)", LogicalType::Varchar),
1205 ("character varying(255)", LogicalType::Varchar),
1206 ("bool", LogicalType::Boolean),
1207 ("datetime", LogicalType::Timestamp),
1208 ("numeric(9, 2)", LogicalType::Decimal { width: 9, scale: 2 }),
1209 ("decimal", LogicalType::Decimal { width: 18, scale: 3 }),
1210 ("timestamp without time zone", LogicalType::Timestamp),
1211 ("timestamp with time zone", LogicalType::TimestampTz),
1212 ("time with time zone", LogicalType::TimeTz),
1213 ];
1214 for (text, expected) in cases {
1215 assert_eq!(LogicalType::parse(text).unwrap(), expected, "{text}");
1216 }
1217 }
1218
1219 #[test]
1220 fn list_and_array_suffixes_bind_left_to_right() {
1221 assert_eq!(
1222 LogicalType::parse("INTEGER[][3]").unwrap(),
1223 LogicalType::array(LogicalType::list(LogicalType::Integer), 3)
1224 );
1225 assert_eq!(
1226 LogicalType::parse("STRUCT(a INT)[]").unwrap(),
1227 LogicalType::list(LogicalType::Struct(vec![Field::new("a", LogicalType::Integer)]))
1228 );
1229 }
1230
1231 #[test]
1232 fn a_decimal_is_stored_in_the_narrowest_integer_that_holds_it() {
1233 assert_eq!(LogicalType::decimal(4, 2).unwrap().physical(), PhysicalType::Int16);
1234 assert_eq!(LogicalType::decimal(9, 2).unwrap().physical(), PhysicalType::Int32);
1235 assert_eq!(LogicalType::decimal(18, 2).unwrap().physical(), PhysicalType::Int64);
1236 assert_eq!(LogicalType::decimal(38, 2).unwrap().physical(), PhysicalType::Int128);
1237 }
1238
1239 #[test]
1243 fn a_message_names_the_type_by_what_it_is_stored_in() {
1244 assert_eq!(LogicalType::Boolean.physical_name(), "BOOL");
1245 assert_eq!(LogicalType::TinyInt.physical_name(), "INT8");
1246 assert_eq!(LogicalType::Integer.physical_name(), "INT32");
1247 assert_eq!(LogicalType::UBigInt.physical_name(), "UINT64");
1248 assert_eq!(LogicalType::HugeInt.physical_name(), "INT128");
1249 assert_eq!(LogicalType::Float.physical_name(), "FLOAT");
1250 assert_eq!(LogicalType::Varchar.physical_name(), "VARCHAR");
1251 assert_eq!(LogicalType::Date.physical_name(), "DATE");
1252 assert_eq!(LogicalType::decimal(4, 2).unwrap().physical_name(), "DECIMAL(4)");
1253 assert_eq!(LogicalType::decimal(11, 0).unwrap().physical_name(), "DECIMAL(18)");
1254 assert_eq!(LogicalType::decimal(18, 8).unwrap().physical_name(), "DECIMAL(18)");
1255 assert_eq!(LogicalType::decimal(38, 2).unwrap().physical_name(), "DECIMAL(38)");
1256 assert_eq!(LogicalType::Integer.decimal_storage(), None);
1257 }
1258
1259 #[test]
1260 fn a_decimal_outside_the_bounds_is_rejected_rather_than_clamped() {
1261 assert!(LogicalType::decimal(0, 0).is_err());
1262 assert!(LogicalType::decimal(39, 0).is_err());
1263 assert!(LogicalType::decimal(4, 5).is_err());
1264 assert!(LogicalType::parse("DECIMAL(39,0)").is_err());
1265 }
1266
1267 #[test]
1268 fn a_date_and_an_integer_share_a_layout_and_not_a_meaning() {
1269 assert_eq!(LogicalType::Date.physical(), LogicalType::Integer.physical());
1270 assert_ne!(LogicalType::Date, LogicalType::Integer);
1271 assert!(LogicalType::Date.is_temporal());
1272 assert!(!LogicalType::Date.is_numeric());
1273 }
1274
1275 #[test]
1276 fn nesting_reports_its_children_in_child_column_order() {
1277 let ty = LogicalType::map(LogicalType::Varchar, LogicalType::Integer);
1278 assert!(ty.is_nested());
1279 assert_eq!(ty.children(), vec![LogicalType::Varchar, LogicalType::Integer]);
1280 assert_eq!(LogicalType::Integer.children(), Vec::new());
1281 }
1282
1283 #[test]
1284 fn text_that_is_not_a_type_is_rejected_with_the_word_that_broke_it() {
1285 let error = LogicalType::parse("INTEGRE").unwrap_err();
1286 assert!(error.message().contains("INTEGRE"), "{error}");
1287 assert!(LogicalType::parse("INTEGER JUNK").is_err());
1288 assert!(LogicalType::parse("STRUCT(a)").is_err());
1289 assert!(LogicalType::parse("").is_err());
1290 }
1291}