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