From 1ef31ec4c51eab9104734af95b782a05d8569085 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Tue, 18 Oct 2022 22:00:43 +0800 Subject: [PATCH 1/5] feat: add support for custom types with argument --- src/ast/data_type.rs | 3 +++ src/parser.rs | 60 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/ast/data_type.rs b/src/ast/data_type.rs index 58c1305ac..4b74d03e0 100644 --- a/src/ast/data_type.rs +++ b/src/ast/data_type.rs @@ -130,6 +130,8 @@ pub enum DataType { Bytea, /// Custom type such as enums Custom(ObjectName), + /// Custom type with arguments + CustomWithArgs(ObjectName, Vec), /// Arrays Array(Box), /// Enums @@ -218,6 +220,7 @@ impl fmt::Display for DataType { DataType::Bytea => write!(f, "BYTEA"), DataType::Array(ty) => write!(f, "{}[]", ty), DataType::Custom(ty) => write!(f, "{}", ty), + DataType::CustomWithArgs(ty, args) => write!(f, "{}({})", ty, args.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..c629180c8 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(arguments) = self.parse_optional_type_arguments()? { + Ok(DataType::CustomWithArgs(type_name, arguments)) + } else { + Ok(DataType::Custom(type_name)) + } } }, unexpected => self.expected("a data type name", unexpected), @@ -3870,6 +3874,28 @@ impl<'a> Parser<'a> { } } + pub fn parse_optional_type_arguments(&mut self) -> Result>, ParserError> { + if self.consume_token(&Token::LParen) { + let mut args = Vec::new(); + loop { + match self.next_token() { + Token::Word(w) => args.push(w.to_string()), + Token::Number(n, _) => args.push(n), + Token::SingleQuotedString(s) => args.push(s), + unexpected => self.expected("type argument", unexpected)?, + } + + if !self.consume_token(&Token::Comma) { + break; + } + } + self.expect_token(&Token::RParen)?; + Ok(Some(args)) + } else { + Ok(None) + } + } + pub fn parse_delete(&mut self) -> Result { self.expect_keyword(Keyword::FROM)?; let table_name = self.parse_table_factor()?; @@ -5489,7 +5515,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 +5692,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()]),) + ); + + test_parse_data_type!( + dialect, + "GEOMETRY(POINT)", + DataType::CustomWithArgs( + ObjectName(vec!["GEOMETRY".into()]), + vec!["POINT".to_string()] + ) + ); + + test_parse_data_type!( + dialect, + "GEOMETRY(POINT, 4326)", + DataType::CustomWithArgs( + ObjectName(vec!["GEOMETRY".into()]), + vec!["POINT".to_string(), "4326".to_string()] + ) + ); + } + #[test] fn test_ansii_exact_numeric_types() { // Exact numeric types: From d642d5214adf4c8a0a4de17226b911c42a6524f1 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Wed, 19 Oct 2022 10:21:04 +0800 Subject: [PATCH 2/5] refactor: add support for number and string as type arguments --- src/parser.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index c629180c8..cf418fec8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3882,14 +3882,17 @@ impl<'a> Parser<'a> { Token::Word(w) => args.push(w.to_string()), Token::Number(n, _) => args.push(n), Token::SingleQuotedString(s) => args.push(s), - unexpected => self.expected("type argument", unexpected)?, - } - if !self.consume_token(&Token::Comma) { - break; + Token::Comma => { + continue; + } + Token::RParen => { + break; + } + unexpected => self.expected("type argument", unexpected)?, } } - self.expect_token(&Token::RParen)?; + Ok(Some(args)) } else { Ok(None) From cd053d144efe6c479375fa6102a4f1533c781afd Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Wed, 19 Oct 2022 18:10:38 +0800 Subject: [PATCH 3/5] fix: ignore CustomWithArgs when parsing TypedString --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index cf418fec8..13babfc1e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -412,7 +412,7 @@ impl<'a> Parser<'a> { // name, resulting in `NOT 'a'` being recognized as a `TypedString` instead of // an unary negation `NOT ('a' LIKE 'b')`. To solve this, we don't accept the // `type 'string'` syntax for the custom data types at all. - DataType::Custom(..) => parser_err!("dummy"), + DataType::Custom(..) | DataType::CustomWithArgs(..) => parser_err!("dummy"), data_type => Ok(Expr::TypedString { data_type, value: parser.parse_literal_string()?, From 5aa13eb5316ee0cf338c4f9fe52cd9f94c9dac9b Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Wed, 19 Oct 2022 21:24:44 +0800 Subject: [PATCH 4/5] refactor: merge CustomWithArgs into Custom --- src/ast/data_type.rs | 13 ++++++++----- src/parser.rs | 12 ++++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/ast/data_type.rs b/src/ast/data_type.rs index 4b74d03e0..b6f675ccd 100644 --- a/src/ast/data_type.rs +++ b/src/ast/data_type.rs @@ -129,9 +129,7 @@ pub enum DataType { /// Bytea Bytea, /// Custom type such as enums - Custom(ObjectName), - /// Custom type with arguments - CustomWithArgs(ObjectName, Vec), + Custom(ObjectName, Vec), /// Arrays Array(Box), /// Enums @@ -219,8 +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::CustomWithArgs(ty, args) => write!(f, "{}({})", ty, args.join(", ")), + DataType::Custom(ty, args) => { + if args.is_empty() { + write!(f, "{}", ty) + } else { + write!(f, "{}({})", ty, args.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 13babfc1e..2ca4cd74e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -412,7 +412,7 @@ impl<'a> Parser<'a> { // name, resulting in `NOT 'a'` being recognized as a `TypedString` instead of // an unary negation `NOT ('a' LIKE 'b')`. To solve this, we don't accept the // `type 'string'` syntax for the custom data types at all. - DataType::Custom(..) | DataType::CustomWithArgs(..) => parser_err!("dummy"), + DataType::Custom(..) => parser_err!("dummy"), data_type => Ok(Expr::TypedString { data_type, value: parser.parse_literal_string()?, @@ -3625,9 +3625,9 @@ impl<'a> Parser<'a> { self.prev_token(); let type_name = self.parse_object_name()?; if let Some(arguments) = self.parse_optional_type_arguments()? { - Ok(DataType::CustomWithArgs(type_name, arguments)) + Ok(DataType::Custom(type_name, arguments)) } else { - Ok(DataType::Custom(type_name)) + Ok(DataType::Custom(type_name, vec![])) } } }, @@ -5703,13 +5703,13 @@ mod tests { test_parse_data_type!( dialect, "GEOMETRY", - DataType::Custom(ObjectName(vec!["GEOMETRY".into()]),) + DataType::Custom(ObjectName(vec!["GEOMETRY".into()]), vec![]) ); test_parse_data_type!( dialect, "GEOMETRY(POINT)", - DataType::CustomWithArgs( + DataType::Custom( ObjectName(vec!["GEOMETRY".into()]), vec!["POINT".to_string()] ) @@ -5718,7 +5718,7 @@ mod tests { test_parse_data_type!( dialect, "GEOMETRY(POINT, 4326)", - DataType::CustomWithArgs( + DataType::Custom( ObjectName(vec!["GEOMETRY".into()]), vec!["POINT".to_string(), "4326".to_string()] ) From 46ff40784a2ec4a085fe171444a8a490943f42bb Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Thu, 20 Oct 2022 11:00:24 +0800 Subject: [PATCH 5/5] refactor: rename arguments to modifiers --- src/ast/data_type.rs | 6 +++--- src/parser.rs | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ast/data_type.rs b/src/ast/data_type.rs index b6f675ccd..a66761167 100644 --- a/src/ast/data_type.rs +++ b/src/ast/data_type.rs @@ -217,11 +217,11 @@ impl fmt::Display for DataType { DataType::String => write!(f, "STRING"), DataType::Bytea => write!(f, "BYTEA"), DataType::Array(ty) => write!(f, "{}[]", ty), - DataType::Custom(ty, args) => { - if args.is_empty() { + DataType::Custom(ty, modifiers) => { + if modifiers.is_empty() { write!(f, "{}", ty) } else { - write!(f, "{}({})", ty, args.join(", ")) + write!(f, "{}({})", ty, modifiers.join(", ")) } } DataType::Enum(vals) => { diff --git a/src/parser.rs b/src/parser.rs index 2ca4cd74e..3dcfb4310 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3624,8 +3624,8 @@ impl<'a> Parser<'a> { _ => { self.prev_token(); let type_name = self.parse_object_name()?; - if let Some(arguments) = self.parse_optional_type_arguments()? { - Ok(DataType::Custom(type_name, arguments)) + if let Some(modifiers) = self.parse_optional_type_modifiers()? { + Ok(DataType::Custom(type_name, modifiers)) } else { Ok(DataType::Custom(type_name, vec![])) } @@ -3874,14 +3874,14 @@ impl<'a> Parser<'a> { } } - pub fn parse_optional_type_arguments(&mut self) -> Result>, ParserError> { + pub fn parse_optional_type_modifiers(&mut self) -> Result>, ParserError> { if self.consume_token(&Token::LParen) { - let mut args = Vec::new(); + let mut modifiers = Vec::new(); loop { match self.next_token() { - Token::Word(w) => args.push(w.to_string()), - Token::Number(n, _) => args.push(n), - Token::SingleQuotedString(s) => args.push(s), + Token::Word(w) => modifiers.push(w.to_string()), + Token::Number(n, _) => modifiers.push(n), + Token::SingleQuotedString(s) => modifiers.push(s), Token::Comma => { continue; @@ -3889,11 +3889,11 @@ impl<'a> Parser<'a> { Token::RParen => { break; } - unexpected => self.expected("type argument", unexpected)?, + unexpected => self.expected("type modifiers", unexpected)?, } } - Ok(Some(args)) + Ok(Some(modifiers)) } else { Ok(None) }