vibesql_parser/parser/advanced_objects/
routines.rs

1//! Stored procedures and functions parsing (SQL:1999 Feature P001)
2//!
3//! This module parses:
4//! - CREATE PROCEDURE statements
5//! - CREATE FUNCTION statements
6//! - CALL statements
7//! - DROP PROCEDURE statements
8//! - DROP FUNCTION statements
9//!
10//! Syntax (MySQL-compatible):
11//!
12//! CREATE PROCEDURE procedure_name ([param_list])
13//! BEGIN
14//!   -- statements
15//! END;
16//!
17//! CREATE FUNCTION function_name ([param_list])
18//! RETURNS data_type
19//! BEGIN
20//!   -- statements
21//!   RETURN value;
22//! END;
23//!
24//! CALL procedure_name([args]);
25//!
26//! DROP PROCEDURE [IF EXISTS] procedure_name;
27//! DROP FUNCTION [IF EXISTS] function_name;
28
29use crate::keywords::Keyword;
30use crate::parser::{ParseError, Parser};
31use crate::token::Token;
32use vibesql_ast::{
33    CallStmt, CreateFunctionStmt, CreateProcedureStmt, DropFunctionStmt, DropProcedureStmt,
34    FunctionParameter, ParameterMode, ProceduralStatement, ProcedureBody, ProcedureParameter,
35    SqlSecurity,
36};
37
38impl Parser {
39    /// Parse CREATE PROCEDURE statement
40    ///
41    /// Syntax: CREATE PROCEDURE proc_name ([param_list]) [characteristics] BEGIN ... END;
42    pub fn parse_create_procedure(&mut self) -> Result<CreateProcedureStmt, ParseError> {
43        // Already consumed CREATE PROCEDURE
44        let procedure_name = self.parse_identifier()?;
45
46        self.expect_token(Token::LParen)?;
47        let parameters = self.parse_procedure_parameters()?;
48        self.expect_token(Token::RParen)?;
49
50        // Parse optional characteristics before body
51        let (sql_security, comment, language) = self.parse_procedure_characteristics()?;
52
53        let body = self.parse_procedure_body()?;
54
55        Ok(CreateProcedureStmt {
56            procedure_name,
57            parameters,
58            body,
59            sql_security,
60            comment,
61            language,
62        })
63    }
64
65    /// Parse CREATE FUNCTION statement
66    ///
67    /// Syntax: CREATE FUNCTION func_name ([param_list]) RETURNS data_type [characteristics] BEGIN ... END;
68    pub fn parse_create_function(&mut self) -> Result<CreateFunctionStmt, ParseError> {
69        // Already consumed CREATE FUNCTION
70        let function_name = self.parse_identifier()?;
71
72        self.expect_token(Token::LParen)?;
73        let parameters = self.parse_function_parameters()?;
74        self.expect_token(Token::RParen)?;
75
76        self.expect_keyword(Keyword::Returns)?;
77        let return_type = self.parse_data_type()?;
78
79        // Parse optional characteristics before body
80        let (deterministic, sql_security, comment, language) =
81            self.parse_function_characteristics()?;
82
83        let body = self.parse_procedure_body()?;
84
85        Ok(CreateFunctionStmt {
86            function_name,
87            parameters,
88            return_type,
89            body,
90            deterministic,
91            sql_security,
92            comment,
93            language,
94        })
95    }
96
97    /// Parse DROP PROCEDURE statement
98    ///
99    /// Syntax: DROP PROCEDURE [IF EXISTS] proc_name;
100    pub fn parse_drop_procedure(&mut self) -> Result<DropProcedureStmt, ParseError> {
101        // Already consumed DROP PROCEDURE
102        let if_exists = self.try_consume_keyword(Keyword::If);
103        if if_exists {
104            self.expect_keyword(Keyword::Exists)?;
105        }
106
107        let procedure_name = self.parse_identifier()?;
108
109        Ok(DropProcedureStmt { procedure_name, if_exists })
110    }
111
112    /// Parse DROP FUNCTION statement
113    ///
114    /// Syntax: DROP FUNCTION [IF EXISTS] func_name;
115    pub fn parse_drop_function(&mut self) -> Result<DropFunctionStmt, ParseError> {
116        // Already consumed DROP FUNCTION
117        let if_exists = self.try_consume_keyword(Keyword::If);
118        if if_exists {
119            self.expect_keyword(Keyword::Exists)?;
120        }
121
122        let function_name = self.parse_identifier()?;
123
124        Ok(DropFunctionStmt { function_name, if_exists })
125    }
126
127    /// Parse CALL statement
128    ///
129    /// Syntax: CALL procedure_name([args]);
130    pub fn parse_call(&mut self) -> Result<CallStmt, ParseError> {
131        // Already consumed CALL
132        let procedure_name = self.parse_identifier()?;
133
134        self.expect_token(Token::LParen)?;
135        let arguments = self.parse_expression_list()?;
136        self.expect_token(Token::RParen)?;
137
138        Ok(CallStmt { procedure_name, arguments })
139    }
140
141    /// Parse procedure parameters: [param_mode] name data_type [, ...]
142    ///
143    /// Example: IN user_id INT, OUT result VARCHAR(100), INOUT status CHAR
144    fn parse_procedure_parameters(&mut self) -> Result<Vec<ProcedureParameter>, ParseError> {
145        let mut parameters = Vec::new();
146
147        // Empty parameter list
148        if self.peek() == &Token::RParen {
149            return Ok(parameters);
150        }
151
152        loop {
153            // Parse parameter mode (IN, OUT, INOUT)
154            let mode = if self.try_consume_keyword(Keyword::In) {
155                ParameterMode::In
156            } else if self.try_consume_keyword(Keyword::Out) {
157                ParameterMode::Out
158            } else if self.try_consume_keyword(Keyword::InOut) {
159                ParameterMode::InOut
160            } else {
161                // Default to IN if not specified
162                ParameterMode::In
163            };
164
165            let name = self.parse_identifier()?;
166            let data_type = self.parse_data_type()?;
167
168            parameters.push(ProcedureParameter { mode, name, data_type });
169
170            if !self.try_consume(&Token::Comma) {
171                break;
172            }
173        }
174
175        Ok(parameters)
176    }
177
178    /// Parse function parameters: name data_type [, ...]
179    ///
180    /// Functions typically only have IN parameters (no mode specification)
181    /// Example: user_id INT, price DECIMAL(10,2)
182    fn parse_function_parameters(&mut self) -> Result<Vec<FunctionParameter>, ParseError> {
183        let mut parameters = Vec::new();
184
185        // Empty parameter list
186        if self.peek() == &Token::RParen {
187            return Ok(parameters);
188        }
189
190        loop {
191            let name = self.parse_identifier()?;
192            let data_type = self.parse_data_type()?;
193
194            parameters.push(FunctionParameter { name, data_type });
195
196            if !self.try_consume(&Token::Comma) {
197                break;
198            }
199        }
200
201        Ok(parameters)
202    }
203
204    /// Parse procedure body
205    ///
206    /// For now, we support two formats:
207    /// 1. BEGIN ... END; (procedural block)
208    /// 2. RawSql for simple cases
209    fn parse_procedure_body(&mut self) -> Result<ProcedureBody, ParseError> {
210        if self.try_consume_keyword(Keyword::Begin) {
211            // Parse BEGIN ... END block
212            let statements = self.parse_procedural_statements()?;
213            self.expect_keyword(Keyword::End)?;
214            Ok(ProcedureBody::BeginEnd(statements))
215        } else {
216            // For now, error - require BEGIN/END block
217            Err(ParseError { message: "Expected BEGIN keyword for procedure body".to_string() })
218        }
219    }
220
221    /// Parse procedural statements (statements within BEGIN/END block)
222    ///
223    /// Supported statements:
224    /// - SQL statements (SELECT, INSERT, UPDATE, DELETE, etc.)
225    /// - DECLARE variable declarations
226    /// - SET variable assignments
227    /// - IF/ELSE conditional
228    /// - WHILE loops
229    /// - LOOP statements
230    /// - REPEAT/UNTIL loops
231    /// - RETURN (for functions)
232    /// - LEAVE/ITERATE (for loop control)
233    fn parse_procedural_statements(&mut self) -> Result<Vec<ProceduralStatement>, ParseError> {
234        let mut statements = Vec::new();
235
236        while !self.peek_keyword(Keyword::End) && self.peek() != &Token::Eof {
237            let stmt = if self.try_consume_keyword(Keyword::Declare) {
238                self.parse_declare_statement()?
239            } else if self.try_consume_keyword(Keyword::Set) {
240                self.parse_set_statement()?
241            } else if self.try_consume_keyword(Keyword::If) {
242                self.parse_if_statement()?
243            } else if self.try_consume_keyword(Keyword::While) {
244                self.parse_while_statement()?
245            } else if self.try_consume_keyword(Keyword::Loop) {
246                self.parse_loop_statement()?
247            } else if self.try_consume_keyword(Keyword::Repeat) {
248                self.parse_repeat_statement()?
249            } else if self.try_consume_keyword(Keyword::Return) {
250                let expr = self.parse_expression()?;
251                self.expect_token(Token::Semicolon)?;
252                ProceduralStatement::Return(Box::new(expr))
253            } else if self.try_consume_keyword(Keyword::Leave) {
254                let label = self.parse_identifier()?;
255                self.expect_token(Token::Semicolon)?;
256                ProceduralStatement::Leave(label)
257            } else if self.try_consume_keyword(Keyword::Iterate) {
258                let label = self.parse_identifier()?;
259                self.expect_token(Token::Semicolon)?;
260                ProceduralStatement::Iterate(label)
261            } else {
262                // Try to parse as a SQL statement
263                let sql_stmt = self.parse_statement()?;
264                ProceduralStatement::Sql(Box::new(sql_stmt))
265            };
266
267            statements.push(stmt);
268        }
269
270        Ok(statements)
271    }
272
273    /// Parse DECLARE statement
274    ///
275    /// Syntax: DECLARE var_name data_type [DEFAULT expr];
276    fn parse_declare_statement(&mut self) -> Result<ProceduralStatement, ParseError> {
277        let name = self.parse_identifier()?;
278        let data_type = self.parse_data_type()?;
279
280        let default_value = if self.try_consume_keyword(Keyword::Default) {
281            Some(Box::new(self.parse_expression()?))
282        } else {
283            None
284        };
285
286        self.expect_token(Token::Semicolon)?;
287
288        Ok(ProceduralStatement::Declare { name, data_type, default_value })
289    }
290
291    /// Parse SET statement
292    ///
293    /// Syntax: SET var_name = expr;
294    fn parse_set_statement(&mut self) -> Result<ProceduralStatement, ParseError> {
295        let name = self.parse_identifier()?;
296        self.expect_token(Token::Symbol('='))?;
297        let value = self.parse_expression()?;
298        self.expect_token(Token::Semicolon)?;
299
300        Ok(ProceduralStatement::Set { name, value: Box::new(value) })
301    }
302
303    /// Parse IF statement
304    ///
305    /// Syntax: IF condition THEN ... [ELSE ...] END IF;
306    fn parse_if_statement(&mut self) -> Result<ProceduralStatement, ParseError> {
307        let condition = self.parse_expression()?;
308        self.expect_keyword(Keyword::Then)?;
309
310        let then_statements =
311            self.parse_procedural_statements_until(&[Keyword::Else, Keyword::End])?;
312
313        let else_statements = if self.try_consume_keyword(Keyword::Else) {
314            Some(self.parse_procedural_statements_until(&[Keyword::End])?)
315        } else {
316            None
317        };
318
319        self.expect_keyword(Keyword::End)?;
320        self.expect_keyword(Keyword::If)?;
321        self.expect_token(Token::Semicolon)?;
322
323        Ok(ProceduralStatement::If {
324            condition: Box::new(condition),
325            then_statements,
326            else_statements,
327        })
328    }
329
330    /// Parse WHILE statement
331    ///
332    /// Syntax: WHILE condition DO ... END WHILE;
333    fn parse_while_statement(&mut self) -> Result<ProceduralStatement, ParseError> {
334        let condition = self.parse_expression()?;
335        self.expect_keyword(Keyword::Do)?;
336
337        let statements = self.parse_procedural_statements_until(&[Keyword::End])?;
338
339        self.expect_keyword(Keyword::End)?;
340        self.expect_keyword(Keyword::While)?;
341        self.expect_token(Token::Semicolon)?;
342
343        Ok(ProceduralStatement::While { condition: Box::new(condition), statements })
344    }
345
346    /// Parse LOOP statement
347    ///
348    /// Syntax: LOOP ... END LOOP;
349    fn parse_loop_statement(&mut self) -> Result<ProceduralStatement, ParseError> {
350        let statements = self.parse_procedural_statements_until(&[Keyword::End])?;
351
352        self.expect_keyword(Keyword::Loop)?;
353        self.expect_token(Token::Semicolon)?;
354
355        Ok(ProceduralStatement::Loop { statements })
356    }
357
358    /// Parse REPEAT statement
359    ///
360    /// Syntax: REPEAT ... UNTIL condition END REPEAT;
361    fn parse_repeat_statement(&mut self) -> Result<ProceduralStatement, ParseError> {
362        let statements = self.parse_procedural_statements_until(&[Keyword::Until])?;
363
364        self.expect_keyword(Keyword::Until)?;
365        let condition = self.parse_expression()?;
366        self.expect_keyword(Keyword::End)?;
367        self.expect_keyword(Keyword::Repeat)?;
368        self.expect_token(Token::Semicolon)?;
369
370        Ok(ProceduralStatement::Repeat { statements, condition: Box::new(condition) })
371    }
372
373    /// Parse procedural statements until one of the keywords is encountered
374    ///
375    /// This helper stops parsing when it encounters any of the given keywords,
376    /// without consuming them.
377    fn parse_procedural_statements_until(
378        &mut self,
379        stop_keywords: &[Keyword],
380    ) -> Result<Vec<ProceduralStatement>, ParseError> {
381        let mut statements = Vec::new();
382
383        while self.peek() != &Token::Eof {
384            // Check if we've hit a stop keyword
385            if stop_keywords.iter().any(|kw| self.peek_keyword(*kw)) {
386                break;
387            }
388
389            let stmt = if self.try_consume_keyword(Keyword::Declare) {
390                self.parse_declare_statement()?
391            } else if self.try_consume_keyword(Keyword::Set) {
392                self.parse_set_statement()?
393            } else if self.try_consume_keyword(Keyword::If) {
394                self.parse_if_statement()?
395            } else if self.try_consume_keyword(Keyword::While) {
396                self.parse_while_statement()?
397            } else if self.try_consume_keyword(Keyword::Loop) {
398                self.parse_loop_statement()?
399            } else if self.try_consume_keyword(Keyword::Repeat) {
400                self.parse_repeat_statement()?
401            } else if self.try_consume_keyword(Keyword::Return) {
402                let expr = self.parse_expression()?;
403                self.expect_token(Token::Semicolon)?;
404                ProceduralStatement::Return(Box::new(expr))
405            } else if self.try_consume_keyword(Keyword::Leave) {
406                let label = self.parse_identifier()?;
407                self.expect_token(Token::Semicolon)?;
408                ProceduralStatement::Leave(label)
409            } else if self.try_consume_keyword(Keyword::Iterate) {
410                let label = self.parse_identifier()?;
411                self.expect_token(Token::Semicolon)?;
412                ProceduralStatement::Iterate(label)
413            } else {
414                // Try to parse as a SQL statement
415                let sql_stmt = self.parse_statement()?;
416                ProceduralStatement::Sql(Box::new(sql_stmt))
417            };
418
419            statements.push(stmt);
420        }
421
422        Ok(statements)
423    }
424
425    /// Parse procedure characteristics (optional)
426    ///
427    /// Returns: (sql_security, comment, language)
428    #[allow(clippy::type_complexity)]
429    fn parse_procedure_characteristics(
430        &mut self,
431    ) -> Result<(Option<SqlSecurity>, Option<String>, Option<String>), ParseError> {
432        let mut sql_security = None;
433        let mut comment = None;
434        let mut language = None;
435
436        // Parse characteristics in any order
437        loop {
438            if self.try_consume_keyword(Keyword::Sql) {
439                self.expect_keyword(Keyword::Security)?;
440                if self.try_consume_keyword(Keyword::Definer) {
441                    sql_security = Some(SqlSecurity::Definer);
442                } else if self.try_consume_keyword(Keyword::Invoker) {
443                    sql_security = Some(SqlSecurity::Invoker);
444                } else {
445                    return Err(ParseError {
446                        message: "Expected DEFINER or INVOKER after SQL SECURITY".to_string(),
447                    });
448                }
449            } else if self.try_consume_keyword(Keyword::Comment) {
450                if let Token::String(s) = self.peek().clone() {
451                    self.advance();
452                    comment = Some(s);
453                } else {
454                    return Err(ParseError {
455                        message: "Expected string literal after COMMENT".to_string(),
456                    });
457                }
458            } else if self.try_consume_keyword(Keyword::Language) {
459                self.expect_keyword(Keyword::Sql)?;
460                language = Some("SQL".to_string());
461            } else {
462                // No more characteristics
463                break;
464            }
465        }
466
467        Ok((sql_security, comment, language))
468    }
469
470    /// Parse function characteristics (optional)
471    ///
472    /// Returns: (deterministic, sql_security, comment, language)
473    #[allow(clippy::type_complexity)]
474    fn parse_function_characteristics(
475        &mut self,
476    ) -> Result<(Option<bool>, Option<SqlSecurity>, Option<String>, Option<String>), ParseError>
477    {
478        let mut deterministic = None;
479        let mut sql_security = None;
480        let mut comment = None;
481        let mut language = None;
482
483        // Parse characteristics in any order
484        loop {
485            if self.try_consume_keyword(Keyword::Deterministic) {
486                deterministic = Some(true);
487            } else if self.try_consume_keyword(Keyword::Not) {
488                self.expect_keyword(Keyword::Deterministic)?;
489                deterministic = Some(false);
490            } else if self.try_consume_keyword(Keyword::Sql) {
491                self.expect_keyword(Keyword::Security)?;
492                if self.try_consume_keyword(Keyword::Definer) {
493                    sql_security = Some(SqlSecurity::Definer);
494                } else if self.try_consume_keyword(Keyword::Invoker) {
495                    sql_security = Some(SqlSecurity::Invoker);
496                } else {
497                    return Err(ParseError {
498                        message: "Expected DEFINER or INVOKER after SQL SECURITY".to_string(),
499                    });
500                }
501            } else if self.try_consume_keyword(Keyword::Comment) {
502                if let Token::String(s) = self.peek().clone() {
503                    self.advance();
504                    comment = Some(s);
505                } else {
506                    return Err(ParseError {
507                        message: "Expected string literal after COMMENT".to_string(),
508                    });
509                }
510            } else if self.try_consume_keyword(Keyword::Language) {
511                self.expect_keyword(Keyword::Sql)?;
512                language = Some("SQL".to_string());
513            } else {
514                // No more characteristics
515                break;
516            }
517        }
518
519        Ok((deterministic, sql_security, comment, language))
520    }
521}