Skip to content

Commit c6a3910

Browse files
MySQL: allow USING clause before ON in CREATE INDEX
MySQL allows specifying the index type `USING index_type` before the `ON` clause in `CREATE INDEX` statements. This PR allows the `CREATE INDEX` parser to accept both positions of the `USING` clause, regardless of the dialect. docs: https://dev.mysql.com/doc/refman/8.4/en/create-index.html
1 parent 54a24e7 commit c6a3910

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

src/parser/mod.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7061,19 +7061,24 @@ impl<'a> Parser<'a> {
70617061
pub fn parse_create_index(&mut self, unique: bool) -> Result<Statement, ParserError> {
70627062
let concurrently = self.parse_keyword(Keyword::CONCURRENTLY);
70637063
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
7064+
7065+
let mut using = None;
7066+
70647067
let index_name = if if_not_exists || !self.parse_keyword(Keyword::ON) {
70657068
let index_name = self.parse_object_name(false)?;
7069+
// MySQL allows `USING index_type` either before or after `ON table_name`
7070+
using = self.parse_optional_using_then_index_type()?;
70667071
self.expect_keyword_is(Keyword::ON)?;
70677072
Some(index_name)
70687073
} else {
70697074
None
70707075
};
7076+
70717077
let table_name = self.parse_object_name(false)?;
7072-
let using = if self.parse_keyword(Keyword::USING) {
7073-
Some(self.parse_index_type()?)
7074-
} else {
7075-
None
7076-
};
7078+
7079+
// MySQL allows having two `USING` clauses.
7080+
// In that case, the second clause overwrites the first.
7081+
using = self.parse_optional_using_then_index_type()?.or(using);
70777082

70787083
let columns = self.parse_parenthesized_index_column_list()?;
70797084

tests/sqlparser_common.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17246,3 +17246,47 @@ fn parse_invisible_column() {
1724617246
_ => panic!("Unexpected statement {stmt}"),
1724717247
}
1724817248
}
17249+
17250+
#[test]
17251+
fn parse_create_index_using_before_on() {
17252+
let sql = "CREATE INDEX idx_name USING BTREE ON table_name (col1)";
17253+
// Can't use `verified_stmt` here as the USING will be placed after the `ON` clause
17254+
match all_dialects().parse_sql_statements(sql).unwrap()[0].clone() {
17255+
Statement::CreateIndex(CreateIndex {
17256+
name,
17257+
table_name,
17258+
using,
17259+
columns,
17260+
unique,
17261+
..
17262+
}) => {
17263+
assert_eq!(name.unwrap().to_string(), "idx_name");
17264+
assert_eq!(table_name.to_string(), "table_name");
17265+
assert_eq!(using, Some(IndexType::BTree));
17266+
assert_eq!(columns.len(), 1);
17267+
assert!(!unique);
17268+
}
17269+
_ => unreachable!(),
17270+
}
17271+
}
17272+
17273+
#[test]
17274+
fn parse_create_index_using_multiple_clauses() {
17275+
let sql = "CREATE INDEX idx_name USING BTREE ON table_name USING HASH (col1)";
17276+
// Can't use `verified_stmt` here as the first USING will be ignored
17277+
match all_dialects().parse_sql_statements(sql).unwrap()[0].clone() {
17278+
Statement::CreateIndex(CreateIndex {
17279+
name,
17280+
table_name,
17281+
using,
17282+
columns,
17283+
..
17284+
}) => {
17285+
assert_eq!(name.unwrap().to_string(), "idx_name");
17286+
assert_eq!(table_name.to_string(), "table_name");
17287+
assert_eq!(using, Some(IndexType::Hash));
17288+
assert_eq!(columns.len(), 1);
17289+
}
17290+
_ => unreachable!(),
17291+
}
17292+
}

0 commit comments

Comments
 (0)