diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs index d460c5237..bf5d2c429 100644 --- a/src/dialect/generic.rs +++ b/src/dialect/generic.rs @@ -271,4 +271,8 @@ impl Dialect for GenericDialect { fn supports_select_format(&self) -> bool { true } + + fn supports_constraint_keyword_without_name(&self) -> bool { + true + } } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index 98ec93da4..2902d03e7 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -1068,6 +1068,23 @@ pub trait Dialect: Debug + Any { false } + /// Returns true if the dialect supports the `CONSTRAINT` keyword without a name + /// in table constraint definitions. + /// + /// Example: + /// ```sql + /// CREATE TABLE t (a INT, CONSTRAINT CHECK (a > 0)) + /// ``` + /// + /// This is a MySQL extension; the SQL standard requires a name after `CONSTRAINT`. + /// When the name is omitted, the output normalizes to just the constraint type + /// without the `CONSTRAINT` keyword (e.g., `CHECK (a > 0)`). + /// + /// + fn supports_constraint_keyword_without_name(&self) -> bool { + false + } + /// Returns true if the specified keyword is reserved and cannot be /// used as an identifier without special handling like quoting. fn is_reserved_for_identifier(&self, kw: Keyword) -> bool { diff --git a/src/dialect/mysql.rs b/src/dialect/mysql.rs index 81aa9d445..2d1c7899b 100644 --- a/src/dialect/mysql.rs +++ b/src/dialect/mysql.rs @@ -182,6 +182,11 @@ impl Dialect for MySqlDialect { fn supports_binary_kw_as_cast(&self) -> bool { true } + + /// See: + fn supports_constraint_keyword_without_name(&self) -> bool { + true + } } /// `LOCK TABLES` diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e07120174..2b1f096d5 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9252,7 +9252,16 @@ impl<'a> Parser<'a> { &mut self, ) -> Result, ParserError> { let name = if self.parse_keyword(Keyword::CONSTRAINT) { - Some(self.parse_identifier()?) + if self.dialect.supports_constraint_keyword_without_name() + && (self.peek_keyword(Keyword::CHECK) + || self.peek_keyword(Keyword::PRIMARY) + || self.peek_keyword(Keyword::UNIQUE) + || self.peek_keyword(Keyword::FOREIGN)) + { + None + } else { + Some(self.parse_identifier()?) + } } else { None }; diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 4a6205386..db75a1714 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -3457,6 +3457,13 @@ fn parse_create_table_unallow_constraint_then_index() { assert!(mysql_and_generic().parse_sql_statements(sql).is_ok()); } +#[test] +fn parse_create_table_constraint_check_without_name() { + let sql = "CREATE TABLE t (x INT, CONSTRAINT CHECK (x > 1))"; + let normalized = "CREATE TABLE t (x INT, CHECK (x > 1))"; + mysql_and_generic().one_statement_parses_to(sql, normalized); +} + #[test] fn parse_create_table_with_fulltext_definition() { mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, FULLTEXT (id))");