Skip to content

Commit 1f9dbf5

Browse files
committed
Support MySQL index options such as USING in CREATE INDEX
For MySQL, the preferred position to put `USING BTREE` is after the column list rather than before; before is still supported but considered [deprecated]: > The preferred position is after the column list. Expect support for use of the option before the column list to be removed in a future MySQL release. [deprecated]: https://dev.mysql.com/doc/refman/8.4/en/alter-table.html We already parse this correctly for `ALTER TABLE` statements to add keys, and in table-level constraint parsing for `CREATE TABLE`. We can just reuse the same parsing to support the newer style for `CREATE INDEX` statements as well.
1 parent a841272 commit 1f9dbf5

File tree

6 files changed

+57
-0
lines changed

6 files changed

+57
-0
lines changed

src/ast/ddl.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2335,6 +2335,7 @@ pub struct CreateIndex {
23352335
/// WITH clause: <https://www.postgresql.org/docs/current/sql-createindex.html>
23362336
pub with: Vec<Expr>,
23372337
pub predicate: Option<Expr>,
2338+
pub index_options: Vec<IndexOption>,
23382339
}
23392340

23402341
impl fmt::Display for CreateIndex {
@@ -2378,6 +2379,9 @@ impl fmt::Display for CreateIndex {
23782379
if let Some(predicate) = &self.predicate {
23792380
write!(f, " WHERE {predicate}")?;
23802381
}
2382+
if !self.index_options.is_empty() {
2383+
write!(f, " {}", display_separated(&self.index_options, " "))?;
2384+
}
23812385
Ok(())
23822386
}
23832387
}

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,7 @@ impl Spanned for CreateIndex {
747747
nulls_distinct: _, // bool
748748
with,
749749
predicate,
750+
index_options: _,
750751
} = self;
751752

752753
union_spans(

src/parser/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7077,6 +7077,13 @@ impl<'a> Parser<'a> {
70777077
None
70787078
};
70797079

7080+
// MySQL options (including the modern style of `USING` after the column list instead of
7081+
// before, which is deprecated) shouldn't conflict with other preceding options (e.g. `WITH
7082+
// PARSER` won't be caught by the above `WITH` clause parsing because MySQL doesn't set that
7083+
// support flag). This is probably invalid syntax for other dialects, but it is simpler to
7084+
// parse it anyway (as we do inside `ALTER TABLE` and `CREATE TABLE` parsing).
7085+
let index_options = self.parse_index_options()?;
7086+
70807087
Ok(Statement::CreateIndex(CreateIndex {
70817088
name: index_name,
70827089
table_name,
@@ -7089,6 +7096,7 @@ impl<'a> Parser<'a> {
70897096
nulls_distinct,
70907097
with,
70917098
predicate,
7099+
index_options,
70927100
}))
70937101
}
70947102

tests/sqlparser_common.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9273,6 +9273,7 @@ fn test_create_index_with_using_function() {
92739273
nulls_distinct: None,
92749274
with,
92759275
predicate: None,
9276+
index_options,
92769277
}) => {
92779278
assert_eq!("idx_name", name.to_string());
92789279
assert_eq!("test", table_name.to_string());
@@ -9283,6 +9284,7 @@ fn test_create_index_with_using_function() {
92839284
assert!(if_not_exists);
92849285
assert!(include.is_empty());
92859286
assert!(with.is_empty());
9287+
assert!(index_options.is_empty());
92869288
}
92879289
_ => unreachable!(),
92889290
}
@@ -9324,6 +9326,7 @@ fn test_create_index_with_with_clause() {
93249326
nulls_distinct: None,
93259327
with,
93269328
predicate: None,
9329+
index_options,
93279330
}) => {
93289331
pretty_assertions::assert_eq!("title_idx", name.to_string());
93299332
pretty_assertions::assert_eq!("films", table_name.to_string());
@@ -9333,6 +9336,7 @@ fn test_create_index_with_with_clause() {
93339336
assert!(!if_not_exists);
93349337
assert!(include.is_empty());
93359338
pretty_assertions::assert_eq!(with_parameters, with);
9339+
assert!(index_options.is_empty());
93369340
}
93379341
_ => unreachable!(),
93389342
}

tests/sqlparser_mysql.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4210,3 +4210,21 @@ fn parse_show_charset() {
42104210
mysql().verified_stmt("SHOW CHARSET WHERE charset = 'utf8mb4%'");
42114211
mysql().verified_stmt("SHOW CHARSET LIKE 'utf8mb4%'");
42124212
}
4213+
4214+
#[test]
4215+
fn test_ddl_with_index_using() {
4216+
let columns = "(name, age DESC)";
4217+
let using = "USING BTREE";
4218+
4219+
for sql in [
4220+
format!("CREATE INDEX idx_name ON test {using} {columns}"),
4221+
format!("CREATE TABLE foo (name VARCHAR(255), age INT, KEY idx_name {using} {columns})"),
4222+
format!("ALTER TABLE foo ADD KEY idx_name {using} {columns}"),
4223+
format!("CREATE INDEX idx_name ON test{columns} {using}"),
4224+
// TODO: Add index options to `TableConstraint::Index`
4225+
// format!("CREATE TABLE foo (name VARCHAR(255), age INT, KEY idx_name{columns} {using})"),
4226+
// format!("ALTER TABLE foo ADD KEY idx_name{columns} {using}"),
4227+
] {
4228+
mysql_and_generic().verified_stmt(&sql);
4229+
}
4230+
}

tests/sqlparser_postgres.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2500,6 +2500,7 @@ fn parse_create_index() {
25002500
include,
25012501
with,
25022502
predicate: None,
2503+
index_options,
25032504
}) => {
25042505
assert_eq_vec(&["my_index"], &name);
25052506
assert_eq_vec(&["my_table"], &table_name);
@@ -2510,6 +2511,7 @@ fn parse_create_index() {
25102511
assert_eq_vec(&["col1", "col2"], &columns);
25112512
assert!(include.is_empty());
25122513
assert!(with.is_empty());
2514+
assert!(index_options.is_empty());
25132515
}
25142516
_ => unreachable!(),
25152517
}
@@ -2531,6 +2533,7 @@ fn parse_create_anonymous_index() {
25312533
nulls_distinct: None,
25322534
with,
25332535
predicate: None,
2536+
index_options,
25342537
}) => {
25352538
assert_eq!(None, name);
25362539
assert_eq_vec(&["my_table"], &table_name);
@@ -2541,6 +2544,7 @@ fn parse_create_anonymous_index() {
25412544
assert_eq_vec(&["col1", "col2"], &columns);
25422545
assert!(include.is_empty());
25432546
assert!(with.is_empty());
2547+
assert!(index_options.is_empty());
25442548
}
25452549
_ => unreachable!(),
25462550
}
@@ -2639,13 +2643,15 @@ fn parse_create_indices_with_operator_classes() {
26392643
nulls_distinct: None,
26402644
with,
26412645
predicate: None,
2646+
index_options,
26422647
}) => {
26432648
assert_eq_vec(&["the_index_name"], &name);
26442649
assert_eq_vec(&["users"], &table_name);
26452650
assert_eq!(expected_index_type, using);
26462651
assert_eq!(expected_function_column, columns[0],);
26472652
assert!(include.is_empty());
26482653
assert!(with.is_empty());
2654+
assert!(index_options.is_empty());
26492655
}
26502656
_ => unreachable!(),
26512657
}
@@ -2663,6 +2669,7 @@ fn parse_create_indices_with_operator_classes() {
26632669
nulls_distinct: None,
26642670
with,
26652671
predicate: None,
2672+
index_options,
26662673
}) => {
26672674
assert_eq_vec(&["the_index_name"], &name);
26682675
assert_eq_vec(&["users"], &table_name);
@@ -2688,6 +2695,7 @@ fn parse_create_indices_with_operator_classes() {
26882695
assert_eq!(expected_function_column, columns[1],);
26892696
assert!(include.is_empty());
26902697
assert!(with.is_empty());
2698+
assert!(index_options.is_empty());
26912699
}
26922700
_ => unreachable!(),
26932701
}
@@ -2712,6 +2720,7 @@ fn parse_create_bloom() {
27122720
nulls_distinct: None,
27132721
with,
27142722
predicate: None,
2723+
index_options,
27152724
}) => {
27162725
assert_eq_vec(&["bloomidx"], &name);
27172726
assert_eq_vec(&["tbloom"], &table_name);
@@ -2743,6 +2752,7 @@ fn parse_create_bloom() {
27432752
],
27442753
with
27452754
);
2755+
assert!(index_options.is_empty());
27462756
}
27472757
_ => unreachable!(),
27482758
}
@@ -2764,13 +2774,15 @@ fn parse_create_brin() {
27642774
nulls_distinct: None,
27652775
with,
27662776
predicate: None,
2777+
index_options,
27672778
}) => {
27682779
assert_eq_vec(&["brin_sensor_data_recorded_at"], &name);
27692780
assert_eq_vec(&["sensor_data"], &table_name);
27702781
assert_eq!(IndexType::BRIN, using);
27712782
assert_eq_vec(&["recorded_at"], &columns);
27722783
assert!(include.is_empty());
27732784
assert!(with.is_empty());
2785+
assert!(index_options.is_empty());
27742786
}
27752787
_ => unreachable!(),
27762788
}
@@ -2827,6 +2839,7 @@ fn parse_create_index_concurrently() {
28272839
nulls_distinct: None,
28282840
with,
28292841
predicate: None,
2842+
index_options,
28302843
}) => {
28312844
assert_eq_vec(&["my_index"], &name);
28322845
assert_eq_vec(&["my_table"], &table_name);
@@ -2837,6 +2850,7 @@ fn parse_create_index_concurrently() {
28372850
assert_eq_vec(&["col1", "col2"], &columns);
28382851
assert!(include.is_empty());
28392852
assert!(with.is_empty());
2853+
assert!(index_options.is_empty());
28402854
}
28412855
_ => unreachable!(),
28422856
}
@@ -2858,6 +2872,7 @@ fn parse_create_index_with_predicate() {
28582872
nulls_distinct: None,
28592873
with,
28602874
predicate: Some(_),
2875+
index_options,
28612876
}) => {
28622877
assert_eq_vec(&["my_index"], &name);
28632878
assert_eq_vec(&["my_table"], &table_name);
@@ -2868,6 +2883,7 @@ fn parse_create_index_with_predicate() {
28682883
assert_eq_vec(&["col1", "col2"], &columns);
28692884
assert!(include.is_empty());
28702885
assert!(with.is_empty());
2886+
assert!(index_options.is_empty());
28712887
}
28722888
_ => unreachable!(),
28732889
}
@@ -2889,6 +2905,7 @@ fn parse_create_index_with_include() {
28892905
nulls_distinct: None,
28902906
with,
28912907
predicate: None,
2908+
index_options,
28922909
}) => {
28932910
assert_eq_vec(&["my_index"], &name);
28942911
assert_eq_vec(&["my_table"], &table_name);
@@ -2899,6 +2916,7 @@ fn parse_create_index_with_include() {
28992916
assert_eq_vec(&["col1", "col2"], &columns);
29002917
assert_eq_vec(&["col3", "col4"], &include);
29012918
assert!(with.is_empty());
2919+
assert!(index_options.is_empty());
29022920
}
29032921
_ => unreachable!(),
29042922
}
@@ -2920,6 +2938,7 @@ fn parse_create_index_with_nulls_distinct() {
29202938
nulls_distinct: Some(nulls_distinct),
29212939
with,
29222940
predicate: None,
2941+
index_options,
29232942
}) => {
29242943
assert_eq_vec(&["my_index"], &name);
29252944
assert_eq_vec(&["my_table"], &table_name);
@@ -2931,6 +2950,7 @@ fn parse_create_index_with_nulls_distinct() {
29312950
assert!(include.is_empty());
29322951
assert!(!nulls_distinct);
29332952
assert!(with.is_empty());
2953+
assert!(index_options.is_empty());
29342954
}
29352955
_ => unreachable!(),
29362956
}
@@ -2949,6 +2969,7 @@ fn parse_create_index_with_nulls_distinct() {
29492969
nulls_distinct: Some(nulls_distinct),
29502970
with,
29512971
predicate: None,
2972+
index_options,
29522973
}) => {
29532974
assert_eq_vec(&["my_index"], &name);
29542975
assert_eq_vec(&["my_table"], &table_name);
@@ -2960,6 +2981,7 @@ fn parse_create_index_with_nulls_distinct() {
29602981
assert!(include.is_empty());
29612982
assert!(nulls_distinct);
29622983
assert!(with.is_empty());
2984+
assert!(index_options.is_empty());
29632985
}
29642986
_ => unreachable!(),
29652987
}

0 commit comments

Comments
 (0)