Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
502 changes: 492 additions & 10 deletions src/ast/ddl.rs

Large diffs are not rendered by default.

470 changes: 4 additions & 466 deletions src/ast/dml.rs

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions src/ast/helpers/stmt_create_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};

use super::super::dml::CreateTable;
use crate::ast::{
ClusteredBy, ColumnDef, CommentDef, CreateTableOptions, Expr, FileFormat,
ClusteredBy, ColumnDef, CommentDef, CreateTable, CreateTableOptions, Expr, FileFormat,
HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit, OneOrManyWithParens, Query,
RowAccessPolicy, Statement, StorageSerializationPolicy, TableConstraint, Tag,
WrappedCollection,
Expand Down
14 changes: 7 additions & 7 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ pub use self::ddl::{
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain, CreateFunction,
Deduplicate, DeferrableInitial, DropBehavior, GeneratedAs, GeneratedExpressionMode,
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
IdentityPropertyOrder, IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner,
Partition, ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity,
TableConstraint, TagsColumnOption, UserDefinedTypeCompositeAttributeDef,
UserDefinedTypeRepresentation, ViewColumnDef,
CreateIndex, CreateTable, Deduplicate, DeferrableInitial, DropBehavior, GeneratedAs,
GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
IdentityPropertyKind, IdentityPropertyOrder, IndexColumn, IndexOption, IndexType,
KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition, ProcedureParam, ReferentialAction,
RenameTableNameKind, ReplicaIdentity, TableConstraint, TagsColumnOption,
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef,
};
pub use self::dml::{CreateIndex, CreateTable, Delete, IndexColumn, Insert};
pub use self::dml::{Delete, Insert};
pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{
AfterMatchSkip, ConnectBy, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
Expand Down
6 changes: 5 additions & 1 deletion src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,7 @@ impl Spanned for TableConstraint {
name,
index_type: _,
columns,
index_options: _,
} => union_spans(
name.iter()
.map(|i| i.span)
Expand Down Expand Up @@ -747,6 +748,8 @@ impl Spanned for CreateIndex {
nulls_distinct: _, // bool
with,
predicate,
index_options: _,
alter_options,
} = self;

union_spans(
Expand All @@ -756,7 +759,8 @@ impl Spanned for CreateIndex {
.chain(columns.iter().map(|i| i.column.span()))
.chain(include.iter().map(|i| i.span))
.chain(with.iter().map(|i| i.span()))
.chain(predicate.iter().map(|i| i.span())),
.chain(predicate.iter().map(|i| i.span()))
.chain(alter_options.iter().map(|i| i.span())),
)
}
}
Expand Down
27 changes: 27 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7077,6 +7077,22 @@ impl<'a> Parser<'a> {
None
};

// MySQL options (including the modern style of `USING` after the column list instead of
// before, which is deprecated) shouldn't conflict with other preceding options (e.g. `WITH
// PARSER` won't be caught by the above `WITH` clause parsing because MySQL doesn't set that
// support flag). This is probably invalid syntax for other dialects, but it is simpler to
// parse it anyway (as we do inside `ALTER TABLE` and `CREATE TABLE` parsing).
let index_options = self.parse_index_options()?;

// MySQL allows `ALGORITHM` and `LOCK` options. Unlike in `ALTER TABLE`, they need not be comma separated.
let mut alter_options = Vec::new();
while self
.peek_one_of_keywords(&[Keyword::ALGORITHM, Keyword::LOCK])
.is_some()
{
alter_options.push(self.parse_alter_table_operation()?)
}

Ok(Statement::CreateIndex(CreateIndex {
name: index_name,
table_name,
Expand All @@ -7089,6 +7105,8 @@ impl<'a> Parser<'a> {
nulls_distinct,
with,
predicate,
index_options,
alter_options,
}))
}

Expand Down Expand Up @@ -8401,12 +8419,14 @@ impl<'a> Parser<'a> {

let index_type = self.parse_optional_using_then_index_type()?;
let columns = self.parse_parenthesized_index_column_list()?;
let index_options = self.parse_index_options()?;

Ok(Some(TableConstraint::Index {
display_as_key,
name,
index_type,
columns,
index_options,
}))
}
Token::Word(w)
Expand Down Expand Up @@ -17469,6 +17489,7 @@ mod tests {
name: None,
index_type: None,
columns: vec![mk_expected_col("c1")],
index_options: vec![],
}
);

Expand All @@ -17480,6 +17501,7 @@ mod tests {
name: None,
index_type: None,
columns: vec![mk_expected_col("c1")],
index_options: vec![],
}
);

Expand All @@ -17491,6 +17513,7 @@ mod tests {
name: Some(Ident::with_quote('\'', "index")),
index_type: None,
columns: vec![mk_expected_col("c1"), mk_expected_col("c2")],
index_options: vec![],
}
);

Expand All @@ -17502,6 +17525,7 @@ mod tests {
name: None,
index_type: Some(IndexType::BTree),
columns: vec![mk_expected_col("c1")],
index_options: vec![],
}
);

Expand All @@ -17513,6 +17537,7 @@ mod tests {
name: None,
index_type: Some(IndexType::Hash),
columns: vec![mk_expected_col("c1")],
index_options: vec![],
}
);

Expand All @@ -17524,6 +17549,7 @@ mod tests {
name: Some(Ident::new("idx_name")),
index_type: Some(IndexType::BTree),
columns: vec![mk_expected_col("c1")],
index_options: vec![],
}
);

Expand All @@ -17535,6 +17561,7 @@ mod tests {
name: Some(Ident::new("idx_name")),
index_type: Some(IndexType::Hash),
columns: vec![mk_expected_col("c1")],
index_options: vec![],
}
);
}
Expand Down
12 changes: 10 additions & 2 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9189,7 +9189,7 @@ fn ensure_multiple_dialects_are_tested() {

#[test]
fn parse_create_index() {
let sql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_name ON test(name,age DESC)";
let sql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_name ON test(name, age DESC)";
let indexed_columns: Vec<IndexColumn> = vec![
IndexColumn {
operator_class: None,
Expand Down Expand Up @@ -9235,7 +9235,7 @@ fn parse_create_index() {

#[test]
fn test_create_index_with_using_function() {
let sql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_name ON test USING BTREE (name,age DESC)";
let sql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_name ON test USING BTREE (name, age DESC)";
let indexed_columns: Vec<IndexColumn> = vec![
IndexColumn {
operator_class: None,
Expand Down Expand Up @@ -9273,6 +9273,8 @@ fn test_create_index_with_using_function() {
nulls_distinct: None,
with,
predicate: None,
index_options,
alter_options,
}) => {
assert_eq!("idx_name", name.to_string());
assert_eq!("test", table_name.to_string());
Expand All @@ -9283,6 +9285,8 @@ fn test_create_index_with_using_function() {
assert!(if_not_exists);
assert!(include.is_empty());
assert!(with.is_empty());
assert!(index_options.is_empty());
assert!(alter_options.is_empty());
}
_ => unreachable!(),
}
Expand Down Expand Up @@ -9324,6 +9328,8 @@ fn test_create_index_with_with_clause() {
nulls_distinct: None,
with,
predicate: None,
index_options,
alter_options,
}) => {
pretty_assertions::assert_eq!("title_idx", name.to_string());
pretty_assertions::assert_eq!("films", table_name.to_string());
Expand All @@ -9333,6 +9339,8 @@ fn test_create_index_with_with_clause() {
assert!(!if_not_exists);
assert!(include.is_empty());
pretty_assertions::assert_eq!(with_parameters, with);
assert!(index_options.is_empty());
assert!(alter_options.is_empty());
}
_ => unreachable!(),
}
Expand Down
28 changes: 28 additions & 0 deletions tests/sqlparser_mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4210,3 +4210,31 @@ fn parse_show_charset() {
mysql().verified_stmt("SHOW CHARSET WHERE charset = 'utf8mb4%'");
mysql().verified_stmt("SHOW CHARSET LIKE 'utf8mb4%'");
}

#[test]
fn test_ddl_with_index_using() {
let columns = "(name, age DESC)";
let using = "USING BTREE";

for sql in [
format!("CREATE INDEX idx_name ON test {using} {columns}"),
format!("CREATE TABLE foo (name VARCHAR(255), age INT, KEY idx_name {using} {columns})"),
format!("ALTER TABLE foo ADD KEY idx_name {using} {columns}"),
format!("CREATE INDEX idx_name ON test{columns} {using}"),
format!("CREATE TABLE foo (name VARCHAR(255), age INT, KEY idx_name {columns} {using})"),
format!("ALTER TABLE foo ADD KEY idx_name {columns} {using}"),
] {
mysql_and_generic().verified_stmt(&sql);
}
}

#[test]
fn test_create_index_options() {
mysql_and_generic()
.verified_stmt("CREATE INDEX idx_name ON t(c1, c2) USING HASH LOCK = SHARED");
mysql_and_generic()
.verified_stmt("CREATE INDEX idx_name ON t(c1, c2) USING BTREE ALGORITHM = INPLACE");
mysql_and_generic().verified_stmt(
"CREATE INDEX idx_name ON t(c1, c2) USING BTREE LOCK = EXCLUSIVE ALGORITHM = DEFAULT",
);
}
Loading