Skip to content

Commit deed2ce

Browse files
committed
MySQL: Allow optional SIGNED suffix on integer data types
In MySQL, a data type like `INT(20) SIGNED` is equivalent to `INT(20)`. In other dialects, this may be interpreted differently; e.g. in Postgres, `SELECT 1::integer signed` indicates an alias of "signed". So we parse the optional `SIGNED` suffix only on dialects that allow it (I currently don't know of any other than MySQL).
1 parent 9127370 commit deed2ce

File tree

5 files changed

+70
-0
lines changed

5 files changed

+70
-0
lines changed

src/dialect/generic.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ impl Dialect for GenericDialect {
100100
true
101101
}
102102

103+
fn allow_optional_signed_suffix(&self) -> bool {
104+
true
105+
}
106+
103107
fn supports_create_index_with_clause(&self) -> bool {
104108
true
105109
}

src/dialect/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,11 @@ pub trait Dialect: Debug + Any {
835835
false
836836
}
837837

838+
/// Returns true if this dialect allows an optional `SIGNED` suffix after integer data types.
839+
fn allow_optional_signed_suffix(&self) -> bool {
840+
false
841+
}
842+
838843
/// Returns true if this dialect allows dollar placeholders
839844
/// e.g. `SELECT $var` (SQLite)
840845
fn supports_dollar_placeholder(&self) -> bool {

src/dialect/mysql.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ impl Dialect for MySqlDialect {
6666
Some('`')
6767
}
6868

69+
fn allow_optional_signed_suffix(&self) -> bool {
70+
true
71+
}
72+
6973
// See https://dev.mysql.com/doc/refman/8.0/en/string-literals.html#character-escape-sequences
7074
fn supports_string_literal_backslash_escape(&self) -> bool {
7175
true

src/parser/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9852,6 +9852,8 @@ impl<'a> Parser<'a> {
98529852
if self.parse_keyword(Keyword::UNSIGNED) {
98539853
Ok(DataType::TinyIntUnsigned(optional_precision?))
98549854
} else {
9855+
let _ = dialect.allow_optional_signed_suffix()
9856+
&& self.parse_keyword(Keyword::SIGNED);
98559857
Ok(DataType::TinyInt(optional_precision?))
98569858
}
98579859
}
@@ -9868,6 +9870,8 @@ impl<'a> Parser<'a> {
98689870
if self.parse_keyword(Keyword::UNSIGNED) {
98699871
Ok(DataType::SmallIntUnsigned(optional_precision?))
98709872
} else {
9873+
let _ = dialect.allow_optional_signed_suffix()
9874+
&& self.parse_keyword(Keyword::SIGNED);
98719875
Ok(DataType::SmallInt(optional_precision?))
98729876
}
98739877
}
@@ -9876,6 +9880,8 @@ impl<'a> Parser<'a> {
98769880
if self.parse_keyword(Keyword::UNSIGNED) {
98779881
Ok(DataType::MediumIntUnsigned(optional_precision?))
98789882
} else {
9883+
let _ = dialect.allow_optional_signed_suffix()
9884+
&& self.parse_keyword(Keyword::SIGNED);
98799885
Ok(DataType::MediumInt(optional_precision?))
98809886
}
98819887
}
@@ -9884,6 +9890,8 @@ impl<'a> Parser<'a> {
98849890
if self.parse_keyword(Keyword::UNSIGNED) {
98859891
Ok(DataType::IntUnsigned(optional_precision?))
98869892
} else {
9893+
let _ = dialect.allow_optional_signed_suffix()
9894+
&& self.parse_keyword(Keyword::SIGNED);
98879895
Ok(DataType::Int(optional_precision?))
98889896
}
98899897
}
@@ -9913,6 +9921,8 @@ impl<'a> Parser<'a> {
99139921
if self.parse_keyword(Keyword::UNSIGNED) {
99149922
Ok(DataType::IntegerUnsigned(optional_precision?))
99159923
} else {
9924+
let _ = dialect.allow_optional_signed_suffix()
9925+
&& self.parse_keyword(Keyword::SIGNED);
99169926
Ok(DataType::Integer(optional_precision?))
99179927
}
99189928
}
@@ -9921,6 +9931,8 @@ impl<'a> Parser<'a> {
99219931
if self.parse_keyword(Keyword::UNSIGNED) {
99229932
Ok(DataType::BigIntUnsigned(optional_precision?))
99239933
} else {
9934+
let _ = dialect.allow_optional_signed_suffix()
9935+
&& self.parse_keyword(Keyword::SIGNED);
99249936
Ok(DataType::BigInt(optional_precision?))
99259937
}
99269938
}

tests/sqlparser_mysql.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,51 @@ fn parse_create_table_unsigned() {
17051705
}
17061706
}
17071707

1708+
#[test]
1709+
fn parse_create_table_signed() {
1710+
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)";
1711+
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))";
1712+
match mysql().one_statement_parses_to(sql, canonical) {
1713+
Statement::CreateTable(CreateTable { name, columns, .. }) => {
1714+
assert_eq!(name.to_string(), "foo");
1715+
assert_eq!(
1716+
vec![
1717+
ColumnDef {
1718+
name: Ident::new("bar_tinyint"),
1719+
data_type: DataType::TinyInt(Some(3)),
1720+
options: vec![],
1721+
},
1722+
ColumnDef {
1723+
name: Ident::new("bar_smallint"),
1724+
data_type: DataType::SmallInt(Some(5)),
1725+
options: vec![],
1726+
},
1727+
ColumnDef {
1728+
name: Ident::new("bar_mediumint"),
1729+
data_type: DataType::MediumInt(Some(13)),
1730+
options: vec![],
1731+
},
1732+
ColumnDef {
1733+
name: Ident::new("bar_int"),
1734+
data_type: DataType::Int(Some(11)),
1735+
options: vec![],
1736+
},
1737+
ColumnDef {
1738+
name: Ident::new("bar_bigint"),
1739+
data_type: DataType::BigInt(Some(20)),
1740+
options: vec![],
1741+
},
1742+
],
1743+
columns
1744+
);
1745+
}
1746+
_ => unreachable!(),
1747+
}
1748+
all_dialects_except(|d| d.allow_optional_signed_suffix())
1749+
.run_parser_method(sql, |p| p.parse_statement())
1750+
.expect_err("SIGNED suffix should not be allowed");
1751+
}
1752+
17081753
#[test]
17091754
fn parse_simple_insert() {
17101755
let sql = r"INSERT INTO tasks (title, priority) VALUES ('Test Some Inserts', 1), ('Test Entry 2', 2), ('Test Entry 3', 3)";

0 commit comments

Comments
 (0)