Skip to content

Commit ea7f902

Browse files
MySQL: Add support for unsigned numeric types (#2031)
1 parent f642dd5 commit ea7f902

File tree

3 files changed

+182
-15
lines changed

3 files changed

+182
-15
lines changed

src/ast/data_type.rs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ pub enum DataType {
131131
///
132132
/// [1]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#exact-numeric-type
133133
Decimal(ExactNumberInfo),
134+
/// [MySQL] unsigned decimal with optional precision and scale, e.g. DECIMAL UNSIGNED or DECIMAL(10,2) UNSIGNED.
135+
/// Note: Using UNSIGNED with DECIMAL is deprecated in recent versions of MySQL.
136+
///
137+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/numeric-type-syntax.html
138+
DecimalUnsigned(ExactNumberInfo),
134139
/// [BigNumeric] type used in BigQuery.
135140
///
136141
/// [BigNumeric]: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#bignumeric_literals
@@ -143,8 +148,19 @@ pub enum DataType {
143148
///
144149
/// [1]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#exact-numeric-type
145150
Dec(ExactNumberInfo),
146-
/// Floating point with optional precision, e.g. FLOAT(8).
147-
Float(Option<u64>),
151+
/// [MySQL] unsigned decimal (DEC alias) with optional precision and scale, e.g. DEC UNSIGNED or DEC(10,2) UNSIGNED.
152+
/// Note: Using UNSIGNED with DEC is deprecated in recent versions of MySQL.
153+
///
154+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/numeric-type-syntax.html
155+
DecUnsigned(ExactNumberInfo),
156+
/// Floating point with optional precision and scale, e.g. FLOAT, FLOAT(8), or FLOAT(8,2).
157+
Float(ExactNumberInfo),
158+
/// [MySQL] unsigned floating point with optional precision and scale, e.g.
159+
/// FLOAT UNSIGNED, FLOAT(10) UNSIGNED or FLOAT(10,2) UNSIGNED.
160+
/// Note: Using UNSIGNED with FLOAT is deprecated in recent versions of MySQL.
161+
///
162+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/numeric-type-syntax.html
163+
FloatUnsigned(ExactNumberInfo),
148164
/// Tiny integer with optional display width, e.g. TINYINT or TINYINT(3).
149165
TinyInt(Option<u64>),
150166
/// Unsigned tiny integer with optional display width,
@@ -302,17 +318,32 @@ pub enum DataType {
302318
Float64,
303319
/// Floating point, e.g. REAL.
304320
Real,
321+
/// [MySQL] unsigned real, e.g. REAL UNSIGNED.
322+
/// Note: Using UNSIGNED with REAL is deprecated in recent versions of MySQL.
323+
///
324+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/numeric-type-syntax.html
325+
RealUnsigned,
305326
/// Float8 is an alias for Double in [PostgreSQL].
306327
///
307328
/// [PostgreSQL]: https://www.postgresql.org/docs/current/datatype.html
308329
Float8,
309330
/// Double
310331
Double(ExactNumberInfo),
332+
/// [MySQL] unsigned double precision with optional precision, e.g. DOUBLE UNSIGNED or DOUBLE(10,2) UNSIGNED.
333+
/// Note: Using UNSIGNED with DOUBLE is deprecated in recent versions of MySQL.
334+
///
335+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/numeric-type-syntax.html
336+
DoubleUnsigned(ExactNumberInfo),
311337
/// Double Precision, see [SQL Standard], [PostgreSQL].
312338
///
313339
/// [SQL Standard]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#approximate-numeric-type
314340
/// [PostgreSQL]: https://www.postgresql.org/docs/current/datatype-numeric.html
315341
DoublePrecision,
342+
/// [MySQL] unsigned double precision, e.g. DOUBLE PRECISION UNSIGNED.
343+
/// Note: Using UNSIGNED with DOUBLE PRECISION is deprecated in recent versions of MySQL.
344+
///
345+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/numeric-type-syntax.html
346+
DoublePrecisionUnsigned,
316347
/// Bool is an alias for Boolean, see [PostgreSQL].
317348
///
318349
/// [PostgreSQL]: https://www.postgresql.org/docs/current/datatype.html
@@ -497,12 +528,19 @@ impl fmt::Display for DataType {
497528
DataType::Decimal(info) => {
498529
write!(f, "DECIMAL{info}")
499530
}
531+
DataType::DecimalUnsigned(info) => {
532+
write!(f, "DECIMAL{info} UNSIGNED")
533+
}
500534
DataType::Dec(info) => {
501535
write!(f, "DEC{info}")
502536
}
537+
DataType::DecUnsigned(info) => {
538+
write!(f, "DEC{info} UNSIGNED")
539+
}
503540
DataType::BigNumeric(info) => write!(f, "BIGNUMERIC{info}"),
504541
DataType::BigDecimal(info) => write!(f, "BIGDECIMAL{info}"),
505-
DataType::Float(size) => format_type_with_optional_length(f, "FLOAT", size, false),
542+
DataType::Float(info) => write!(f, "FLOAT{info}"),
543+
DataType::FloatUnsigned(info) => write!(f, "FLOAT{info} UNSIGNED"),
506544
DataType::TinyInt(zerofill) => {
507545
format_type_with_optional_length(f, "TINYINT", zerofill, false)
508546
}
@@ -616,12 +654,15 @@ impl fmt::Display for DataType {
616654
write!(f, "UNSIGNED INTEGER")
617655
}
618656
DataType::Real => write!(f, "REAL"),
657+
DataType::RealUnsigned => write!(f, "REAL UNSIGNED"),
619658
DataType::Float4 => write!(f, "FLOAT4"),
620659
DataType::Float32 => write!(f, "Float32"),
621660
DataType::Float64 => write!(f, "FLOAT64"),
622661
DataType::Double(info) => write!(f, "DOUBLE{info}"),
662+
DataType::DoubleUnsigned(info) => write!(f, "DOUBLE{info} UNSIGNED"),
623663
DataType::Float8 => write!(f, "FLOAT8"),
624664
DataType::DoublePrecision => write!(f, "DOUBLE PRECISION"),
665+
DataType::DoublePrecisionUnsigned => write!(f, "DOUBLE PRECISION UNSIGNED"),
625666
DataType::Bool => write!(f, "BOOL"),
626667
DataType::Boolean => write!(f, "BOOLEAN"),
627668
DataType::Date => write!(f, "DATE"),

src/parser/mod.rs

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10181,19 +10181,41 @@ impl<'a> Parser<'a> {
1018110181
Token::Word(w) => match w.keyword {
1018210182
Keyword::BOOLEAN => Ok(DataType::Boolean),
1018310183
Keyword::BOOL => Ok(DataType::Bool),
10184-
Keyword::FLOAT => Ok(DataType::Float(self.parse_optional_precision()?)),
10185-
Keyword::REAL => Ok(DataType::Real),
10184+
Keyword::FLOAT => {
10185+
let precision = self.parse_exact_number_optional_precision_scale()?;
10186+
10187+
if self.parse_keyword(Keyword::UNSIGNED) {
10188+
Ok(DataType::FloatUnsigned(precision))
10189+
} else {
10190+
Ok(DataType::Float(precision))
10191+
}
10192+
}
10193+
Keyword::REAL => {
10194+
if self.parse_keyword(Keyword::UNSIGNED) {
10195+
Ok(DataType::RealUnsigned)
10196+
} else {
10197+
Ok(DataType::Real)
10198+
}
10199+
}
1018610200
Keyword::FLOAT4 => Ok(DataType::Float4),
1018710201
Keyword::FLOAT32 => Ok(DataType::Float32),
1018810202
Keyword::FLOAT64 => Ok(DataType::Float64),
1018910203
Keyword::FLOAT8 => Ok(DataType::Float8),
1019010204
Keyword::DOUBLE => {
1019110205
if self.parse_keyword(Keyword::PRECISION) {
10192-
Ok(DataType::DoublePrecision)
10206+
if self.parse_keyword(Keyword::UNSIGNED) {
10207+
Ok(DataType::DoublePrecisionUnsigned)
10208+
} else {
10209+
Ok(DataType::DoublePrecision)
10210+
}
1019310211
} else {
10194-
Ok(DataType::Double(
10195-
self.parse_exact_number_optional_precision_scale()?,
10196-
))
10212+
let precision = self.parse_exact_number_optional_precision_scale()?;
10213+
10214+
if self.parse_keyword(Keyword::UNSIGNED) {
10215+
Ok(DataType::DoubleUnsigned(precision))
10216+
} else {
10217+
Ok(DataType::Double(precision))
10218+
}
1019710219
}
1019810220
}
1019910221
Keyword::TINYINT => {
@@ -10420,12 +10442,24 @@ impl<'a> Parser<'a> {
1042010442
Keyword::NUMERIC => Ok(DataType::Numeric(
1042110443
self.parse_exact_number_optional_precision_scale()?,
1042210444
)),
10423-
Keyword::DECIMAL => Ok(DataType::Decimal(
10424-
self.parse_exact_number_optional_precision_scale()?,
10425-
)),
10426-
Keyword::DEC => Ok(DataType::Dec(
10427-
self.parse_exact_number_optional_precision_scale()?,
10428-
)),
10445+
Keyword::DECIMAL => {
10446+
let precision = self.parse_exact_number_optional_precision_scale()?;
10447+
10448+
if self.parse_keyword(Keyword::UNSIGNED) {
10449+
Ok(DataType::DecimalUnsigned(precision))
10450+
} else {
10451+
Ok(DataType::Decimal(precision))
10452+
}
10453+
}
10454+
Keyword::DEC => {
10455+
let precision = self.parse_exact_number_optional_precision_scale()?;
10456+
10457+
if self.parse_keyword(Keyword::UNSIGNED) {
10458+
Ok(DataType::DecUnsigned(precision))
10459+
} else {
10460+
Ok(DataType::Dec(precision))
10461+
}
10462+
}
1042910463
Keyword::BIGNUMERIC => Ok(DataType::BigNumeric(
1043010464
self.parse_exact_number_optional_precision_scale()?,
1043110465
)),

tests/sqlparser_mysql.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,6 +1757,98 @@ fn parse_signed_data_types() {
17571757
.expect_err("SIGNED suffix should not be allowed");
17581758
}
17591759

1760+
#[test]
1761+
fn parse_deprecated_mysql_unsigned_data_types() {
1762+
let sql = "CREATE TABLE foo (bar_decimal DECIMAL UNSIGNED, bar_decimal_prec DECIMAL(10) UNSIGNED, bar_decimal_scale DECIMAL(10,2) UNSIGNED, bar_dec DEC UNSIGNED, bar_dec_prec DEC(10) UNSIGNED, bar_dec_scale DEC(10,2) UNSIGNED, bar_float FLOAT UNSIGNED, bar_float_prec FLOAT(10) UNSIGNED, bar_float_scale FLOAT(10,2) UNSIGNED, bar_double DOUBLE UNSIGNED, bar_double_prec DOUBLE(10) UNSIGNED, bar_double_scale DOUBLE(10,2) UNSIGNED, bar_real REAL UNSIGNED, bar_double_precision DOUBLE PRECISION UNSIGNED)";
1763+
match mysql().verified_stmt(sql) {
1764+
Statement::CreateTable(CreateTable { name, columns, .. }) => {
1765+
assert_eq!(name.to_string(), "foo");
1766+
assert_eq!(
1767+
vec![
1768+
ColumnDef {
1769+
name: Ident::new("bar_decimal"),
1770+
data_type: DataType::DecimalUnsigned(ExactNumberInfo::None),
1771+
options: vec![],
1772+
},
1773+
ColumnDef {
1774+
name: Ident::new("bar_decimal_prec"),
1775+
data_type: DataType::DecimalUnsigned(ExactNumberInfo::Precision(10)),
1776+
options: vec![],
1777+
},
1778+
ColumnDef {
1779+
name: Ident::new("bar_decimal_scale"),
1780+
data_type: DataType::DecimalUnsigned(ExactNumberInfo::PrecisionAndScale(
1781+
10, 2
1782+
)),
1783+
options: vec![],
1784+
},
1785+
ColumnDef {
1786+
name: Ident::new("bar_dec"),
1787+
data_type: DataType::DecUnsigned(ExactNumberInfo::None),
1788+
options: vec![],
1789+
},
1790+
ColumnDef {
1791+
name: Ident::new("bar_dec_prec"),
1792+
data_type: DataType::DecUnsigned(ExactNumberInfo::Precision(10)),
1793+
options: vec![],
1794+
},
1795+
ColumnDef {
1796+
name: Ident::new("bar_dec_scale"),
1797+
data_type: DataType::DecUnsigned(ExactNumberInfo::PrecisionAndScale(10, 2)),
1798+
options: vec![],
1799+
},
1800+
ColumnDef {
1801+
name: Ident::new("bar_float"),
1802+
data_type: DataType::FloatUnsigned(ExactNumberInfo::None),
1803+
options: vec![],
1804+
},
1805+
ColumnDef {
1806+
name: Ident::new("bar_float_prec"),
1807+
data_type: DataType::FloatUnsigned(ExactNumberInfo::Precision(10)),
1808+
options: vec![],
1809+
},
1810+
ColumnDef {
1811+
name: Ident::new("bar_float_scale"),
1812+
data_type: DataType::FloatUnsigned(ExactNumberInfo::PrecisionAndScale(
1813+
10, 2
1814+
)),
1815+
options: vec![],
1816+
},
1817+
ColumnDef {
1818+
name: Ident::new("bar_double"),
1819+
data_type: DataType::DoubleUnsigned(ExactNumberInfo::None),
1820+
options: vec![],
1821+
},
1822+
ColumnDef {
1823+
name: Ident::new("bar_double_prec"),
1824+
data_type: DataType::DoubleUnsigned(ExactNumberInfo::Precision(10)),
1825+
options: vec![],
1826+
},
1827+
ColumnDef {
1828+
name: Ident::new("bar_double_scale"),
1829+
data_type: DataType::DoubleUnsigned(ExactNumberInfo::PrecisionAndScale(
1830+
10, 2
1831+
)),
1832+
options: vec![],
1833+
},
1834+
ColumnDef {
1835+
name: Ident::new("bar_real"),
1836+
data_type: DataType::RealUnsigned,
1837+
options: vec![],
1838+
},
1839+
ColumnDef {
1840+
name: Ident::new("bar_double_precision"),
1841+
data_type: DataType::DoublePrecisionUnsigned,
1842+
options: vec![],
1843+
},
1844+
],
1845+
columns
1846+
);
1847+
}
1848+
_ => unreachable!(),
1849+
}
1850+
}
1851+
17601852
#[test]
17611853
fn parse_simple_insert() {
17621854
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)