This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch 
gh-readonly-queue/main/pr-2029-c0998832a2f018915ad4464db6aefa5b1e2f4447
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit 7461d8bd02701de55960851c531c1b5326a737c9
Author: Mohamed Abdeen <[email protected]>
AuthorDate: Fri Sep 26 11:46:18 2025 +0100

    MySQL: `CREATE INDEX`: allow `USING` clause before `ON` (#2029)
---
 src/ast/ddl.rs            |  2 ++
 src/parser/mod.rs         | 15 ++++++++++-----
 tests/sqlparser_common.rs | 43 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 55 insertions(+), 5 deletions(-)

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<ObjectName>,
     #[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<IndexType>,
     pub columns: Vec<IndexColumn>,
     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<Statement, 
ParserError> {
         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");


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to