Skip to main content

reddb_server/storage/query/parser/
index_ddl.rs

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