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