diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 4c145f91..c4f76967 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -2361,6 +2361,8 @@ pub struct CreateIndex { pub name: Option, #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))] pub table_name: ObjectName, + /// Index type used in the statement. Can also be found inside [`CreateIndex::index_options`] + /// depending on the position of the option within the statement. pub using: Option, pub columns: Vec, pub unique: bool, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 66089be7..d97fa1bd 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7063,19 +7063,24 @@ impl<'a> Parser<'a> { pub fn parse_create_index(&mut self, unique: bool) -> Result { let concurrently = self.parse_keyword(Keyword::CONCURRENTLY); let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); + + let mut using = None; + let index_name = if if_not_exists || !self.parse_keyword(Keyword::ON) { let index_name = self.parse_object_name(false)?; + // MySQL allows `USING index_type` either before or after `ON table_name` + using = self.parse_optional_using_then_index_type()?; self.expect_keyword_is(Keyword::ON)?; Some(index_name) } else { None }; + let table_name = self.parse_object_name(false)?; - let using = if self.parse_keyword(Keyword::USING) { - Some(self.parse_index_type()?) - } else { - None - }; + + // MySQL allows having two `USING` clauses. + // In that case, the second clause overwrites the first. + using = self.parse_optional_using_then_index_type()?.or(using); let columns = self.parse_parenthesized_index_column_list()?; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index b9434581..365d5469 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -17252,6 +17252,49 @@ fn parse_invisible_column() { } } +#[test] +fn parse_create_index_different_using_positions() { + let sql = "CREATE INDEX idx_name USING BTREE ON table_name (col1)"; + let expected = "CREATE INDEX idx_name ON table_name USING BTREE (col1)"; + match all_dialects().one_statement_parses_to(sql, expected) { + Statement::CreateIndex(CreateIndex { + name, + table_name, + using, + columns, + unique, + .. + }) => { + assert_eq!(name.unwrap().to_string(), "idx_name"); + assert_eq!(table_name.to_string(), "table_name"); + assert_eq!(using, Some(IndexType::BTree)); + assert_eq!(columns.len(), 1); + assert!(!unique); + } + _ => unreachable!(), + } + + let sql = "CREATE INDEX idx_name USING BTREE ON table_name (col1) USING HASH"; + let expected = "CREATE INDEX idx_name ON table_name USING BTREE (col1) USING HASH"; + match all_dialects().one_statement_parses_to(sql, expected) { + Statement::CreateIndex(CreateIndex { + name, + table_name, + columns, + index_options, + .. + }) => { + assert_eq!(name.unwrap().to_string(), "idx_name"); + assert_eq!(table_name.to_string(), "table_name"); + assert_eq!(columns.len(), 1); + assert!(index_options + .iter() + .any(|o| o == &IndexOption::Using(IndexType::Hash))); + } + _ => unreachable!(), + } +} + #[test] fn test_parse_alter_user() { verified_stmt("ALTER USER u1");