vibesql_parser/parser/
introspection.rs

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