Skip to content

Commit 5c3768f

Browse files
mvzinkayman-sigma
authored andcommitted
Improve MySQL option parsing in index definitions (apache#1997)
1 parent 8d7c62a commit 5c3768f

File tree

9 files changed

+628
-498
lines changed

9 files changed

+628
-498
lines changed

src/ast/ddl.rs

Lines changed: 492 additions & 10 deletions
Large diffs are not rendered by default.

src/ast/dml.rs

Lines changed: 4 additions & 466 deletions
Large diffs are not rendered by default.

src/ast/helpers/stmt_create_table.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,8 @@ use serde::{Deserialize, Serialize};
2424
#[cfg(feature = "visitor")]
2525
use sqlparser_derive::{Visit, VisitMut};
2626

27-
use super::super::dml::CreateTable;
2827
use crate::ast::{
29-
ClusteredBy, ColumnDef, CommentDef, CreateTableOptions, Expr, FileFormat,
28+
ClusteredBy, ColumnDef, CommentDef, CreateTable, CreateTableOptions, Expr, FileFormat,
3029
HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit, OneOrManyWithParens, Query,
3130
RowAccessPolicy, Statement, StorageSerializationPolicy, TableConstraint, Tag,
3231
WrappedCollection,

src/ast/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@ pub use self::ddl::{
6363
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
6464
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
6565
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain, CreateFunction,
66-
Deduplicate, DeferrableInitial, DropBehavior, GeneratedAs, GeneratedExpressionMode,
67-
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
68-
IdentityPropertyOrder, IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner,
69-
Partition, ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity,
70-
TableConstraint, TagsColumnOption, UserDefinedTypeCompositeAttributeDef,
71-
UserDefinedTypeRepresentation, ViewColumnDef,
66+
CreateIndex, CreateTable, Deduplicate, DeferrableInitial, DropBehavior, GeneratedAs,
67+
GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
68+
IdentityPropertyKind, IdentityPropertyOrder, IndexColumn, IndexOption, IndexType,
69+
KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition, ProcedureParam, ReferentialAction,
70+
RenameTableNameKind, ReplicaIdentity, TableConstraint, TagsColumnOption,
71+
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef,
7272
};
73-
pub use self::dml::{CreateIndex, CreateTable, Delete, IndexColumn, Insert};
73+
pub use self::dml::{Delete, Insert};
7474
pub use self::operator::{BinaryOperator, UnaryOperator};
7575
pub use self::query::{
7676
AfterMatchSkip, ConnectBy, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,

src/ast/spans.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ impl Spanned for TableConstraint {
713713
name,
714714
index_type: _,
715715
columns,
716+
index_options: _,
716717
} => union_spans(
717718
name.iter()
718719
.map(|i| i.span)
@@ -747,6 +748,8 @@ impl Spanned for CreateIndex {
747748
nulls_distinct: _, // bool
748749
with,
749750
predicate,
751+
index_options: _,
752+
alter_options,
750753
} = self;
751754

752755
union_spans(
@@ -756,7 +759,8 @@ impl Spanned for CreateIndex {
756759
.chain(columns.iter().map(|i| i.column.span()))
757760
.chain(include.iter().map(|i| i.span))
758761
.chain(with.iter().map(|i| i.span()))
759-
.chain(predicate.iter().map(|i| i.span())),
762+
.chain(predicate.iter().map(|i| i.span()))
763+
.chain(alter_options.iter().map(|i| i.span())),
760764
)
761765
}
762766
}

src/parser/mod.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7114,6 +7114,22 @@ impl<'a> Parser<'a> {
71147114
None
71157115
};
71167116

7117+
// MySQL options (including the modern style of `USING` after the column list instead of
7118+
// before, which is deprecated) shouldn't conflict with other preceding options (e.g. `WITH
7119+
// PARSER` won't be caught by the above `WITH` clause parsing because MySQL doesn't set that
7120+
// support flag). This is probably invalid syntax for other dialects, but it is simpler to
7121+
// parse it anyway (as we do inside `ALTER TABLE` and `CREATE TABLE` parsing).
7122+
let index_options = self.parse_index_options()?;
7123+
7124+
// MySQL allows `ALGORITHM` and `LOCK` options. Unlike in `ALTER TABLE`, they need not be comma separated.
7125+
let mut alter_options = Vec::new();
7126+
while self
7127+
.peek_one_of_keywords(&[Keyword::ALGORITHM, Keyword::LOCK])
7128+
.is_some()
7129+
{
7130+
alter_options.push(self.parse_alter_table_operation()?)
7131+
}
7132+
71177133
Ok(Statement::CreateIndex(CreateIndex {
71187134
name: index_name,
71197135
table_name,
@@ -7126,6 +7142,8 @@ impl<'a> Parser<'a> {
71267142
nulls_distinct,
71277143
with,
71287144
predicate,
7145+
index_options,
7146+
alter_options,
71297147
}))
71307148
}
71317149

@@ -8438,12 +8456,14 @@ impl<'a> Parser<'a> {
84388456

84398457
let index_type = self.parse_optional_using_then_index_type()?;
84408458
let columns = self.parse_parenthesized_index_column_list()?;
8459+
let index_options = self.parse_index_options()?;
84418460

84428461
Ok(Some(TableConstraint::Index {
84438462
display_as_key,
84448463
name,
84458464
index_type,
84468465
columns,
8466+
index_options,
84478467
}))
84488468
}
84498469
Token::Word(w)
@@ -17507,6 +17527,7 @@ mod tests {
1750717527
name: None,
1750817528
index_type: None,
1750917529
columns: vec![mk_expected_col("c1")],
17530+
index_options: vec![],
1751017531
}
1751117532
);
1751217533

@@ -17518,6 +17539,7 @@ mod tests {
1751817539
name: None,
1751917540
index_type: None,
1752017541
columns: vec![mk_expected_col("c1")],
17542+
index_options: vec![],
1752117543
}
1752217544
);
1752317545

@@ -17529,6 +17551,7 @@ mod tests {
1752917551
name: Some(Ident::with_quote('\'', "index")),
1753017552
index_type: None,
1753117553
columns: vec![mk_expected_col("c1"), mk_expected_col("c2")],
17554+
index_options: vec![],
1753217555
}
1753317556
);
1753417557

@@ -17540,6 +17563,7 @@ mod tests {
1754017563
name: None,
1754117564
index_type: Some(IndexType::BTree),
1754217565
columns: vec![mk_expected_col("c1")],
17566+
index_options: vec![],
1754317567
}
1754417568
);
1754517569

@@ -17551,6 +17575,7 @@ mod tests {
1755117575
name: None,
1755217576
index_type: Some(IndexType::Hash),
1755317577
columns: vec![mk_expected_col("c1")],
17578+
index_options: vec![],
1755417579
}
1755517580
);
1755617581

@@ -17562,6 +17587,7 @@ mod tests {
1756217587
name: Some(Ident::new("idx_name")),
1756317588
index_type: Some(IndexType::BTree),
1756417589
columns: vec![mk_expected_col("c1")],
17590+
index_options: vec![],
1756517591
}
1756617592
);
1756717593

@@ -17573,6 +17599,7 @@ mod tests {
1757317599
name: Some(Ident::new("idx_name")),
1757417600
index_type: Some(IndexType::Hash),
1757517601
columns: vec![mk_expected_col("c1")],
17602+
index_options: vec![],
1757617603
}
1757717604
);
1757817605
}

tests/sqlparser_common.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9190,7 +9190,7 @@ fn ensure_multiple_dialects_are_tested() {
91909190

91919191
#[test]
91929192
fn parse_create_index() {
9193-
let sql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_name ON test(name,age DESC)";
9193+
let sql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_name ON test(name, age DESC)";
91949194
let indexed_columns: Vec<IndexColumn> = vec![
91959195
IndexColumn {
91969196
operator_class: None,
@@ -9236,7 +9236,7 @@ fn parse_create_index() {
92369236

92379237
#[test]
92389238
fn test_create_index_with_using_function() {
9239-
let sql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_name ON test USING BTREE (name,age DESC)";
9239+
let sql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_name ON test USING BTREE (name, age DESC)";
92409240
let indexed_columns: Vec<IndexColumn> = vec![
92419241
IndexColumn {
92429242
operator_class: None,
@@ -9274,6 +9274,8 @@ fn test_create_index_with_using_function() {
92749274
nulls_distinct: None,
92759275
with,
92769276
predicate: None,
9277+
index_options,
9278+
alter_options,
92779279
}) => {
92789280
assert_eq!("idx_name", name.to_string());
92799281
assert_eq!("test", table_name.to_string());
@@ -9284,6 +9286,8 @@ fn test_create_index_with_using_function() {
92849286
assert!(if_not_exists);
92859287
assert!(include.is_empty());
92869288
assert!(with.is_empty());
9289+
assert!(index_options.is_empty());
9290+
assert!(alter_options.is_empty());
92879291
}
92889292
_ => unreachable!(),
92899293
}
@@ -9325,6 +9329,8 @@ fn test_create_index_with_with_clause() {
93259329
nulls_distinct: None,
93269330
with,
93279331
predicate: None,
9332+
index_options,
9333+
alter_options,
93289334
}) => {
93299335
pretty_assertions::assert_eq!("title_idx", name.to_string());
93309336
pretty_assertions::assert_eq!("films", table_name.to_string());
@@ -9334,6 +9340,8 @@ fn test_create_index_with_with_clause() {
93349340
assert!(!if_not_exists);
93359341
assert!(include.is_empty());
93369342
pretty_assertions::assert_eq!(with_parameters, with);
9343+
assert!(index_options.is_empty());
9344+
assert!(alter_options.is_empty());
93379345
}
93389346
_ => unreachable!(),
93399347
}

tests/sqlparser_mysql.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4215,3 +4215,31 @@ fn parse_show_charset() {
42154215
mysql().verified_stmt("SHOW CHARSET WHERE charset = 'utf8mb4%'");
42164216
mysql().verified_stmt("SHOW CHARSET LIKE 'utf8mb4%'");
42174217
}
4218+
4219+
#[test]
4220+
fn test_ddl_with_index_using() {
4221+
let columns = "(name, age DESC)";
4222+
let using = "USING BTREE";
4223+
4224+
for sql in [
4225+
format!("CREATE INDEX idx_name ON test {using} {columns}"),
4226+
format!("CREATE TABLE foo (name VARCHAR(255), age INT, KEY idx_name {using} {columns})"),
4227+
format!("ALTER TABLE foo ADD KEY idx_name {using} {columns}"),
4228+
format!("CREATE INDEX idx_name ON test{columns} {using}"),
4229+
format!("CREATE TABLE foo (name VARCHAR(255), age INT, KEY idx_name {columns} {using})"),
4230+
format!("ALTER TABLE foo ADD KEY idx_name {columns} {using}"),
4231+
] {
4232+
mysql_and_generic().verified_stmt(&sql);
4233+
}
4234+
}
4235+
4236+
#[test]
4237+
fn test_create_index_options() {
4238+
mysql_and_generic()
4239+
.verified_stmt("CREATE INDEX idx_name ON t(c1, c2) USING HASH LOCK = SHARED");
4240+
mysql_and_generic()
4241+
.verified_stmt("CREATE INDEX idx_name ON t(c1, c2) USING BTREE ALGORITHM = INPLACE");
4242+
mysql_and_generic().verified_stmt(
4243+
"CREATE INDEX idx_name ON t(c1, c2) USING BTREE LOCK = EXCLUSIVE ALGORITHM = DEFAULT",
4244+
);
4245+
}

0 commit comments

Comments
 (0)