Skip to content

Commit ae8cbca

Browse files
committed
sql_parser: crete index statement parser implemented
1 parent 1acf4ac commit ae8cbca

File tree

5 files changed

+177
-16
lines changed

5 files changed

+177
-16
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ The items were taken from the official SQLite documentation
8989
1. [insert-with-cte](https://www.sqlite.org/lang_insert.html) ✅
9090
1. [replace-stmt](https://www.sqlite.org/lang_replace.html) ✅
9191

92-
#### CREATE Statements ![progress](https://progress-bar.xyz/0/?scale=45&suffix=%%%20(0%20of%205)&width=140)
93-
1. [create-virtual-table-stmt](https://www.sqlite.org/lang_createvtab.html)
94-
1. [create-index-stmt](https://www.sqlite.org/lang_createindex.html)
92+
#### CREATE Statements ![progress](https://progress-bar.xyz/2/?scale=45&suffix=%%%20(2%20of%205)&width=140)
93+
1. [create-virtual-table-stmt](https://www.sqlite.org/lang_createvtab.html) ✅
94+
1. [create-index-stmt](https://www.sqlite.org/lang_createindex.html) ✅
9595
1. [create-view-stmt](https://www.sqlite.org/lang_createview.html)
9696
1. [create-table-stmt](https://www.sqlite.org/lang_createtable.html)
9797
1. [create-trigger-stmt](https://www.sqlite.org/lang_createtrigger.html)

src/ast/create.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ pub struct CreateIndexStatement {
2626

2727
pub table_name: Identifier,
2828

29-
pub columns: Vec<Identifier>,
29+
pub columns: Vec<IndexedColumn>,
3030

31-
pub where_clause: Option<Expression>,
31+
pub where_clause: Option<Box<Expression>>,
3232
}
3333

3434
/// An AST for [CREATE VIEW](https://www.sqlite.org/lang_createview.html) SQL statement.

src/parser/create/create_index.rs

+146
Original file line numberDiff line numberDiff line change
@@ -1 +1,147 @@
1+
use crate::{
2+
expression::IdentifierParser,
3+
parser::{insert::InsertStatementParser, select::SelectStatementParser},
4+
CreateIndexStatement, Keyword, Parser, ParsingError,
5+
};
16

7+
use super::CreateStatementParser;
8+
9+
pub trait CreateIndexStatementParser {
10+
fn parse_create_index_statement(
11+
&mut self,
12+
unique: bool,
13+
) -> Result<CreateIndexStatement, ParsingError>;
14+
}
15+
16+
impl<'a> CreateIndexStatementParser for Parser<'a> {
17+
fn parse_create_index_statement(
18+
&mut self,
19+
unique: bool,
20+
) -> Result<CreateIndexStatement, ParsingError> {
21+
self.consume_as_keyword(Keyword::Index)?;
22+
23+
let if_not_exists = self.parse_if_not_exists()?;
24+
let index_name = self.parse_identifier()?;
25+
self.consume_as_keyword(Keyword::On)?;
26+
let table_name = self.parse_identifier()?;
27+
28+
let columns = self.parse_indexed_columns()?;
29+
30+
let where_clause = self.parse_where_clause()?;
31+
32+
Ok(CreateIndexStatement {
33+
unique,
34+
if_not_exists,
35+
index_name,
36+
table_name,
37+
columns,
38+
where_clause,
39+
})
40+
}
41+
}
42+
43+
#[cfg(test)]
44+
mod test_utils {
45+
use crate::{
46+
expression::test_utils::identifier_expression, CreateIndexStatement, Identifier,
47+
IndexedColumn,
48+
};
49+
50+
pub fn create_index_statement() -> CreateIndexStatement {
51+
CreateIndexStatement {
52+
unique: false,
53+
if_not_exists: false,
54+
index_name: Identifier::from("index_name"),
55+
table_name: Identifier::from("table_name"),
56+
columns: vec![IndexedColumn {
57+
column: identifier_expression(&["column1"]),
58+
ordering: None,
59+
}],
60+
where_clause: None,
61+
}
62+
}
63+
}
64+
65+
#[cfg(test)]
66+
mod test_create_index_statement_parser {
67+
use test_utils::create_index_statement;
68+
69+
use crate::{
70+
expression::test_utils::{
71+
binary_op_expression, identifier_expression, numeric_literal_expression,
72+
},
73+
parser::test_utils::run_sunny_day_test,
74+
BinaryOp, Identifier, IndexedColumn, Statement,
75+
};
76+
77+
use super::*;
78+
79+
#[test]
80+
fn test_parse_create_index_statement() {
81+
let sql = "CREATE INDEX index_name ON table_name (column1)";
82+
let expected = create_index_statement();
83+
run_sunny_day_test(sql, Statement::CreateIndex(expected));
84+
}
85+
86+
#[test]
87+
fn test_parse_create_unique_index_statement() {
88+
let sql = "CREATE UNIQUE INDEX index_name ON table_name (column1)";
89+
let mut expected = create_index_statement();
90+
expected.unique = true;
91+
run_sunny_day_test(sql, Statement::CreateIndex(expected));
92+
}
93+
94+
#[test]
95+
fn test_parse_create_index_statement_with_schema() {
96+
let sql = "CREATE INDEX schema_name.index_name ON table_name (column1)";
97+
let mut expected = create_index_statement();
98+
expected.index_name =
99+
Identifier::Compound(vec!["schema_name".to_string(), "index_name".to_string()]);
100+
run_sunny_day_test(sql, Statement::CreateIndex(expected));
101+
}
102+
103+
#[test]
104+
fn test_parse_create_index_statement_with_multiple_columns() {
105+
let sql = "CREATE INDEX index_name ON table_name (column1, column2, column3, column4)";
106+
let mut expected = create_index_statement();
107+
expected.columns = vec![
108+
IndexedColumn {
109+
column: identifier_expression(&["column1"]),
110+
ordering: None,
111+
},
112+
IndexedColumn {
113+
column: identifier_expression(&["column2"]),
114+
ordering: None,
115+
},
116+
IndexedColumn {
117+
column: identifier_expression(&["column3"]),
118+
ordering: None,
119+
},
120+
IndexedColumn {
121+
column: identifier_expression(&["column4"]),
122+
ordering: None,
123+
},
124+
];
125+
run_sunny_day_test(sql, Statement::CreateIndex(expected));
126+
}
127+
128+
#[test]
129+
fn test_parse_create_index_statement_with_if_not_exists() {
130+
let sql = "CREATE INDEX IF NOT EXISTS index_name ON table_name (column1)";
131+
let mut expected = create_index_statement();
132+
expected.if_not_exists = true;
133+
run_sunny_day_test(sql, Statement::CreateIndex(expected));
134+
}
135+
136+
#[test]
137+
fn test_parse_create_index_statement_with_where_clause() {
138+
let sql = "CREATE INDEX index_name ON table_name (column1) WHERE column1 = 1";
139+
let mut expected = create_index_statement();
140+
expected.where_clause = Some(Box::new(binary_op_expression(
141+
BinaryOp::Equals,
142+
identifier_expression(&["column1"]),
143+
numeric_literal_expression("1"),
144+
)));
145+
run_sunny_day_test(sql, Statement::CreateIndex(expected));
146+
}
147+
}

src/parser/create/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod create_trigger;
44
mod create_view;
55
mod create_virtual_table;
66

7+
use create_index::CreateIndexStatementParser;
78
pub use create_virtual_table::*;
89

910
use crate::{Keyword, Statement};
@@ -25,6 +26,13 @@ impl<'a> CreateStatementParser for Parser<'a> {
2526
CreateVirtualTableStatementParser::parse_create_virtual_table_statement(self)
2627
.map(Statement::CreateVirtualTable)
2728
}
29+
Keyword::Unique => {
30+
self.consume_as_keyword(Keyword::Unique)?;
31+
CreateIndexStatementParser::parse_create_index_statement(self, true)
32+
.map(Statement::CreateIndex)
33+
}
34+
Keyword::Index => CreateIndexStatementParser::parse_create_index_statement(self, false)
35+
.map(Statement::CreateIndex),
2836
_ => Err(ParsingError::UnexpectedParsingState(
2937
"Unimplemented".to_string(),
3038
)),

src/parser/insert/mod.rs

+18-11
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub trait InsertStatementParser {
3030

3131
fn parse_upsert_action(&mut self) -> Result<UpsertAction, ParsingError>;
3232

33+
fn parse_indexed_columns(&mut self) -> Result<Vec<IndexedColumn>, ParsingError>;
34+
3335
fn parse_indexed_column(&mut self) -> Result<IndexedColumn, ParsingError>;
3436

3537
fn parse_ordering(&mut self) -> Result<Option<Ordering>, ParsingError>;
@@ -155,18 +157,9 @@ impl<'a> InsertStatementParser for Parser<'a> {
155157
fn parse_upsert_conflict_target(
156158
&mut self,
157159
) -> Result<Option<UpsertConflictTarget>, ParsingError> {
158-
if self.consume_as(TokenType::LeftParen).is_ok() {
159-
let mut columns = vec![];
160-
loop {
161-
columns.push(self.parse_indexed_column()?);
162-
if self.consume_as(TokenType::Comma).is_err() {
163-
break;
164-
}
165-
}
166-
self.consume_as(TokenType::RightParen)?;
167-
160+
if self.peek_as(TokenType::LeftParen).is_ok() {
161+
let columns = self.parse_indexed_columns()?;
168162
let where_clause = self.parse_where_clause()?;
169-
170163
Ok(Some(UpsertConflictTarget {
171164
columns,
172165
where_clause,
@@ -199,6 +192,20 @@ impl<'a> InsertStatementParser for Parser<'a> {
199192
))
200193
}
201194

195+
fn parse_indexed_columns(&mut self) -> Result<Vec<IndexedColumn>, ParsingError> {
196+
self.consume_as(TokenType::LeftParen)?;
197+
198+
let mut columns = vec![];
199+
loop {
200+
columns.push(self.parse_indexed_column()?);
201+
if self.consume_as(TokenType::Comma).is_err() {
202+
break;
203+
}
204+
}
205+
self.consume_as(TokenType::RightParen)?;
206+
Ok(columns)
207+
}
208+
202209
fn parse_indexed_column(&mut self) -> Result<IndexedColumn, ParsingError> {
203210
let column = self.parse_expression()?;
204211
let ordering = self.parse_ordering()?;

0 commit comments

Comments
 (0)