diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs index be2cc007..de83d507 100644 --- a/src/dialect/generic.rs +++ b/src/dialect/generic.rs @@ -183,4 +183,8 @@ impl Dialect for GenericDialect { fn supports_select_wildcard_exclude(&self) -> bool { true } + + fn supports_data_type_signed_suffix(&self) -> bool { + true + } } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index 34cb445c..9003220c 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -1136,6 +1136,18 @@ pub trait Dialect: Debug + Any { fn supports_notnull_operator(&self) -> bool { false } + + /// Returns true if this dialect allows an optional `SIGNED` suffix after integer data types. + /// + /// Example: + /// ```sql + /// CREATE TABLE t (i INT(20) SIGNED); + /// ``` + /// + /// Note that this is canonicalized to `INT(20)`. + fn supports_data_type_signed_suffix(&self) -> bool { + false + } } /// This represents the operators for which precedence must be defined diff --git a/src/dialect/mysql.rs b/src/dialect/mysql.rs index f7b5f574..6cf24e14 100644 --- a/src/dialect/mysql.rs +++ b/src/dialect/mysql.rs @@ -154,6 +154,10 @@ impl Dialect for MySqlDialect { fn supports_comma_separated_set_assignments(&self) -> bool { true } + + fn supports_data_type_signed_suffix(&self) -> bool { + true + } } /// `LOCK TABLES` diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 29e00b06..3bf03605 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9848,6 +9848,9 @@ impl<'a> Parser<'a> { if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::TinyIntUnsigned(optional_precision?)) } else { + if dialect.supports_data_type_signed_suffix() { + let _ = self.parse_keyword(Keyword::SIGNED); + } Ok(DataType::TinyInt(optional_precision?)) } } @@ -9864,6 +9867,9 @@ impl<'a> Parser<'a> { if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::SmallIntUnsigned(optional_precision?)) } else { + if dialect.supports_data_type_signed_suffix() { + let _ = self.parse_keyword(Keyword::SIGNED); + } Ok(DataType::SmallInt(optional_precision?)) } } @@ -9872,6 +9878,9 @@ impl<'a> Parser<'a> { if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::MediumIntUnsigned(optional_precision?)) } else { + if dialect.supports_data_type_signed_suffix() { + let _ = self.parse_keyword(Keyword::SIGNED); + } Ok(DataType::MediumInt(optional_precision?)) } } @@ -9880,6 +9889,9 @@ impl<'a> Parser<'a> { if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::IntUnsigned(optional_precision?)) } else { + if dialect.supports_data_type_signed_suffix() { + let _ = self.parse_keyword(Keyword::SIGNED); + } Ok(DataType::Int(optional_precision?)) } } @@ -9909,6 +9921,9 @@ impl<'a> Parser<'a> { if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::IntegerUnsigned(optional_precision?)) } else { + if dialect.supports_data_type_signed_suffix() { + let _ = self.parse_keyword(Keyword::SIGNED); + } Ok(DataType::Integer(optional_precision?)) } } @@ -9917,6 +9932,9 @@ impl<'a> Parser<'a> { if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::BigIntUnsigned(optional_precision?)) } else { + if dialect.supports_data_type_signed_suffix() { + let _ = self.parse_keyword(Keyword::SIGNED); + } Ok(DataType::BigInt(optional_precision?)) } } diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 82b45740..3ea0543f 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -1705,6 +1705,51 @@ fn parse_create_table_unsigned() { } } +#[test] +fn parse_signed_data_types() { + let sql = "CREATE TABLE foo (bar_tinyint TINYINT(3) SIGNED, bar_smallint SMALLINT(5) SIGNED, bar_mediumint MEDIUMINT(13) SIGNED, bar_int INT(11) SIGNED, bar_bigint BIGINT(20) SIGNED)"; + let canonical = "CREATE TABLE foo (bar_tinyint TINYINT(3), bar_smallint SMALLINT(5), bar_mediumint MEDIUMINT(13), bar_int INT(11), bar_bigint BIGINT(20))"; + match mysql().one_statement_parses_to(sql, canonical) { + Statement::CreateTable(CreateTable { name, columns, .. }) => { + assert_eq!(name.to_string(), "foo"); + assert_eq!( + vec![ + ColumnDef { + name: Ident::new("bar_tinyint"), + data_type: DataType::TinyInt(Some(3)), + options: vec![], + }, + ColumnDef { + name: Ident::new("bar_smallint"), + data_type: DataType::SmallInt(Some(5)), + options: vec![], + }, + ColumnDef { + name: Ident::new("bar_mediumint"), + data_type: DataType::MediumInt(Some(13)), + options: vec![], + }, + ColumnDef { + name: Ident::new("bar_int"), + data_type: DataType::Int(Some(11)), + options: vec![], + }, + ColumnDef { + name: Ident::new("bar_bigint"), + data_type: DataType::BigInt(Some(20)), + options: vec![], + }, + ], + columns + ); + } + _ => unreachable!(), + } + all_dialects_except(|d| d.supports_data_type_signed_suffix()) + .run_parser_method(sql, |p| p.parse_statement()) + .expect_err("SIGNED suffix should not be allowed"); +} + #[test] fn parse_simple_insert() { let sql = r"INSERT INTO tasks (title, priority) VALUES ('Test Some Inserts', 1), ('Test Entry 2', 2), ('Test Entry 3', 3)";