1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! DDL Parser for CREATE INDEX and DROP INDEX
use super::super::ast::{CreateIndexQuery, DropIndexQuery, IndexMethod, QueryExpr};
use super::super::lexer::Token;
use super::error::ParseError;
use super::Parser;
impl<'a> Parser<'a> {
/// Parse: CREATE [UNIQUE] INDEX [IF NOT EXISTS] name ON table (col1, ...) [USING method]
///
/// Called after `Token::Create` has been consumed and we've peeked `Token::Index`
/// or `Token::Unique`.
pub fn parse_create_index_query(&mut self) -> Result<QueryExpr, ParseError> {
// CREATE has already been consumed by the dispatcher
let unique = self.consume(&Token::Unique)?;
self.expect(Token::Index)?;
let if_not_exists = self.match_if_not_exists()?;
let name = self.expect_ident()?;
self.expect(Token::On)?;
let table = self.expect_ident()?;
// Parse column list: (col1, col2, ...)
self.expect(Token::LParen)?;
let mut columns = Vec::new();
loop {
columns.push(self.expect_ident_or_keyword()?);
if !self.consume(&Token::Comma)? {
break;
}
}
self.expect(Token::RParen)?;
// Parse optional USING method
let method = if self.consume(&Token::Using)? {
self.parse_index_method()?
} else {
IndexMethod::BTree // default
};
Ok(QueryExpr::CreateIndex(CreateIndexQuery {
name,
table,
columns,
method,
unique,
if_not_exists,
}))
}
/// Parse: DROP INDEX [IF EXISTS] name ON table
///
/// Called after `Token::Drop` has been consumed and we've peeked `Token::Index`.
pub fn parse_drop_index_query(&mut self) -> Result<QueryExpr, ParseError> {
// DROP has already been consumed by the dispatcher
self.expect(Token::Index)?;
let if_exists = self.match_if_exists()?;
let name = self.expect_ident()?;
self.expect(Token::On)?;
let table = self.expect_ident()?;
Ok(QueryExpr::DropIndex(DropIndexQuery {
name,
table,
if_exists,
}))
}
/// Parse index method identifier: HASH | BTREE | BITMAP | RTREE.
/// `HASH` is also a reserved keyword token, so we match both the
/// keyword form and the ident form — otherwise `USING HASH`
/// fails with "Unexpected token: HASH" even though the parser
/// lists HASH as an expected option.
fn parse_index_method(&mut self) -> Result<IndexMethod, ParseError> {
let peeked = self.peek().clone();
if matches!(peeked, Token::Hash) {
self.advance()?;
return Ok(IndexMethod::Hash);
}
match peeked {
Token::Ident(ref name) => {
let method = match name.to_ascii_uppercase().as_str() {
"HASH" => IndexMethod::Hash,
"BTREE" => IndexMethod::BTree,
"BITMAP" => IndexMethod::Bitmap,
"RTREE" => IndexMethod::RTree,
_ => {
return Err(ParseError::new(
format!(
"unknown index method '{}', expected HASH, BTREE, BITMAP, or RTREE",
name
),
self.position(),
));
}
};
self.advance()?;
Ok(method)
}
other => Err(ParseError::expected(
vec!["HASH", "BTREE", "BITMAP", "RTREE"],
&other,
self.position(),
)),
}
}
}