From bae426f95ebcd01cac03fa876f8f4a6694edd1c4 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Tue, 27 Oct 2026 11:26:11 +0100 Subject: [PATCH 1/2] MySQL: Parse USING index type before PRIMARY KEY and UNIQUE columns --- src/parser/mod.rs | 18 ++++++++++++------ tests/sqlparser_mysql.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 0b31e49907..1847face98 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -10586,7 +10586,7 @@ impl<'a> Parser<'a> { let nulls_distinct = self.parse_optional_nulls_distinct()?; // optional index name - let index_name = self.parse_optional_ident()?; + let index_name = self.parse_optional_index_name()?; let index_type = self.parse_optional_using_then_index_type()?; let columns = self.parse_parenthesized_index_column_list()?; @@ -10621,7 +10621,7 @@ impl<'a> Parser<'a> { } // optional index name - let index_name = self.parse_optional_ident()?; + let index_name = self.parse_optional_index_name()?; let index_type = self.parse_optional_using_then_index_type()?; let columns = self.parse_parenthesized_index_column_list()?; @@ -10702,10 +10702,7 @@ impl<'a> Parser<'a> { { let display_as_key = w.keyword == Keyword::KEY; - let name = match &self.peek_token_ref().token { - Token::Word(word) if word.keyword == Keyword::USING => None, - _ => self.parse_optional_ident()?, - }; + let name = self.parse_optional_index_name()?; let index_type = self.parse_optional_using_then_index_type()?; let columns = self.parse_parenthesized_index_column_list()?; @@ -10972,6 +10969,15 @@ impl<'a> Parser<'a> { self.maybe_parse(|parser| parser.parse_identifier()) } + /// Parse an optional index name, leaving a `USING` index type for the caller. + fn parse_optional_index_name(&mut self) -> Result, ParserError> { + if self.peek_keyword(Keyword::USING) { + Ok(None) + } else { + self.parse_optional_ident() + } + } + #[must_use] /// Parse optional `KEY` or `INDEX` display tokens used in index/constraint declarations. pub fn parse_index_type_display(&mut self) -> KeyOrIndexDisplay { diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index aaf5b6e90b..a7aaef6b48 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -5213,3 +5213,35 @@ fn parse_create_table_engine_name_quoting() { mysql_and_generic().verified_stmt("CREATE TABLE t (a INT) ENGINE = `InnoDB`"); mysql_and_generic().verified_stmt("CREATE TABLE t (a INT) ENGINE = InnoDB"); } + +#[test] +fn parse_key_constraint_index_type_before_columns() { + for key in ["PRIMARY KEY", "UNIQUE", "UNIQUE KEY"] { + let sql = format!("CREATE TABLE t (id INT, {key} USING BTREE (id))"); + let Statement::CreateTable(CreateTable { constraints, .. }) = + mysql_and_generic().verified_stmt(&sql) + else { + unreachable!() + }; + let (index_name, index_type) = match &constraints[..] { + [TableConstraint::PrimaryKey(c)] => (&c.index_name, &c.index_type), + [TableConstraint::Unique(c)] => (&c.index_name, &c.index_type), + _ => unreachable!(), + }; + assert_eq!(index_name, &None, "{sql}"); + assert_eq!(index_type, &Some(IndexType::BTree), "{sql}"); + + mysql_and_generic().verified_stmt(&format!( + "ALTER TABLE t ADD CONSTRAINT c {key} USING HASH (id)" + )); + + let sql = format!("CREATE TABLE t (id INT, {key} USING (id))"); + assert_eq!( + mysql_and_generic().parse_sql_statements(&sql), + Err(ParserError::ParserError( + "Expected: identifier, found: (".to_string() + )), + "{sql}" + ); + } +} From 74985334fda71c6b29dabf25c89f224b327c2772 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Tue, 27 Oct 2026 11:36:28 +0100 Subject: [PATCH 2/2] Add fuzz seed for USING before key columns --- fuzz/fuzz_seeds/mysql_2739_key_using_before_columns.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 fuzz/fuzz_seeds/mysql_2739_key_using_before_columns.sql diff --git a/fuzz/fuzz_seeds/mysql_2739_key_using_before_columns.sql b/fuzz/fuzz_seeds/mysql_2739_key_using_before_columns.sql new file mode 100644 index 0000000000..fd8bf72b05 --- /dev/null +++ b/fuzz/fuzz_seeds/mysql_2739_key_using_before_columns.sql @@ -0,0 +1 @@ +CREATE TABLE t (id INT, PRIMARY KEY USING BTREE (id), UNIQUE USING HASH (id))