Parse SIGNED INTEGER type in MySQL CAST (#1739)

diff --git a/src/ast/data_type.rs b/src/ast/data_type.rs
index cae8ca8..57bc674 100644
--- a/src/ast/data_type.rs
+++ b/src/ast/data_type.rs
@@ -132,19 +132,19 @@
     /// 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
@@ -152,7 +152,7 @@
     /// 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]
@@ -197,11 +197,11 @@
     /// 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]
     ///
@@ -235,9 +235,29 @@
     /// 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
@@ -433,29 +453,29 @@
             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) => {
@@ -479,22 +499,22 @@
             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 => {
@@ -515,6 +535,18 @@
             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"),
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 5263bfc..85d3ed9 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -814,8 +814,9 @@
         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'`
diff --git a/src/keywords.rs b/src/keywords.rs
index d62a038..020b404 100644
--- a/src/keywords.rs
+++ b/src/keywords.rs
@@ -790,6 +790,7 @@
     SHARE,
     SHARING,
     SHOW,
+    SIGNED,
     SIMILAR,
     SKIP,
     SLOW,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 2d64ff3..ddcb605 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -8867,7 +8867,7 @@
                 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?))
                     }
@@ -8875,7 +8875,7 @@
                 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?))
                     }
@@ -8883,7 +8883,7 @@
                 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?))
                     }
@@ -8891,7 +8891,7 @@
                 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?))
                     }
@@ -8899,7 +8899,7 @@
                 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?))
                     }
@@ -8907,7 +8907,7 @@
                 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?))
                     }
@@ -8915,7 +8915,7 @@
                 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?))
                     }
@@ -8928,7 +8928,7 @@
                 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?))
                     }
@@ -8936,7 +8936,7 @@
                 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?))
                     }
@@ -9142,6 +9142,20 @@
                     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)?;
diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs
index 861f782..4856bd8 100644
--- a/tests/sqlparser_mysql.rs
+++ b/tests/sqlparser_mysql.rs
@@ -1359,27 +1359,27 @@
                 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![],
                     },
                 ],
@@ -3339,3 +3339,21 @@
         }
     );
 }
+
+#[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");
+}