Skip to content

Commit 789fcc8

Browse files
authored
Merge pull request apache#167 from mashuai/add_index_support
Adds support for the most common forms of CREATE INDEX, and for DROP INDEX: CREATE [ UNIQUE ] INDEX [ IF NOT EXISTS ] <index_name> ON <table_name> ( col_name [, ...] ) DROP INDEX <index_name>
2 parents 2644bc4 + 320d2f2 commit 789fcc8

File tree

5 files changed

+96
-2
lines changed

5 files changed

+96
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Check https://github.com/andygrove/sqlparser-rs/commits/master for undocumented
1313
- Support Snowflake's `FROM (table_name)` (#155) - thanks @eyalleshem!
1414

1515
### Added
16+
- Support basic forms of `CREATE INDEX` and `DROP INDEX` (#167) - thanks @mashuai!
1617
- Support MSSQL `TOP (<N>) [ PERCENT ] [ WITH TIES ]` (#150) - thanks @alexkyllo!
1718
- Support MySQL `LIMIT row_count OFFSET offset` (not followed by `ROW` or `ROWS`) and remember which variant was parsed (#158) - thanks @mjibson!
1819
- Support PostgreSQL `CREATE TABLE IF NOT EXISTS table_name` (#163) - thanks @alex-dukhno!

src/ast/mod.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,15 @@ pub enum Statement {
476476
file_format: Option<FileFormat>,
477477
location: Option<String>,
478478
},
479+
/// CREATE INDEX
480+
CreateIndex {
481+
/// index name
482+
name: ObjectName,
483+
table_name: ObjectName,
484+
columns: Vec<Ident>,
485+
unique: bool,
486+
if_not_exists: bool,
487+
},
479488
/// ALTER TABLE
480489
AlterTable {
481490
/// Table name
@@ -655,6 +664,28 @@ impl fmt::Display for Statement {
655664
}
656665
Ok(())
657666
}
667+
Statement::CreateIndex {
668+
name,
669+
table_name,
670+
columns,
671+
unique,
672+
if_not_exists,
673+
} => {
674+
write!(
675+
f,
676+
"CREATE{}INDEX{}{} ON {}({}",
677+
if *unique { " UNIQUE " } else { " " },
678+
if *if_not_exists {
679+
" IF NOT EXISTS "
680+
} else {
681+
" "
682+
},
683+
name,
684+
table_name,
685+
display_separated(columns, ",")
686+
)?;
687+
write!(f, ");")
688+
}
658689
Statement::AlterTable { name, operation } => {
659690
write!(f, "ALTER TABLE {} {}", name, operation)
660691
}
@@ -819,13 +850,15 @@ impl FromStr for FileFormat {
819850
pub enum ObjectType {
820851
Table,
821852
View,
853+
Index,
822854
}
823855

824856
impl fmt::Display for ObjectType {
825857
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
826858
f.write_str(match self {
827859
ObjectType::Table => "TABLE",
828860
ObjectType::View => "VIEW",
861+
ObjectType::Index => "INDEX",
829862
})
830863
}
831864
}

src/dialect/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ define_keywords!(
200200
IDENTITY,
201201
IF,
202202
IN,
203+
INDEX,
203204
INDICATOR,
204205
INNER,
205206
INOUT,

src/parser.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -855,13 +855,17 @@ impl Parser {
855855
pub fn parse_create(&mut self) -> Result<Statement, ParserError> {
856856
if self.parse_keyword("TABLE") {
857857
self.parse_create_table()
858+
} else if self.parse_keyword("INDEX") {
859+
self.parse_create_index(false)
860+
} else if self.parse_keywords(vec!["UNIQUE", "INDEX"]) {
861+
self.parse_create_index(true)
858862
} else if self.parse_keyword("MATERIALIZED") || self.parse_keyword("VIEW") {
859863
self.prev_token();
860864
self.parse_create_view()
861865
} else if self.parse_keyword("EXTERNAL") {
862866
self.parse_create_external_table()
863867
} else {
864-
self.expected("TABLE or VIEW after CREATE", self.peek_token())
868+
self.expected("TABLE, VIEW or INDEX after CREATE", self.peek_token())
865869
}
866870
}
867871

@@ -912,8 +916,10 @@ impl Parser {
912916
ObjectType::Table
913917
} else if self.parse_keyword("VIEW") {
914918
ObjectType::View
919+
} else if self.parse_keyword("INDEX") {
920+
ObjectType::Index
915921
} else {
916-
return self.expected("TABLE or VIEW after DROP", self.peek_token());
922+
return self.expected("TABLE, VIEW or INDEX after DROP", self.peek_token());
917923
};
918924
// Many dialects support the non standard `IF EXISTS` clause and allow
919925
// specifying multiple objects to delete in a single statement
@@ -932,6 +938,21 @@ impl Parser {
932938
})
933939
}
934940

941+
pub fn parse_create_index(&mut self, unique: bool) -> Result<Statement, ParserError> {
942+
let if_not_exists = self.parse_keywords(vec!["IF", "NOT", "EXISTS"]);
943+
let index_name = self.parse_object_name()?;
944+
self.expect_keyword("ON")?;
945+
let table_name = self.parse_object_name()?;
946+
let columns = self.parse_parenthesized_column_list(Mandatory)?;
947+
Ok(Statement::CreateIndex {
948+
name: index_name,
949+
table_name,
950+
columns,
951+
unique,
952+
if_not_exists,
953+
})
954+
}
955+
935956
pub fn parse_create_table(&mut self) -> Result<Statement, ParserError> {
936957
let if_not_exists = self.parse_keywords(vec!["IF", "NOT", "EXISTS"]);
937958
let table_name = self.parse_object_name()?;

tests/sqlparser_common.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2631,6 +2631,44 @@ fn ensure_multiple_dialects_are_tested() {
26312631
let _ = parse_sql_statements("SELECT @foo");
26322632
}
26332633

2634+
#[test]
2635+
fn parse_create_index() {
2636+
let sql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_name ON test(name,age);";
2637+
let ident_vec = vec![Ident::new("name"), Ident::new("age")];
2638+
match verified_stmt(sql) {
2639+
Statement::CreateIndex {
2640+
name,
2641+
table_name,
2642+
columns,
2643+
unique,
2644+
if_not_exists,
2645+
} => {
2646+
assert_eq!("idx_name", name.to_string());
2647+
assert_eq!("test", table_name.to_string());
2648+
assert_eq!(ident_vec, columns);
2649+
assert_eq!(true, unique);
2650+
assert_eq!(true, if_not_exists)
2651+
}
2652+
_ => unreachable!(),
2653+
}
2654+
}
2655+
#[test]
2656+
fn parse_drop_index() {
2657+
let sql = "DROP INDEX idx_a";
2658+
match verified_stmt(sql) {
2659+
Statement::Drop {
2660+
names, object_type, ..
2661+
} => {
2662+
assert_eq!(
2663+
vec!["idx_a"],
2664+
names.iter().map(ToString::to_string).collect::<Vec<_>>()
2665+
);
2666+
assert_eq!(ObjectType::Index, object_type);
2667+
}
2668+
_ => unreachable!(),
2669+
}
2670+
}
2671+
26342672
fn parse_sql_statements(sql: &str) -> Result<Vec<Statement>, ParserError> {
26352673
all_dialects().parse_sql_statements(sql)
26362674
}

0 commit comments

Comments
 (0)