Merge pull request #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>
This commit is contained in:
Nickolay Ponomarev 2020-05-27 05:13:06 +03:00 committed by GitHub
commit 789fcc8521
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 96 additions and 2 deletions

View file

@ -13,6 +13,7 @@ Check https://github.com/andygrove/sqlparser-rs/commits/master for undocumented
- Support Snowflake's `FROM (table_name)` (#155) - thanks @eyalleshem!
### Added
- Support basic forms of `CREATE INDEX` and `DROP INDEX` (#167) - thanks @mashuai!
- Support MSSQL `TOP (<N>) [ PERCENT ] [ WITH TIES ]` (#150) - thanks @alexkyllo!
- Support MySQL `LIMIT row_count OFFSET offset` (not followed by `ROW` or `ROWS`) and remember which variant was parsed (#158) - thanks @mjibson!
- Support PostgreSQL `CREATE TABLE IF NOT EXISTS table_name` (#163) - thanks @alex-dukhno!

View file

@ -476,6 +476,15 @@ pub enum Statement {
file_format: Option<FileFormat>,
location: Option<String>,
},
/// CREATE INDEX
CreateIndex {
/// index name
name: ObjectName,
table_name: ObjectName,
columns: Vec<Ident>,
unique: bool,
if_not_exists: bool,
},
/// ALTER TABLE
AlterTable {
/// Table name
@ -655,6 +664,28 @@ impl fmt::Display for Statement {
}
Ok(())
}
Statement::CreateIndex {
name,
table_name,
columns,
unique,
if_not_exists,
} => {
write!(
f,
"CREATE{}INDEX{}{} ON {}({}",
if *unique { " UNIQUE " } else { " " },
if *if_not_exists {
" IF NOT EXISTS "
} else {
" "
},
name,
table_name,
display_separated(columns, ",")
)?;
write!(f, ");")
}
Statement::AlterTable { name, operation } => {
write!(f, "ALTER TABLE {} {}", name, operation)
}
@ -819,6 +850,7 @@ impl FromStr for FileFormat {
pub enum ObjectType {
Table,
View,
Index,
}
impl fmt::Display for ObjectType {
@ -826,6 +858,7 @@ impl fmt::Display for ObjectType {
f.write_str(match self {
ObjectType::Table => "TABLE",
ObjectType::View => "VIEW",
ObjectType::Index => "INDEX",
})
}
}

View file

@ -200,6 +200,7 @@ define_keywords!(
IDENTITY,
IF,
IN,
INDEX,
INDICATOR,
INNER,
INOUT,

View file

@ -855,13 +855,17 @@ impl Parser {
pub fn parse_create(&mut self) -> Result<Statement, ParserError> {
if self.parse_keyword("TABLE") {
self.parse_create_table()
} else if self.parse_keyword("INDEX") {
self.parse_create_index(false)
} else if self.parse_keywords(vec!["UNIQUE", "INDEX"]) {
self.parse_create_index(true)
} else if self.parse_keyword("MATERIALIZED") || self.parse_keyword("VIEW") {
self.prev_token();
self.parse_create_view()
} else if self.parse_keyword("EXTERNAL") {
self.parse_create_external_table()
} else {
self.expected("TABLE or VIEW after CREATE", self.peek_token())
self.expected("TABLE, VIEW or INDEX after CREATE", self.peek_token())
}
}
@ -912,8 +916,10 @@ impl Parser {
ObjectType::Table
} else if self.parse_keyword("VIEW") {
ObjectType::View
} else if self.parse_keyword("INDEX") {
ObjectType::Index
} else {
return self.expected("TABLE or VIEW after DROP", self.peek_token());
return self.expected("TABLE, VIEW or INDEX after DROP", self.peek_token());
};
// Many dialects support the non standard `IF EXISTS` clause and allow
// specifying multiple objects to delete in a single statement
@ -932,6 +938,21 @@ impl Parser {
})
}
pub fn parse_create_index(&mut self, unique: bool) -> Result<Statement, ParserError> {
let if_not_exists = self.parse_keywords(vec!["IF", "NOT", "EXISTS"]);
let index_name = self.parse_object_name()?;
self.expect_keyword("ON")?;
let table_name = self.parse_object_name()?;
let columns = self.parse_parenthesized_column_list(Mandatory)?;
Ok(Statement::CreateIndex {
name: index_name,
table_name,
columns,
unique,
if_not_exists,
})
}
pub fn parse_create_table(&mut self) -> Result<Statement, ParserError> {
let if_not_exists = self.parse_keywords(vec!["IF", "NOT", "EXISTS"]);
let table_name = self.parse_object_name()?;

View file

@ -2631,6 +2631,44 @@ fn ensure_multiple_dialects_are_tested() {
let _ = parse_sql_statements("SELECT @foo");
}
#[test]
fn parse_create_index() {
let sql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_name ON test(name,age);";
let ident_vec = vec![Ident::new("name"), Ident::new("age")];
match verified_stmt(sql) {
Statement::CreateIndex {
name,
table_name,
columns,
unique,
if_not_exists,
} => {
assert_eq!("idx_name", name.to_string());
assert_eq!("test", table_name.to_string());
assert_eq!(ident_vec, columns);
assert_eq!(true, unique);
assert_eq!(true, if_not_exists)
}
_ => unreachable!(),
}
}
#[test]
fn parse_drop_index() {
let sql = "DROP INDEX idx_a";
match verified_stmt(sql) {
Statement::Drop {
names, object_type, ..
} => {
assert_eq!(
vec!["idx_a"],
names.iter().map(ToString::to_string).collect::<Vec<_>>()
);
assert_eq!(ObjectType::Index, object_type);
}
_ => unreachable!(),
}
}
fn parse_sql_statements(sql: &str) -> Result<Vec<Statement>, ParserError> {
all_dialects().parse_sql_statements(sql)
}