Skip to content

Commit 3a077b0

Browse files
authored
Merge pull request MaterializeInc#14 from wangandi/indexing
Support CREATE/DROP INDEX
2 parents 935e52c + cd1be10 commit 3a077b0

File tree

5 files changed

+199
-3
lines changed

5 files changed

+199
-3
lines changed

src/ast/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,15 @@ pub enum Statement {
499499
file_format: Option<FileFormat>,
500500
location: Option<String>,
501501
},
502+
/// `CREATE INDEX`
503+
CreateIndex {
504+
/// Index name
505+
name: Ident,
506+
/// `ON` table or view name
507+
on_name: ObjectName,
508+
/// Expressions that form part of the index key
509+
key_parts: Vec<Expr>,
510+
},
502511
/// `ALTER TABLE`
503512
AlterTable {
504513
/// Table name
@@ -759,6 +768,20 @@ impl fmt::Display for Statement {
759768
}
760769
Ok(())
761770
}
771+
Statement::CreateIndex {
772+
name,
773+
on_name,
774+
key_parts,
775+
} => {
776+
write!(
777+
f,
778+
"CREATE INDEX {} ON {} ({})",
779+
name,
780+
on_name,
781+
display_comma_separated(key_parts),
782+
)?;
783+
Ok(())
784+
}
762785
Statement::AlterTable { name, operation } => {
763786
write!(f, "ALTER TABLE {} {}", name, operation)
764787
}
@@ -797,6 +820,7 @@ impl fmt::Display for Statement {
797820
View => "VIEWS",
798821
Source => "SOURCES",
799822
Sink => "SINKS",
823+
Index => unreachable!(),
800824
}
801825
)
802826
}
@@ -963,6 +987,7 @@ pub enum ObjectType {
963987
View,
964988
Source,
965989
Sink,
990+
Index,
966991
}
967992

968993
impl fmt::Display for ObjectType {
@@ -972,6 +997,7 @@ impl fmt::Display for ObjectType {
972997
ObjectType::View => "VIEW",
973998
ObjectType::Source => "SOURCE",
974999
ObjectType::Sink => "SINK",
1000+
ObjectType::Index => "INDEX",
9751001
})
9761002
}
9771003
}

src/ast/visit_macro.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,15 @@ macro_rules! make_visitor {
382382
visit_create_view(self, name, columns, query, materialized, with_options)
383383
}
384384

385+
fn visit_create_index(
386+
&mut self,
387+
name: &'ast $($mut)* Ident,
388+
on_name: &'ast $($mut)* ObjectName,
389+
key_parts: &'ast $($mut)* Vec<Expr>
390+
){
391+
visit_create_index(self, name, on_name, key_parts)
392+
}
393+
385394
fn visit_create_table(
386395
&mut self,
387396
name: &'ast $($mut)* ObjectName,
@@ -602,6 +611,11 @@ macro_rules! make_visitor {
602611
materialized,
603612
with_options,
604613
} => visitor.visit_create_view(name, columns, query, *materialized, with_options),
614+
Statement::CreateIndex {
615+
name,
616+
on_name,
617+
key_parts,
618+
} => visitor.visit_create_index(name, on_name, key_parts),
605619
Statement::Drop {
606620
object_type,
607621
if_exists,
@@ -1288,6 +1302,19 @@ macro_rules! make_visitor {
12881302
visitor.visit_query(query);
12891303
}
12901304

1305+
pub fn visit_create_index<'ast, V: $name<'ast> + ?Sized>(
1306+
visitor: &mut V,
1307+
name: &'ast $($mut)* Ident,
1308+
on_name: &'ast $($mut)* ObjectName,
1309+
key_parts: &'ast $($mut)* Vec<Expr>,
1310+
) {
1311+
visitor.visit_ident(name);
1312+
visitor.visit_object_name(on_name);
1313+
for key_part in key_parts {
1314+
visitor.visit_expr(key_part);
1315+
}
1316+
}
1317+
12911318
pub fn visit_create_table<'ast, V: $name<'ast> + ?Sized>(
12921319
visitor: &mut V,
12931320
name: &'ast $($mut)* ObjectName,

src/dialect/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ define_keywords!(
203203
IF,
204204
IMMEDIATE,
205205
IN,
206+
INDEX,
206207
INDICATOR,
207208
INNER,
208209
INOUT,

src/parser.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,9 +1055,11 @@ impl Parser {
10551055
self.parse_create_sink()
10561056
} else if self.parse_keyword("EXTERNAL") {
10571057
self.parse_create_external_table()
1058+
} else if self.parse_keyword("INDEX") {
1059+
self.parse_create_index()
10581060
} else {
10591061
self.expected(
1060-
"TABLE, VIEW, SOURCE, or SINK after CREATE",
1062+
"TABLE, VIEW, SOURCE, SINK, or INDEX after CREATE",
10611063
self.peek_token(),
10621064
)
10631065
}
@@ -1163,6 +1165,20 @@ impl Parser {
11631165
})
11641166
}
11651167

1168+
pub fn parse_create_index(&mut self) -> Result<Statement, ParserError> {
1169+
let name = self.parse_identifier()?;
1170+
self.expect_keyword("ON")?;
1171+
let on_name = self.parse_object_name()?;
1172+
self.expect_token(&Token::LParen)?;
1173+
let key_parts = self.parse_comma_separated(Parser::parse_expr)?;
1174+
self.expect_token(&Token::RParen)?;
1175+
Ok(Statement::CreateIndex {
1176+
name,
1177+
on_name,
1178+
key_parts,
1179+
})
1180+
}
1181+
11661182
pub fn parse_drop(&mut self) -> Result<Statement, ParserError> {
11671183
let object_type = if self.parse_keyword("TABLE") {
11681184
ObjectType::Table
@@ -1172,8 +1188,13 @@ impl Parser {
11721188
ObjectType::Source
11731189
} else if self.parse_keywords(vec!["SINK"]) {
11741190
ObjectType::Sink
1191+
} else if self.parse_keyword("INDEX") {
1192+
ObjectType::Index
11751193
} else {
1176-
return self.expected("TABLE or VIEW after DROP", self.peek_token());
1194+
return self.expected(
1195+
"TABLE, VIEW, SOURCE, SINK, or INDEX after DROP",
1196+
self.peek_token(),
1197+
);
11771198
};
11781199
// Many dialects support the non standard `IF EXISTS` clause and allow
11791200
// specifying multiple objects to delete in a single statement

tests/sqlparser_common.rs

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1872,7 +1872,9 @@ fn parse_show_objects() {
18721872
fn parse_show_create_view() {
18731873
assert_eq!(
18741874
verified_stmt("SHOW CREATE VIEW foo"),
1875-
Statement::ShowCreateView { view_name: ObjectName(vec!["foo".into()]) }
1875+
Statement::ShowCreateView {
1876+
view_name: ObjectName(vec!["foo".into()])
1877+
}
18761878
)
18771879
}
18781880

@@ -2711,6 +2713,103 @@ fn parse_create_sink() {
27112713
}
27122714
}
27132715

2716+
#[test]
2717+
fn parse_create_index() {
2718+
let sql = "CREATE INDEX foo ON myschema.bar (a, b)";
2719+
match verified_stmt(sql) {
2720+
Statement::CreateIndex {
2721+
name,
2722+
on_name,
2723+
key_parts,
2724+
} => {
2725+
assert_eq!("foo", name.to_string());
2726+
assert_eq!("myschema.bar", on_name.to_string());
2727+
assert_eq!(
2728+
key_parts,
2729+
vec![
2730+
Expr::Identifier("a".to_string()),
2731+
Expr::Identifier("b".to_string())
2732+
]
2733+
);
2734+
}
2735+
_ => assert!(false),
2736+
}
2737+
2738+
let sql = "CREATE INDEX fizz ON baz (ascii(x), a IS NOT NULL, (EXISTS (SELECT y FROM boop WHERE boop.z = z)), delta)";
2739+
match verified_stmt(sql) {
2740+
Statement::CreateIndex {
2741+
name,
2742+
on_name,
2743+
key_parts,
2744+
} => {
2745+
assert_eq!("fizz", name.to_string());
2746+
assert_eq!("baz", on_name.to_string());
2747+
assert_matches!(key_parts[0], Expr::Function(..));
2748+
assert_eq!(
2749+
key_parts[1],
2750+
Expr::IsNotNull(Box::new(Expr::Identifier("a".to_string())))
2751+
);
2752+
if let Expr::Nested(expr) = &key_parts[2] {
2753+
assert_matches!(**expr, Expr::Exists(..));
2754+
} else {
2755+
assert!(false);
2756+
}
2757+
assert_eq!(key_parts[3], Expr::Identifier("delta".to_string()));
2758+
}
2759+
_ => assert!(false),
2760+
}
2761+
2762+
let sql = "CREATE INDEX ind ON tab ((col + 1))";
2763+
match verified_stmt(sql) {
2764+
Statement::CreateIndex {
2765+
name,
2766+
on_name,
2767+
key_parts,
2768+
} => {
2769+
assert_eq!("ind", name.to_string());
2770+
assert_eq!("tab", on_name.to_string());
2771+
assert_eq!(
2772+
key_parts,
2773+
vec![Expr::Nested(Box::new(Expr::BinaryOp {
2774+
left: Box::new(Expr::Identifier("col".to_string())),
2775+
op: BinaryOperator::Plus,
2776+
right: Box::new(Expr::Value(Value::Number("1".to_string())))
2777+
}))],
2778+
);
2779+
}
2780+
_ => assert!(false),
2781+
}
2782+
let sql = "CREATE INDEX qualifiers ON no_parentheses (alpha.omega)";
2783+
match verified_stmt(sql) {
2784+
Statement::CreateIndex {
2785+
name,
2786+
on_name,
2787+
key_parts,
2788+
} => {
2789+
assert_eq!("qualifiers", name.to_string());
2790+
assert_eq!("no_parentheses", on_name.to_string());
2791+
assert_eq!(
2792+
key_parts,
2793+
vec![Expr::CompoundIdentifier(vec![
2794+
"alpha".to_string(),
2795+
"omega".to_string()
2796+
])],
2797+
);
2798+
}
2799+
_ => assert!(false),
2800+
}
2801+
}
2802+
2803+
#[test]
2804+
fn parse_invalid_create_index() {
2805+
// Index names should not have a schema in front of it
2806+
let res = parse_sql_statements("CREATE INDEX myschema.ind ON foo(b)");
2807+
assert_eq!(
2808+
ParserError::ParserError("Expected ON, found: .".to_string()),
2809+
res.unwrap_err(),
2810+
);
2811+
}
2812+
27142813
#[test]
27152814
fn parse_drop_table() {
27162815
let sql = "DROP TABLE foo";
@@ -2803,6 +2902,28 @@ fn parse_drop_source() {
28032902
}
28042903
}
28052904

2905+
#[test]
2906+
fn parse_drop_index() {
2907+
let sql = "DROP INDEX IF EXISTS myschema.myindex";
2908+
match verified_stmt(sql) {
2909+
Statement::Drop {
2910+
object_type,
2911+
if_exists,
2912+
names,
2913+
cascade,
2914+
} => {
2915+
assert_eq!(true, if_exists);
2916+
assert_eq!(
2917+
vec!["myschema.myindex"],
2918+
names.iter().map(|n| n.to_string()).collect::<Vec<_>>()
2919+
);
2920+
assert_eq!(false, cascade);
2921+
assert_eq!(ObjectType::Index, object_type);
2922+
}
2923+
_ => unreachable!(),
2924+
}
2925+
}
2926+
28062927
#[test]
28072928
fn parse_peek() {
28082929
let sql = "PEEK foo.bar";

0 commit comments

Comments
 (0)