Skip to content

Commit

Permalink
Parse signed/unsigned integer data type in MySQL CAST
Browse files Browse the repository at this point in the history
MySQL doesn't have the same set of possible CAST types as for e.g.
column definitions. For example, it raises a syntax error for `CAST(1 AS
INTEGER SIGNED)` and instead expects `CAST(1 AS SIGNED INTEGER)`.

We retain the current somewhat permissive datatype parsing behavior
(e.g. allowing `CAST(1 AS BIGINT)` even though MySQL would raise a
syntax error), and add two datatypes for this specific case (`SIGNED
[INTEGER]` and `UNSIGNED [INTEGER]`).

Closes apache#1589
  • Loading branch information
mvzink committed Feb 24, 2025
1 parent 3ace97c commit 2877163
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 2 deletions.
26 changes: 26 additions & 0 deletions src/ast/data_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,26 @@ pub enum DataType {
UnsignedBigInt(Option<u64>),
/// Unsigned Int8 with optional display width e.g. INT8 UNSIGNED or INT8(11) UNSIGNED
UnsignedInt8(Option<u64>),
/// Signed integer as used in [MySQL CAST] target types, with optional `INTEGER` suffix:
/// `SIGNED [INTEGER]`
///
/// Note that this doesn't accept a display width and is reversed from the syntax used in column
/// definitions ([`DataType::Int`]): `INTEGER [SIGNED]`
///
/// Semantically equivalent to `BIGINT`.
///
/// [MySQL CAST]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html
Signed(bool),
/// Unsigned integer as used in [MySQL CAST] target types, with optional `INTEGER` suffix:
/// `UNSIGNED [INTEGER]`
///
/// Note that this doesn't accept a display widths and is reversed from the syntax used in
/// column definitions ([`DataType::UnsignedInteger`]): `INTEGER [UNSIGNED]`
///
/// Semantically equivalent to `BIGINT UNSIGNED`.
///
/// [MySQL CAST]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html
Unsigned(bool),
/// Float4 as alias for Real in [postgresql]
///
/// [postgresql]: https://www.postgresql.org/docs/15/datatype.html
Expand Down Expand Up @@ -515,6 +535,12 @@ impl fmt::Display for DataType {
DataType::UInt256 => {
write!(f, "UInt256")
}
DataType::Signed(integer) => {
write!(f, "SIGNED{}", if *integer { " INTEGER" } else { "" })
}
DataType::Unsigned(integer) => {
write!(f, "UNSIGNED{}", if *integer { " INTEGER" } else { "" })
}
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 @@ -798,8 +798,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
8 changes: 8 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9110,6 +9110,14 @@ impl<'a> Parser<'a> {
let columns = self.parse_returns_table_columns()?;
Ok(DataType::Table(columns))
}
Keyword::SIGNED => {
let integer = self.parse_keyword(Keyword::INTEGER);
Ok(DataType::Signed(integer))
}
Keyword::UNSIGNED => {
let integer = self.parse_keyword(Keyword::INTEGER);
Ok(DataType::Unsigned(integer))
}
_ => {
self.prev_token();
let type_name = self.parse_object_name(false)?;
Expand Down
18 changes: 18 additions & 0 deletions tests/sqlparser_mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3255,3 +3255,21 @@ fn parse_looks_like_single_line_comment() {
"UPDATE account SET balance = balance WHERE account_id = 5752",
);
}

#[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");
}

0 comments on commit 2877163

Please sign in to comment.