Skip to content

Parse SIGNED INTEGER type in MySQL CAST #1739

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 51 additions & 19 deletions src/ast/data_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,27 +132,27 @@ pub enum DataType {
/// Tiny integer with optional display width e.g. TINYINT or TINYINT(3)
TinyInt(Option<u64>),
/// Unsigned tiny integer with optional display width e.g. TINYINT UNSIGNED or TINYINT(3) UNSIGNED
UnsignedTinyInt(Option<u64>),
TinyIntUnsigned(Option<u64>),
/// Int2 as alias for SmallInt in [postgresql]
/// Note: Int2 mean 2 bytes in postgres (not 2 bits)
/// Int2 with optional display width e.g. INT2 or INT2(5)
///
/// [postgresql]: https://www.postgresql.org/docs/15/datatype.html
Int2(Option<u64>),
/// Unsigned Int2 with optional display width e.g. INT2 Unsigned or INT2(5) Unsigned
UnsignedInt2(Option<u64>),
/// Unsigned Int2 with optional display width e.g. INT2 UNSIGNED or INT2(5) UNSIGNED
Int2Unsigned(Option<u64>),
/// Small integer with optional display width e.g. SMALLINT or SMALLINT(5)
SmallInt(Option<u64>),
/// Unsigned small integer with optional display width e.g. SMALLINT UNSIGNED or SMALLINT(5) UNSIGNED
UnsignedSmallInt(Option<u64>),
SmallIntUnsigned(Option<u64>),
/// MySQL medium integer ([1]) with optional display width e.g. MEDIUMINT or MEDIUMINT(5)
///
/// [1]: https://dev.mysql.com/doc/refman/8.0/en/integer-types.html
MediumInt(Option<u64>),
/// Unsigned medium integer ([1]) with optional display width e.g. MEDIUMINT UNSIGNED or MEDIUMINT(5) UNSIGNED
///
/// [1]: https://dev.mysql.com/doc/refman/8.0/en/integer-types.html
UnsignedMediumInt(Option<u64>),
MediumIntUnsigned(Option<u64>),
/// Int with optional display width e.g. INT or INT(11)
Int(Option<u64>),
/// Int4 as alias for Integer in [postgresql]
Expand Down Expand Up @@ -197,11 +197,11 @@ pub enum DataType {
/// Integer with optional display width e.g. INTEGER or INTEGER(11)
Integer(Option<u64>),
/// Unsigned int with optional display width e.g. INT UNSIGNED or INT(11) UNSIGNED
UnsignedInt(Option<u64>),
IntUnsigned(Option<u64>),
/// Unsigned int4 with optional display width e.g. INT4 UNSIGNED or INT4(11) UNSIGNED
UnsignedInt4(Option<u64>),
Int4Unsigned(Option<u64>),
/// Unsigned integer with optional display width e.g. INTEGER UNSIGNED or INTEGER(11) UNSIGNED
UnsignedInteger(Option<u64>),
IntegerUnsigned(Option<u64>),
/// Unsigned integer type in [clickhouse]
/// Note: UInt8 mean 8 bits in [clickhouse]
///
Expand Down Expand Up @@ -235,9 +235,29 @@ pub enum DataType {
/// Big integer with optional display width e.g. BIGINT or BIGINT(20)
BigInt(Option<u64>),
/// Unsigned big integer with optional display width e.g. BIGINT UNSIGNED or BIGINT(20) UNSIGNED
UnsignedBigInt(Option<u64>),
BigIntUnsigned(Option<u64>),
/// Unsigned Int8 with optional display width e.g. INT8 UNSIGNED or INT8(11) UNSIGNED
UnsignedInt8(Option<u64>),
Int8Unsigned(Option<u64>),
/// Signed integer as used in [MySQL CAST] target types, without optional `INTEGER` suffix:
/// `SIGNED`
///
/// [MySQL CAST]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html
Signed,
/// Signed integer as used in [MySQL CAST] target types, with optional `INTEGER` suffix:
/// `SIGNED INTEGER`
///
/// [MySQL CAST]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html
SignedInteger,
/// Signed integer as used in [MySQL CAST] target types, without optional `INTEGER` suffix:
/// `SIGNED`
///
/// [MySQL CAST]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html
Unsigned,
/// Unsigned integer as used in [MySQL CAST] target types, with optional `INTEGER` suffix:
/// `UNSIGNED INTEGER`
///
/// [MySQL CAST]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html
UnsignedInteger,
/// Float4 as alias for Real in [postgresql]
///
/// [postgresql]: https://www.postgresql.org/docs/15/datatype.html
Expand Down Expand Up @@ -433,29 +453,29 @@ impl fmt::Display for DataType {
DataType::TinyInt(zerofill) => {
format_type_with_optional_length(f, "TINYINT", zerofill, false)
}
DataType::UnsignedTinyInt(zerofill) => {
DataType::TinyIntUnsigned(zerofill) => {
format_type_with_optional_length(f, "TINYINT", zerofill, true)
}
DataType::Int2(zerofill) => {
format_type_with_optional_length(f, "INT2", zerofill, false)
}
DataType::UnsignedInt2(zerofill) => {
DataType::Int2Unsigned(zerofill) => {
format_type_with_optional_length(f, "INT2", zerofill, true)
}
DataType::SmallInt(zerofill) => {
format_type_with_optional_length(f, "SMALLINT", zerofill, false)
}
DataType::UnsignedSmallInt(zerofill) => {
DataType::SmallIntUnsigned(zerofill) => {
format_type_with_optional_length(f, "SMALLINT", zerofill, true)
}
DataType::MediumInt(zerofill) => {
format_type_with_optional_length(f, "MEDIUMINT", zerofill, false)
}
DataType::UnsignedMediumInt(zerofill) => {
DataType::MediumIntUnsigned(zerofill) => {
format_type_with_optional_length(f, "MEDIUMINT", zerofill, true)
}
DataType::Int(zerofill) => format_type_with_optional_length(f, "INT", zerofill, false),
DataType::UnsignedInt(zerofill) => {
DataType::IntUnsigned(zerofill) => {
format_type_with_optional_length(f, "INT", zerofill, true)
}
DataType::Int4(zerofill) => {
Expand All @@ -479,22 +499,22 @@ impl fmt::Display for DataType {
DataType::Int256 => {
write!(f, "Int256")
}
DataType::UnsignedInt4(zerofill) => {
DataType::Int4Unsigned(zerofill) => {
format_type_with_optional_length(f, "INT4", zerofill, true)
}
DataType::Integer(zerofill) => {
format_type_with_optional_length(f, "INTEGER", zerofill, false)
}
DataType::UnsignedInteger(zerofill) => {
DataType::IntegerUnsigned(zerofill) => {
format_type_with_optional_length(f, "INTEGER", zerofill, true)
}
DataType::BigInt(zerofill) => {
format_type_with_optional_length(f, "BIGINT", zerofill, false)
}
DataType::UnsignedBigInt(zerofill) => {
DataType::BigIntUnsigned(zerofill) => {
format_type_with_optional_length(f, "BIGINT", zerofill, true)
}
DataType::UnsignedInt8(zerofill) => {
DataType::Int8Unsigned(zerofill) => {
format_type_with_optional_length(f, "INT8", zerofill, true)
}
DataType::UInt8 => {
Expand All @@ -515,6 +535,18 @@ impl fmt::Display for DataType {
DataType::UInt256 => {
write!(f, "UInt256")
}
DataType::Signed => {
write!(f, "SIGNED")
}
DataType::SignedInteger => {
write!(f, "SIGNED INTEGER")
}
DataType::Unsigned => {
write!(f, "UNSIGNED")
}
DataType::UnsignedInteger => {
write!(f, "UNSIGNED INTEGER")
}
DataType::Real => write!(f, "REAL"),
DataType::Float4 => write!(f, "FLOAT4"),
DataType::Float32 => write!(f, "Float32"),
Expand Down
5 changes: 3 additions & 2 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -814,8 +814,9 @@ pub enum Expr {
kind: CastKind,
expr: Box<Expr>,
data_type: DataType,
// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by BigQuery
// https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax
/// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by [BigQuery]
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax
format: Option<CastFormat>,
},
/// AT a timestamp to a different timezone e.g. `FROM_UNIXTIME(0) AT TIME ZONE 'UTC-06:00'`
Expand Down
1 change: 1 addition & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,7 @@ define_keywords!(
SHARE,
SHARING,
SHOW,
SIGNED,
SIMILAR,
SKIP,
SLOW,
Expand Down
32 changes: 23 additions & 9 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8867,55 +8867,55 @@ impl<'a> Parser<'a> {
Keyword::TINYINT => {
let optional_precision = self.parse_optional_precision();
if self.parse_keyword(Keyword::UNSIGNED) {
Ok(DataType::UnsignedTinyInt(optional_precision?))
Ok(DataType::TinyIntUnsigned(optional_precision?))
} else {
Ok(DataType::TinyInt(optional_precision?))
}
}
Keyword::INT2 => {
let optional_precision = self.parse_optional_precision();
if self.parse_keyword(Keyword::UNSIGNED) {
Ok(DataType::UnsignedInt2(optional_precision?))
Ok(DataType::Int2Unsigned(optional_precision?))
} else {
Ok(DataType::Int2(optional_precision?))
}
}
Keyword::SMALLINT => {
let optional_precision = self.parse_optional_precision();
if self.parse_keyword(Keyword::UNSIGNED) {
Ok(DataType::UnsignedSmallInt(optional_precision?))
Ok(DataType::SmallIntUnsigned(optional_precision?))
} else {
Ok(DataType::SmallInt(optional_precision?))
}
}
Keyword::MEDIUMINT => {
let optional_precision = self.parse_optional_precision();
if self.parse_keyword(Keyword::UNSIGNED) {
Ok(DataType::UnsignedMediumInt(optional_precision?))
Ok(DataType::MediumIntUnsigned(optional_precision?))
} else {
Ok(DataType::MediumInt(optional_precision?))
}
}
Keyword::INT => {
let optional_precision = self.parse_optional_precision();
if self.parse_keyword(Keyword::UNSIGNED) {
Ok(DataType::UnsignedInt(optional_precision?))
Ok(DataType::IntUnsigned(optional_precision?))
} else {
Ok(DataType::Int(optional_precision?))
}
}
Keyword::INT4 => {
let optional_precision = self.parse_optional_precision();
if self.parse_keyword(Keyword::UNSIGNED) {
Ok(DataType::UnsignedInt4(optional_precision?))
Ok(DataType::Int4Unsigned(optional_precision?))
} else {
Ok(DataType::Int4(optional_precision?))
}
}
Keyword::INT8 => {
let optional_precision = self.parse_optional_precision();
if self.parse_keyword(Keyword::UNSIGNED) {
Ok(DataType::UnsignedInt8(optional_precision?))
Ok(DataType::Int8Unsigned(optional_precision?))
} else {
Ok(DataType::Int8(optional_precision?))
}
Expand All @@ -8928,15 +8928,15 @@ impl<'a> Parser<'a> {
Keyword::INTEGER => {
let optional_precision = self.parse_optional_precision();
if self.parse_keyword(Keyword::UNSIGNED) {
Ok(DataType::UnsignedInteger(optional_precision?))
Ok(DataType::IntegerUnsigned(optional_precision?))
} else {
Ok(DataType::Integer(optional_precision?))
}
}
Keyword::BIGINT => {
let optional_precision = self.parse_optional_precision();
if self.parse_keyword(Keyword::UNSIGNED) {
Ok(DataType::UnsignedBigInt(optional_precision?))
Ok(DataType::BigIntUnsigned(optional_precision?))
} else {
Ok(DataType::BigInt(optional_precision?))
}
Expand Down Expand Up @@ -9142,6 +9142,20 @@ impl<'a> Parser<'a> {
let columns = self.parse_returns_table_columns()?;
Ok(DataType::Table(columns))
}
Keyword::SIGNED => {
if self.parse_keyword(Keyword::INTEGER) {
Ok(DataType::SignedInteger)
} else {
Ok(DataType::Signed)
}
}
Keyword::UNSIGNED => {
if self.parse_keyword(Keyword::INTEGER) {
Ok(DataType::UnsignedInteger)
} else {
Ok(DataType::Unsigned)
}
}
_ => {
self.prev_token();
let type_name = self.parse_object_name(false)?;
Expand Down
28 changes: 23 additions & 5 deletions tests/sqlparser_mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1359,27 +1359,27 @@ fn parse_create_table_unsigned() {
vec![
ColumnDef {
name: Ident::new("bar_tinyint"),
data_type: DataType::UnsignedTinyInt(Some(3)),
data_type: DataType::TinyIntUnsigned(Some(3)),
options: vec![],
},
ColumnDef {
name: Ident::new("bar_smallint"),
data_type: DataType::UnsignedSmallInt(Some(5)),
data_type: DataType::SmallIntUnsigned(Some(5)),
options: vec![],
},
ColumnDef {
name: Ident::new("bar_mediumint"),
data_type: DataType::UnsignedMediumInt(Some(13)),
data_type: DataType::MediumIntUnsigned(Some(13)),
options: vec![],
},
ColumnDef {
name: Ident::new("bar_int"),
data_type: DataType::UnsignedInt(Some(11)),
data_type: DataType::IntUnsigned(Some(11)),
options: vec![],
},
ColumnDef {
name: Ident::new("bar_bigint"),
data_type: DataType::UnsignedBigInt(Some(20)),
data_type: DataType::BigIntUnsigned(Some(20)),
options: vec![],
},
],
Expand Down Expand Up @@ -3339,3 +3339,21 @@ fn parse_drop_trigger() {
}
);
}

#[test]
fn parse_cast_integers() {
mysql().verified_expr("CAST(foo AS UNSIGNED)");
mysql().verified_expr("CAST(foo AS SIGNED)");
mysql().verified_expr("CAST(foo AS UNSIGNED INTEGER)");
mysql().verified_expr("CAST(foo AS SIGNED INTEGER)");

mysql()
.run_parser_method("CAST(foo AS UNSIGNED(3))", |p| p.parse_expr())
.expect_err("CAST doesn't allow display width");
mysql()
.run_parser_method("CAST(foo AS UNSIGNED(3) INTEGER)", |p| p.parse_expr())
.expect_err("CAST doesn't allow display width");
mysql()
.run_parser_method("CAST(foo AS UNSIGNED INTEGER(3))", |p| p.parse_expr())
.expect_err("CAST doesn't allow display width");
}