diff --git a/src/ast/data_type.rs b/src/ast/data_type.rs index 58c1305ac..a66761167 100644 --- a/src/ast/data_type.rs +++ b/src/ast/data_type.rs @@ -129,7 +129,7 @@ pub enum DataType { /// Bytea Bytea, /// Custom type such as enums - Custom(ObjectName), + Custom(ObjectName, Vec), /// Arrays Array(Box), /// Enums @@ -217,7 +217,13 @@ impl fmt::Display for DataType { DataType::String => write!(f, "STRING"), DataType::Bytea => write!(f, "BYTEA"), DataType::Array(ty) => write!(f, "{}[]", ty), - DataType::Custom(ty) => write!(f, "{}", ty), + DataType::Custom(ty, modifiers) => { + if modifiers.is_empty() { + write!(f, "{}", ty) + } else { + write!(f, "{}({})", ty, modifiers.join(", ")) + } + } DataType::Enum(vals) => { write!(f, "ENUM(")?; for (i, v) in vals.iter().enumerate() { diff --git a/src/parser.rs b/src/parser.rs index 045f7fa23..3dcfb4310 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3624,7 +3624,11 @@ impl<'a> Parser<'a> { _ => { self.prev_token(); let type_name = self.parse_object_name()?; - Ok(DataType::Custom(type_name)) + if let Some(modifiers) = self.parse_optional_type_modifiers()? { + Ok(DataType::Custom(type_name, modifiers)) + } else { + Ok(DataType::Custom(type_name, vec![])) + } } }, unexpected => self.expected("a data type name", unexpected), @@ -3870,6 +3874,31 @@ impl<'a> Parser<'a> { } } + pub fn parse_optional_type_modifiers(&mut self) -> Result>, ParserError> { + if self.consume_token(&Token::LParen) { + let mut modifiers = Vec::new(); + loop { + match self.next_token() { + Token::Word(w) => modifiers.push(w.to_string()), + Token::Number(n, _) => modifiers.push(n), + Token::SingleQuotedString(s) => modifiers.push(s), + + Token::Comma => { + continue; + } + Token::RParen => { + break; + } + unexpected => self.expected("type modifiers", unexpected)?, + } + } + + Ok(Some(modifiers)) + } else { + Ok(None) + } + } + pub fn parse_delete(&mut self) -> Result { self.expect_keyword(Keyword::FROM)?; let table_name = self.parse_table_factor()?; @@ -5489,7 +5518,7 @@ mod tests { #[cfg(test)] mod test_parse_data_type { use crate::ast::{ - CharLengthUnits, CharacterLength, DataType, ExactNumberInfo, TimezoneInfo, + CharLengthUnits, CharacterLength, DataType, ExactNumberInfo, ObjectName, TimezoneInfo, }; use crate::dialect::{AnsiDialect, GenericDialect}; use crate::test_utils::TestedDialects; @@ -5666,6 +5695,36 @@ mod tests { test_parse_data_type!(dialect, "CLOB(20)", DataType::Clob(Some(20))); } + #[test] + fn test_parse_custom_types() { + let dialect = TestedDialects { + dialects: vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})], + }; + test_parse_data_type!( + dialect, + "GEOMETRY", + DataType::Custom(ObjectName(vec!["GEOMETRY".into()]), vec![]) + ); + + test_parse_data_type!( + dialect, + "GEOMETRY(POINT)", + DataType::Custom( + ObjectName(vec!["GEOMETRY".into()]), + vec!["POINT".to_string()] + ) + ); + + test_parse_data_type!( + dialect, + "GEOMETRY(POINT, 4326)", + DataType::Custom( + ObjectName(vec!["GEOMETRY".into()]), + vec!["POINT".to_string(), "4326".to_string()] + ) + ); + } + #[test] fn test_ansii_exact_numeric_types() { // Exact numeric types: