vibesql_parser/parser/
introspection.rs

1//! Database introspection statement parsing (SHOW, DESCRIBE)
2
3use super::{ParseError, Parser};
4use crate::keywords::Keyword;
5use crate::token::Token;
6
7impl Parser {
8    /// Helper to parse a string literal
9    fn parse_string(&mut self) -> Result<String, ParseError> {
10        match self.peek() {
11            Token::String(s) => {
12                let string_val = s.clone();
13                self.advance();
14                Ok(string_val)
15            }
16            _ => Err(ParseError {
17                message: format!("Expected string literal, found {:?}", self.peek()),
18            }),
19        }
20    }
21
22    /// Parse SHOW statement
23    pub fn parse_show_statement(&mut self) -> Result<vibesql_ast::Statement, ParseError> {
24        self.expect_keyword(Keyword::Show)?;
25
26        // Determine which SHOW variant
27        match self.peek() {
28            Token::Keyword(Keyword::Tables) => {
29                Ok(vibesql_ast::Statement::ShowTables(self.parse_show_tables()?))
30            }
31            Token::Keyword(Keyword::Databases) => {
32                Ok(vibesql_ast::Statement::ShowDatabases(self.parse_show_databases()?))
33            }
34            Token::Keyword(Keyword::Full) => {
35                // SHOW FULL COLUMNS - handle FULL modifier for SHOW COLUMNS
36                Ok(vibesql_ast::Statement::ShowColumns(self.parse_show_columns()?))
37            }
38            Token::Keyword(Keyword::Columns) | Token::Keyword(Keyword::Fields) => {
39                Ok(vibesql_ast::Statement::ShowColumns(self.parse_show_columns()?))
40            }
41            Token::Keyword(Keyword::Index) | Token::Keyword(Keyword::Indexes) | Token::Keyword(Keyword::Keys) => {
42                Ok(vibesql_ast::Statement::ShowIndex(self.parse_show_index()?))
43            }
44            Token::Keyword(Keyword::Create) => {
45                self.advance(); // consume CREATE
46                if self.peek_keyword(Keyword::Table) {
47                    Ok(vibesql_ast::Statement::ShowCreateTable(self.parse_show_create_table()?))
48                } else {
49                    Err(ParseError {
50                        message: "Expected TABLE after SHOW CREATE".to_string(),
51                    })
52                }
53            }
54            _ => Err(ParseError {
55                message: format!(
56                    "Expected TABLES, DATABASES, COLUMNS, INDEX, or CREATE after SHOW, found {:?}",
57                    self.peek()
58                ),
59            }),
60        }
61    }
62
63    /// Parse SHOW TABLES [FROM database] [LIKE pattern | WHERE expr]
64    fn parse_show_tables(&mut self) -> Result<vibesql_ast::ShowTablesStmt, ParseError> {
65        self.expect_keyword(Keyword::Tables)?;
66
67        // Optional FROM database
68        let database = if self.peek_keyword(Keyword::From) {
69            self.advance();
70            Some(self.parse_identifier()?)
71        } else {
72            None
73        };
74
75        // Optional LIKE pattern
76        let like_pattern = if self.peek_keyword(Keyword::Like) {
77            self.advance();
78            Some(self.parse_string()?)
79        } else {
80            None
81        };
82
83        // Optional WHERE clause
84        let where_clause = if self.peek_keyword(Keyword::Where) {
85            self.advance();
86            Some(self.parse_expression()?)
87        } else {
88            None
89        };
90
91        Ok(vibesql_ast::ShowTablesStmt { database, like_pattern, where_clause })
92    }
93
94    /// Parse SHOW DATABASES [LIKE pattern | WHERE expr]
95    fn parse_show_databases(&mut self) -> Result<vibesql_ast::ShowDatabasesStmt, ParseError> {
96        self.expect_keyword(Keyword::Databases)?;
97
98        // Optional LIKE pattern
99        let like_pattern = if self.peek_keyword(Keyword::Like) {
100            self.advance();
101            Some(self.parse_string()?)
102        } else {
103            None
104        };
105
106        // Optional WHERE clause
107        let where_clause = if self.peek_keyword(Keyword::Where) {
108            self.advance();
109            Some(self.parse_expression()?)
110        } else {
111            None
112        };
113
114        Ok(vibesql_ast::ShowDatabasesStmt { like_pattern, where_clause })
115    }
116
117    /// Parse SHOW [FULL] COLUMNS FROM table [FROM database] [LIKE pattern | WHERE expr]
118    fn parse_show_columns(&mut self) -> Result<vibesql_ast::ShowColumnsStmt, ParseError> {
119        // Check for FULL modifier
120        let full = if self.peek_keyword(Keyword::Full) {
121            self.advance();
122            true
123        } else {
124            false
125        };
126
127        // COLUMNS or FIELDS (synonyms)
128        if !self.peek_keyword(Keyword::Columns) && !self.peek_keyword(Keyword::Fields) {
129            return Err(ParseError {
130                message: "Expected COLUMNS or FIELDS".to_string(),
131            });
132        }
133        self.advance();
134
135        // FROM table_name
136        self.expect_keyword(Keyword::From)?;
137        let table_name = self.parse_identifier()?;
138
139        // Optional FROM database (second FROM)
140        let database = if self.peek_keyword(Keyword::From) {
141            self.advance();
142            Some(self.parse_identifier()?)
143        } else {
144            None
145        };
146
147        // Optional LIKE pattern
148        let like_pattern = if self.peek_keyword(Keyword::Like) {
149            self.advance();
150            Some(self.parse_string()?)
151        } else {
152            None
153        };
154
155        // Optional WHERE clause
156        let where_clause = if self.peek_keyword(Keyword::Where) {
157            self.advance();
158            Some(self.parse_expression()?)
159        } else {
160            None
161        };
162
163        Ok(vibesql_ast::ShowColumnsStmt {
164            table_name,
165            database,
166            full,
167            like_pattern,
168            where_clause,
169        })
170    }
171
172    /// Parse SHOW INDEX FROM table [FROM database]
173    fn parse_show_index(&mut self) -> Result<vibesql_ast::ShowIndexStmt, ParseError> {
174        // INDEX, INDEXES, or KEYS (synonyms)
175        self.advance(); // consume the keyword
176
177        // FROM table_name
178        self.expect_keyword(Keyword::From)?;
179        let table_name = self.parse_identifier()?;
180
181        // Optional FROM database
182        let database = if self.peek_keyword(Keyword::From) {
183            self.advance();
184            Some(self.parse_identifier()?)
185        } else {
186            None
187        };
188
189        Ok(vibesql_ast::ShowIndexStmt { table_name, database })
190    }
191
192    /// Parse SHOW CREATE TABLE table_name
193    fn parse_show_create_table(&mut self) -> Result<vibesql_ast::ShowCreateTableStmt, ParseError> {
194        self.expect_keyword(Keyword::Table)?;
195        let table_name = self.parse_identifier()?;
196
197        Ok(vibesql_ast::ShowCreateTableStmt { table_name })
198    }
199
200    /// Parse DESCRIBE table [column_pattern]
201    pub fn parse_describe_statement(&mut self) -> Result<vibesql_ast::DescribeStmt, ParseError> {
202        self.expect_keyword(Keyword::Describe)?;
203
204        let table_name = self.parse_identifier()?;
205
206        // Optional column name or pattern
207        let column_pattern = if matches!(self.peek(), Token::Identifier(_) | Token::String(_)) {
208            Some(match self.peek() {
209                Token::String(_) => self.parse_string()?,
210                _ => self.parse_identifier()?,
211            })
212        } else {
213            None
214        };
215
216        Ok(vibesql_ast::DescribeStmt { table_name, column_pattern })
217    }
218
219    /// Parse EXPLAIN [ANALYZE] statement
220    ///
221    /// Syntax: EXPLAIN [ANALYZE] [FORMAT {TEXT | JSON}] statement
222    pub fn parse_explain_statement(&mut self) -> Result<vibesql_ast::ExplainStmt, ParseError> {
223        self.expect_keyword(Keyword::Explain)?;
224
225        // Check for ANALYZE option
226        let analyze = if matches!(self.peek(), Token::Keyword(Keyword::Analyze)) {
227            self.advance();
228            true
229        } else {
230            false
231        };
232
233        // Check for FORMAT option (optional)
234        let format = if matches!(self.peek(), Token::Identifier(ref s) if s.to_uppercase() == "FORMAT") {
235            self.advance(); // consume FORMAT
236            match self.peek() {
237                Token::Identifier(ref s) if s.to_uppercase() == "TEXT" => {
238                    self.advance();
239                    vibesql_ast::ExplainFormat::Text
240                }
241                Token::Identifier(ref s) if s.to_uppercase() == "JSON" => {
242                    self.advance();
243                    vibesql_ast::ExplainFormat::Json
244                }
245                _ => {
246                    return Err(ParseError {
247                        message: format!("Expected TEXT or JSON after FORMAT, found {:?}", self.peek()),
248                    });
249                }
250            }
251        } else {
252            vibesql_ast::ExplainFormat::Text
253        };
254
255        // Parse the inner statement (SELECT, INSERT, UPDATE, DELETE)
256        let statement = match self.peek() {
257            Token::Keyword(Keyword::Select) | Token::Keyword(Keyword::With) => {
258                let select_stmt = self.parse_select_statement()?;
259                vibesql_ast::Statement::Select(Box::new(select_stmt))
260            }
261            Token::Keyword(Keyword::Insert) => {
262                let insert_stmt = self.parse_insert_statement()?;
263                vibesql_ast::Statement::Insert(insert_stmt)
264            }
265            Token::Keyword(Keyword::Update) => {
266                let update_stmt = self.parse_update_statement()?;
267                vibesql_ast::Statement::Update(update_stmt)
268            }
269            Token::Keyword(Keyword::Delete) => {
270                let delete_stmt = self.parse_delete_statement()?;
271                vibesql_ast::Statement::Delete(delete_stmt)
272            }
273            _ => {
274                return Err(ParseError {
275                    message: format!(
276                        "EXPLAIN requires SELECT, INSERT, UPDATE, or DELETE statement, found {:?}",
277                        self.peek()
278                    ),
279                });
280            }
281        };
282
283        Ok(vibesql_ast::ExplainStmt { statement: Box::new(statement), format, analyze })
284    }
285}