diff --git a/src/ast/data_type.rs b/src/ast/data_type.rs index 506de815..315d22b5 100644 --- a/src/ast/data_type.rs +++ b/src/ast/data_type.rs @@ -518,18 +518,29 @@ impl fmt::Display for ExactNumberInfo { #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] -pub struct CharacterLength { - /// Default (if VARYING) or maximum (if not VARYING) length - pub length: u64, - /// Optional unit. If not informed, the ANSI handles it as CHARACTERS implicitly - pub unit: Option, +pub enum CharacterLength { + IntegerLength { + /// Default (if VARYING) or maximum (if not VARYING) length + length: u64, + /// Optional unit. If not informed, the ANSI handles it as CHARACTERS implicitly + unit: Option, + }, + /// VARCHAR(MAX) or NVARCHAR(MAX), used in T-SQL (Miscrosoft SQL Server) + Max, } impl fmt::Display for CharacterLength { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.length)?; - if let Some(unit) = &self.unit { - write!(f, " {unit}")?; + match self { + CharacterLength::IntegerLength { length, unit } => { + write!(f, "{}", length)?; + if let Some(unit) = unit { + write!(f, " {unit}")?; + } + } + CharacterLength::Max => { + write!(f, "MAX")?; + } } Ok(()) } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 1964437e..1cf47f5c 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -5617,6 +5617,9 @@ impl<'a> Parser<'a> { } pub fn parse_character_length(&mut self) -> Result { + if self.parse_keyword(Keyword::MAX) { + return Ok(CharacterLength::Max); + } let length = self.parse_literal_uint()?; let unit = if self.parse_keyword(Keyword::CHARACTERS) { Some(CharLengthUnits::Characters) @@ -5625,8 +5628,7 @@ impl<'a> Parser<'a> { } else { None }; - - Ok(CharacterLength { length, unit }) + Ok(CharacterLength::IntegerLength { length, unit }) } pub fn parse_optional_precision_scale( @@ -8111,7 +8113,7 @@ mod tests { test_parse_data_type!( dialect, "CHARACTER(20)", - DataType::Character(Some(CharacterLength { + DataType::Character(Some(CharacterLength::IntegerLength { length: 20, unit: None })) @@ -8120,7 +8122,7 @@ mod tests { test_parse_data_type!( dialect, "CHARACTER(20 CHARACTERS)", - DataType::Character(Some(CharacterLength { + DataType::Character(Some(CharacterLength::IntegerLength { length: 20, unit: Some(CharLengthUnits::Characters) })) @@ -8129,7 +8131,7 @@ mod tests { test_parse_data_type!( dialect, "CHARACTER(20 OCTETS)", - DataType::Character(Some(CharacterLength { + DataType::Character(Some(CharacterLength::IntegerLength { length: 20, unit: Some(CharLengthUnits::Octets) })) @@ -8140,7 +8142,7 @@ mod tests { test_parse_data_type!( dialect, "CHAR(20)", - DataType::Char(Some(CharacterLength { + DataType::Char(Some(CharacterLength::IntegerLength { length: 20, unit: None })) @@ -8149,7 +8151,7 @@ mod tests { test_parse_data_type!( dialect, "CHAR(20 CHARACTERS)", - DataType::Char(Some(CharacterLength { + DataType::Char(Some(CharacterLength::IntegerLength { length: 20, unit: Some(CharLengthUnits::Characters) })) @@ -8158,7 +8160,7 @@ mod tests { test_parse_data_type!( dialect, "CHAR(20 OCTETS)", - DataType::Char(Some(CharacterLength { + DataType::Char(Some(CharacterLength::IntegerLength { length: 20, unit: Some(CharLengthUnits::Octets) })) @@ -8167,7 +8169,7 @@ mod tests { test_parse_data_type!( dialect, "CHARACTER VARYING(20)", - DataType::CharacterVarying(Some(CharacterLength { + DataType::CharacterVarying(Some(CharacterLength::IntegerLength { length: 20, unit: None })) @@ -8176,7 +8178,7 @@ mod tests { test_parse_data_type!( dialect, "CHARACTER VARYING(20 CHARACTERS)", - DataType::CharacterVarying(Some(CharacterLength { + DataType::CharacterVarying(Some(CharacterLength::IntegerLength { length: 20, unit: Some(CharLengthUnits::Characters) })) @@ -8185,7 +8187,7 @@ mod tests { test_parse_data_type!( dialect, "CHARACTER VARYING(20 OCTETS)", - DataType::CharacterVarying(Some(CharacterLength { + DataType::CharacterVarying(Some(CharacterLength::IntegerLength { length: 20, unit: Some(CharLengthUnits::Octets) })) @@ -8194,7 +8196,7 @@ mod tests { test_parse_data_type!( dialect, "CHAR VARYING(20)", - DataType::CharVarying(Some(CharacterLength { + DataType::CharVarying(Some(CharacterLength::IntegerLength { length: 20, unit: None })) @@ -8203,7 +8205,7 @@ mod tests { test_parse_data_type!( dialect, "CHAR VARYING(20 CHARACTERS)", - DataType::CharVarying(Some(CharacterLength { + DataType::CharVarying(Some(CharacterLength::IntegerLength { length: 20, unit: Some(CharLengthUnits::Characters) })) @@ -8212,7 +8214,7 @@ mod tests { test_parse_data_type!( dialect, "CHAR VARYING(20 OCTETS)", - DataType::CharVarying(Some(CharacterLength { + DataType::CharVarying(Some(CharacterLength::IntegerLength { length: 20, unit: Some(CharLengthUnits::Octets) })) @@ -8221,7 +8223,7 @@ mod tests { test_parse_data_type!( dialect, "VARCHAR(20)", - DataType::Varchar(Some(CharacterLength { + DataType::Varchar(Some(CharacterLength::IntegerLength { length: 20, unit: None })) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index befdf512..c4499731 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -2381,7 +2381,7 @@ fn parse_create_table() { vec![ ColumnDef { name: "name".into(), - data_type: DataType::Varchar(Some(CharacterLength { + data_type: DataType::Varchar(Some(CharacterLength::IntegerLength { length: 100, unit: None, })), @@ -2929,7 +2929,7 @@ fn parse_create_external_table() { vec![ ColumnDef { name: "name".into(), - data_type: DataType::Varchar(Some(CharacterLength { + data_type: DataType::Varchar(Some(CharacterLength::IntegerLength { length: 100, unit: None, })), @@ -3000,7 +3000,7 @@ fn parse_create_or_replace_external_table() { columns, vec![ColumnDef { name: "name".into(), - data_type: DataType::Varchar(Some(CharacterLength { + data_type: DataType::Varchar(Some(CharacterLength::IntegerLength { length: 100, unit: None, })), diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 4aa993fa..73994681 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -127,7 +127,7 @@ fn parse_create_procedure() { value: "@bar".into(), quote_style: None }, - data_type: DataType::Varchar(Some(CharacterLength { + data_type: DataType::Varchar(Some(CharacterLength::IntegerLength { length: 256, unit: None })) @@ -431,6 +431,11 @@ fn parse_like() { chk(true); } +#[test] +fn parse_cast_varchar_max() { + ms_and_generic().verified_expr("CAST('foo' AS VARCHAR(MAX))"); +} + #[test] fn parse_similar_to() { fn chk(negated: bool) { diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 606d835f..c1fba820 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -349,10 +349,12 @@ fn parse_create_table_with_defaults() { }, ColumnDef { name: "first_name".into(), - data_type: DataType::CharacterVarying(Some(CharacterLength { - length: 45, - unit: None - })), + data_type: DataType::CharacterVarying(Some( + CharacterLength::IntegerLength { + length: 45, + unit: None + } + )), collation: None, options: vec![ColumnOptionDef { name: None, @@ -361,10 +363,12 @@ fn parse_create_table_with_defaults() { }, ColumnDef { name: "last_name".into(), - data_type: DataType::CharacterVarying(Some(CharacterLength { - length: 45, - unit: None - })), + data_type: DataType::CharacterVarying(Some( + CharacterLength::IntegerLength { + length: 45, + unit: None + } + )), collation: Some(ObjectName(vec![Ident::with_quote('"', "es_ES")])), options: vec![ColumnOptionDef { name: None, @@ -373,10 +377,12 @@ fn parse_create_table_with_defaults() { }, ColumnDef { name: "email".into(), - data_type: DataType::CharacterVarying(Some(CharacterLength { - length: 50, - unit: None - })), + data_type: DataType::CharacterVarying(Some( + CharacterLength::IntegerLength { + length: 50, + unit: None + } + )), collation: None, options: vec![], },