vibesql_parser/arena_parser/
mod.rs

1//! Arena-allocated SQL parser.
2//!
3//! This module provides a parser that allocates AST nodes from a bump arena
4//! for improved performance. All allocations are contiguous in memory and
5//! freed in a single operation when the arena is dropped.
6//!
7//! # Usage
8//!
9//! For arena-allocated AST (fastest, but requires arena lifetime management):
10//! ```ignore
11//! use bumpalo::Bump;
12//! use vibesql_parser::arena_parser::ArenaParser;
13//!
14//! let arena = Bump::new();
15//! let result = ArenaParser::parse_sql("SELECT * FROM users", &arena);
16//! ```
17//!
18//! For standard heap-allocated AST (convenient, with arena parsing benefits):
19//! ```ignore
20//! use vibesql_parser::arena_parser::parse_select_to_owned;
21//!
22//! // Parse with arena internally, convert to owned SelectStmt
23//! let stmt = parse_select_to_owned("SELECT * FROM users")?;
24//! ```
25
26mod ddl;
27mod delete;
28mod expression;
29mod insert;
30mod select;
31mod update;
32
33use bumpalo::Bump;
34use vibesql_ast::arena::{
35    AlterTableStmt, ArenaInterner, Converter, DeleteStmt, Expression, InsertStmt, SelectStmt,
36    Statement, Symbol, UpdateStmt,
37};
38
39use crate::keywords::Keyword;
40use crate::{Lexer, ParseError, Token};
41
42/// Arena-based SQL parser.
43///
44/// Unlike the standard [`Parser`](crate::Parser), this parser allocates all
45/// AST nodes from a bump arena, resulting in:
46/// - O(1) allocation time (vs heap allocation overhead)
47/// - Better cache locality (contiguous memory)
48/// - Single deallocation when arena is dropped
49pub struct ArenaParser<'arena> {
50    tokens: Vec<Token>,
51    position: usize,
52    placeholder_count: usize,
53    arena: &'arena Bump,
54    interner: ArenaInterner<'arena>,
55}
56
57impl<'arena> ArenaParser<'arena> {
58    /// Create a new arena parser from tokens.
59    pub fn new(tokens: Vec<Token>, arena: &'arena Bump) -> Self {
60        ArenaParser {
61            tokens,
62            position: 0,
63            placeholder_count: 0,
64            arena,
65            interner: ArenaInterner::new(arena),
66        }
67    }
68
69    /// Returns a reference to the interner for symbol resolution during conversion.
70    pub fn interner(&self) -> &ArenaInterner<'arena> {
71        &self.interner
72    }
73
74    /// Consumes the parser and returns the interner.
75    pub fn into_interner(self) -> ArenaInterner<'arena> {
76        self.interner
77    }
78
79    /// Parse SQL input string into an arena-allocated Statement.
80    ///
81    /// Supports the full range of SQL statements including DML (SELECT, INSERT,
82    /// UPDATE, DELETE), DDL (CREATE, DROP, ALTER), and transaction statements.
83    pub fn parse_sql(
84        input: &str,
85        arena: &'arena Bump,
86    ) -> Result<Statement<'arena>, ParseError> {
87        let mut lexer = Lexer::new(input);
88        let tokens = lexer
89            .tokenize()
90            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
91
92        let mut parser = ArenaParser::new(tokens, arena);
93        parser.parse_statement()
94    }
95
96    /// Parse SQL input string into an arena-allocated SelectStmt.
97    ///
98    /// Convenience method for when you know you're parsing a SELECT.
99    pub fn parse_select(
100        input: &str,
101        arena: &'arena Bump,
102    ) -> Result<&'arena SelectStmt<'arena>, ParseError> {
103        let mut lexer = Lexer::new(input);
104        let tokens = lexer
105            .tokenize()
106            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
107
108        let mut parser = ArenaParser::new(tokens, arena);
109        parser.parse_select_statement()
110    }
111
112    /// Parse SQL input string into an arena-allocated SelectStmt, returning the interner too.
113    ///
114    /// Use this method when you need to resolve Symbol values to strings.
115    pub fn parse_select_with_interner(
116        input: &str,
117        arena: &'arena Bump,
118    ) -> Result<(&'arena SelectStmt<'arena>, ArenaInterner<'arena>), ParseError> {
119        let mut lexer = Lexer::new(input);
120        let tokens = lexer
121            .tokenize()
122            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
123
124        let mut parser = ArenaParser::new(tokens, arena);
125        let stmt = parser.parse_select_statement()?;
126        Ok((stmt, parser.into_interner()))
127    }
128
129    /// Parse a single statement.
130    fn parse_statement(&mut self) -> Result<Statement<'arena>, ParseError> {
131        // Skip leading semicolons
132        while self.try_consume(&Token::Semicolon) {}
133
134        match self.peek() {
135            // DML statements
136            Token::Keyword(Keyword::Select) | Token::Keyword(Keyword::With) => {
137                let stmt = self.parse_select_statement()?;
138                Ok(Statement::Select(stmt))
139            }
140            Token::Keyword(Keyword::Insert) => {
141                let stmt = self.parse_insert_statement()?;
142                Ok(Statement::Insert(stmt.clone()))
143            }
144            Token::Keyword(Keyword::Replace) => {
145                let stmt = self.parse_replace_statement()?;
146                Ok(Statement::Insert(stmt.clone()))
147            }
148            Token::Keyword(Keyword::Update) => {
149                let stmt = self.parse_update_statement()?;
150                Ok(Statement::Update(stmt.clone()))
151            }
152            Token::Keyword(Keyword::Delete) => {
153                let stmt = self.parse_delete_statement()?;
154                Ok(Statement::Delete(stmt.clone()))
155            }
156
157            // DDL statements
158            Token::Keyword(Keyword::Create) => self.parse_create_statement(),
159            Token::Keyword(Keyword::Drop) => self.parse_drop_statement(),
160            Token::Keyword(Keyword::Alter) => {
161                let stmt = self.parse_alter_table_statement()?;
162                Ok(Statement::AlterTable(stmt.clone()))
163            }
164            Token::Keyword(Keyword::Truncate) => {
165                let stmt = self.parse_truncate_table_statement()?;
166                Ok(Statement::TruncateTable(stmt))
167            }
168            Token::Keyword(Keyword::Analyze) => {
169                let stmt = self.parse_analyze_statement()?;
170                Ok(Statement::Analyze(stmt))
171            }
172
173            // Transaction statements
174            Token::Keyword(Keyword::Begin) | Token::Keyword(Keyword::Start) => {
175                let stmt = self.parse_begin_statement()?;
176                Ok(Statement::BeginTransaction(stmt))
177            }
178            Token::Keyword(Keyword::Commit) => {
179                let stmt = self.parse_commit_statement()?;
180                Ok(Statement::Commit(stmt))
181            }
182            Token::Keyword(Keyword::Rollback) => {
183                // Check for ROLLBACK TO SAVEPOINT
184                if self.peek_next_keyword(Keyword::To) {
185                    let stmt = self.parse_rollback_to_savepoint_statement()?;
186                    Ok(Statement::RollbackToSavepoint(stmt))
187                } else {
188                    let stmt = self.parse_rollback_statement()?;
189                    Ok(Statement::Rollback(stmt))
190                }
191            }
192            Token::Keyword(Keyword::Savepoint) => {
193                let stmt = self.parse_savepoint_statement()?;
194                Ok(Statement::Savepoint(stmt))
195            }
196            Token::Keyword(Keyword::Release) => {
197                let stmt = self.parse_release_savepoint_statement()?;
198                Ok(Statement::ReleaseSavepoint(stmt))
199            }
200
201            _ => Err(ParseError {
202                message: format!("Unexpected token: {:?}", self.peek()),
203            }),
204        }
205    }
206
207    /// Parse CREATE statement and dispatch to appropriate sub-parser.
208    fn parse_create_statement(&mut self) -> Result<Statement<'arena>, ParseError> {
209        // Peek ahead to determine what we're creating
210        let mut offset = 1; // Skip CREATE
211
212        // Skip optional OR REPLACE
213        if matches!(self.peek_at_offset(offset), Token::Keyword(Keyword::Or)) {
214            offset += 2; // Skip OR REPLACE
215        }
216
217        // Skip optional UNIQUE, FULLTEXT, SPATIAL
218        if matches!(
219            self.peek_at_offset(offset),
220            Token::Keyword(Keyword::Unique)
221                | Token::Keyword(Keyword::Fulltext)
222                | Token::Keyword(Keyword::Spatial)
223        ) {
224            offset += 1;
225        }
226
227        // Skip optional TEMP/TEMPORARY
228        if matches!(
229            self.peek_at_offset(offset),
230            Token::Keyword(Keyword::Temp) | Token::Keyword(Keyword::Temporary)
231        ) {
232            offset += 1;
233        }
234
235        match self.peek_at_offset(offset) {
236            Token::Keyword(Keyword::Index) => {
237                let stmt = self.parse_create_index_statement()?;
238                Ok(Statement::CreateIndex(stmt))
239            }
240            Token::Keyword(Keyword::View) => {
241                let stmt = self.parse_create_view_statement()?;
242                Ok(Statement::CreateView(stmt))
243            }
244            _ => Err(ParseError {
245                message: format!(
246                    "Unsupported CREATE statement type: {:?}",
247                    self.peek_at_offset(offset)
248                ),
249            }),
250        }
251    }
252
253    /// Parse DROP statement and dispatch to appropriate sub-parser.
254    fn parse_drop_statement(&mut self) -> Result<Statement<'arena>, ParseError> {
255        // Peek ahead to determine what we're dropping
256        match self.peek_at_offset(1) {
257            Token::Keyword(Keyword::Table) => {
258                let stmt = self.parse_drop_table_statement()?;
259                Ok(Statement::DropTable(stmt))
260            }
261            Token::Keyword(Keyword::Index) => {
262                let stmt = self.parse_drop_index_statement()?;
263                Ok(Statement::DropIndex(stmt))
264            }
265            Token::Keyword(Keyword::View) => {
266                let stmt = self.parse_drop_view_statement()?;
267                Ok(Statement::DropView(stmt))
268            }
269            _ => Err(ParseError {
270                message: format!(
271                    "Unsupported DROP statement type: {:?}",
272                    self.peek_at_offset(1)
273                ),
274            }),
275        }
276    }
277
278    /// Parse an expression and return an arena-allocated reference.
279    pub fn parse_expression_sql(
280        input: &str,
281        arena: &'arena Bump,
282    ) -> Result<&'arena Expression<'arena>, ParseError> {
283        let mut lexer = Lexer::new(input);
284        let tokens = lexer
285            .tokenize()
286            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
287
288        let mut parser = ArenaParser::new(tokens, arena);
289        let expr = parser.parse_expression()?;
290        Ok(arena.alloc(expr))
291    }
292
293    /// Parse SQL input string into an arena-allocated AlterTableStmt.
294    pub fn parse_alter_table_sql(
295        input: &str,
296        arena: &'arena Bump,
297    ) -> Result<&'arena AlterTableStmt<'arena>, ParseError> {
298        let mut lexer = Lexer::new(input);
299        let tokens = lexer
300            .tokenize()
301            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
302
303        let mut parser = ArenaParser::new(tokens, arena);
304        parser.parse_alter_table_statement()
305    }
306
307    /// Parse an INSERT statement into an arena-allocated InsertStmt.
308    pub fn parse_insert(
309        input: &str,
310        arena: &'arena Bump,
311    ) -> Result<&'arena InsertStmt<'arena>, ParseError> {
312        let mut lexer = Lexer::new(input);
313        let tokens = lexer
314            .tokenize()
315            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
316
317        let mut parser = ArenaParser::new(tokens, arena);
318        parser.parse_insert_statement()
319    }
320
321    /// Parse an UPDATE statement into an arena-allocated UpdateStmt.
322    pub fn parse_update(
323        input: &str,
324        arena: &'arena Bump,
325    ) -> Result<&'arena UpdateStmt<'arena>, ParseError> {
326        let mut lexer = Lexer::new(input);
327        let tokens = lexer
328            .tokenize()
329            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
330
331        let mut parser = ArenaParser::new(tokens, arena);
332        parser.parse_update_statement()
333    }
334
335    /// Parse a DELETE statement into an arena-allocated DeleteStmt.
336    pub fn parse_delete(
337        input: &str,
338        arena: &'arena Bump,
339    ) -> Result<&'arena DeleteStmt<'arena>, ParseError> {
340        let mut lexer = Lexer::new(input);
341        let tokens = lexer
342            .tokenize()
343            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
344
345        let mut parser = ArenaParser::new(tokens, arena);
346        parser.parse_delete_statement()
347    }
348
349    /// Parse a REPLACE statement (alias for INSERT OR REPLACE) into an arena-allocated InsertStmt.
350    pub fn parse_replace(
351        input: &str,
352        arena: &'arena Bump,
353    ) -> Result<&'arena InsertStmt<'arena>, ParseError> {
354        let mut lexer = Lexer::new(input);
355        let tokens = lexer
356            .tokenize()
357            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
358
359        let mut parser = ArenaParser::new(tokens, arena);
360        parser.parse_replace_statement()
361    }
362
363    /// Parse an ALTER TABLE statement, returning the interner for symbol resolution.
364    pub fn parse_alter_table_sql_with_interner(
365        input: &str,
366        arena: &'arena Bump,
367    ) -> Result<(&'arena AlterTableStmt<'arena>, ArenaInterner<'arena>), ParseError> {
368        let mut lexer = Lexer::new(input);
369        let tokens = lexer
370            .tokenize()
371            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
372
373        let mut parser = ArenaParser::new(tokens, arena);
374        let stmt = parser.parse_alter_table_statement()?;
375        Ok((stmt, parser.into_interner()))
376    }
377
378    /// Parse a DELETE statement, returning the interner for symbol resolution.
379    pub fn parse_delete_with_interner(
380        input: &str,
381        arena: &'arena Bump,
382    ) -> Result<(&'arena DeleteStmt<'arena>, ArenaInterner<'arena>), ParseError> {
383        let mut lexer = Lexer::new(input);
384        let tokens = lexer
385            .tokenize()
386            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
387
388        let mut parser = ArenaParser::new(tokens, arena);
389        let stmt = parser.parse_delete_statement()?;
390        Ok((stmt, parser.into_interner()))
391    }
392
393    /// Parse an UPDATE statement, returning the interner for symbol resolution.
394    pub fn parse_update_with_interner(
395        input: &str,
396        arena: &'arena Bump,
397    ) -> Result<(&'arena UpdateStmt<'arena>, ArenaInterner<'arena>), ParseError> {
398        let mut lexer = Lexer::new(input);
399        let tokens = lexer
400            .tokenize()
401            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
402
403        let mut parser = ArenaParser::new(tokens, arena);
404        let stmt = parser.parse_update_statement()?;
405        Ok((stmt, parser.into_interner()))
406    }
407
408    /// Parse an INSERT statement, returning the interner for symbol resolution.
409    pub fn parse_insert_with_interner(
410        input: &str,
411        arena: &'arena Bump,
412    ) -> Result<(&'arena InsertStmt<'arena>, ArenaInterner<'arena>), ParseError> {
413        let mut lexer = Lexer::new(input);
414        let tokens = lexer
415            .tokenize()
416            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
417
418        let mut parser = ArenaParser::new(tokens, arena);
419        let stmt = parser.parse_insert_statement()?;
420        Ok((stmt, parser.into_interner()))
421    }
422
423    /// Parse a REPLACE statement, returning the interner for symbol resolution.
424    pub fn parse_replace_with_interner(
425        input: &str,
426        arena: &'arena Bump,
427    ) -> Result<(&'arena InsertStmt<'arena>, ArenaInterner<'arena>), ParseError> {
428        let mut lexer = Lexer::new(input);
429        let tokens = lexer
430            .tokenize()
431            .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
432
433        let mut parser = ArenaParser::new(tokens, arena);
434        let stmt = parser.parse_replace_statement()?;
435        Ok((stmt, parser.into_interner()))
436    }
437
438    /// Intern a string and return a Symbol.
439    #[inline]
440    pub(crate) fn intern(&mut self, s: &str) -> Symbol {
441        self.interner.intern(s)
442    }
443
444    /// Allocate a string in the arena (for non-identifier strings).
445    #[inline]
446    #[allow(dead_code)]
447    pub(crate) fn alloc_str(&self, s: &str) -> &'arena str {
448        self.arena.alloc_str(s)
449    }
450
451    /// Get a reference to the arena.
452    #[inline]
453    #[allow(dead_code)]
454    pub(crate) fn arena(&self) -> &'arena Bump {
455        self.arena
456    }
457
458    // ========================================================================
459    // Token manipulation helpers (same as standard parser)
460    // ========================================================================
461
462    /// Peek at the current token without consuming it.
463    pub(crate) fn peek(&self) -> &Token {
464        self.tokens.get(self.position).unwrap_or(&Token::Eof)
465    }
466
467    /// Peek at the next token (position + 1) without consuming.
468    #[allow(dead_code)]
469    pub(crate) fn peek_next(&self) -> &Token {
470        self.tokens.get(self.position + 1).unwrap_or(&Token::Eof)
471    }
472
473    /// Peek at token at specific offset from current position.
474    #[allow(dead_code)]
475    pub(crate) fn peek_at_offset(&self, offset: usize) -> &Token {
476        self.tokens.get(self.position + offset).unwrap_or(&Token::Eof)
477    }
478
479    /// Advance to the next token.
480    pub(crate) fn advance(&mut self) {
481        if self.position < self.tokens.len() {
482            self.position += 1;
483        }
484    }
485
486    /// Check if current token is a specific keyword.
487    pub(crate) fn peek_keyword(&self, keyword: Keyword) -> bool {
488        matches!(self.peek(), Token::Keyword(kw) if *kw == keyword)
489    }
490
491    /// Check if next token (position + 1) is a specific keyword.
492    #[allow(dead_code)]
493    pub(crate) fn peek_next_keyword(&self, keyword: Keyword) -> bool {
494        matches!(self.peek_next(), Token::Keyword(kw) if *kw == keyword)
495    }
496
497    /// Consume a keyword, returning an error if it's not the expected keyword.
498    pub(crate) fn consume_keyword(&mut self, keyword: Keyword) -> Result<(), ParseError> {
499        if self.peek_keyword(keyword) {
500            self.advance();
501            Ok(())
502        } else {
503            Err(ParseError {
504                message: format!("Expected keyword {:?}, found {:?}", keyword, self.peek()),
505            })
506        }
507    }
508
509    /// Try to consume a keyword, returning true if successful.
510    pub(crate) fn try_consume_keyword(&mut self, keyword: Keyword) -> bool {
511        if self.peek_keyword(keyword) {
512            self.advance();
513            true
514        } else {
515            false
516        }
517    }
518
519    /// Expect a specific keyword.
520    pub(crate) fn expect_keyword(&mut self, keyword: Keyword) -> Result<(), ParseError> {
521        self.consume_keyword(keyword)
522    }
523
524    /// Expect a specific token.
525    pub(crate) fn expect_token(&mut self, expected: Token) -> Result<(), ParseError> {
526        if self.peek() == &expected {
527            self.advance();
528            Ok(())
529        } else {
530            Err(ParseError {
531                message: format!("Expected {:?}, found {:?}", expected, self.peek()),
532            })
533        }
534    }
535
536    /// Try to consume a specific token, returning true if successful.
537    pub(crate) fn try_consume(&mut self, token: &Token) -> bool {
538        if self.peek() == token {
539            self.advance();
540            true
541        } else {
542            false
543        }
544    }
545
546    /// Get the next placeholder index.
547    pub(crate) fn next_placeholder(&mut self) -> usize {
548        let index = self.placeholder_count;
549        self.placeholder_count += 1;
550        index
551    }
552
553    // ========================================================================
554    // Common parsing helpers
555    // ========================================================================
556
557    /// Parse an identifier and intern it, returning a Symbol.
558    pub(crate) fn parse_arena_identifier(&mut self) -> Result<Symbol, ParseError> {
559        match self.peek() {
560            Token::Identifier(name) => {
561                let name = name.clone();
562                self.advance();
563                Ok(self.intern(&name))
564            }
565            _ => Err(ParseError {
566                message: format!("Expected identifier, found {:?}", self.peek()),
567            }),
568        }
569    }
570
571    /// Parse a comma-separated list of identifiers.
572    pub(crate) fn parse_identifier_list(
573        &mut self,
574    ) -> Result<bumpalo::collections::Vec<'arena, Symbol>, ParseError> {
575        let mut list = bumpalo::collections::Vec::new_in(self.arena);
576        loop {
577            list.push(self.parse_arena_identifier()?);
578            if !self.try_consume(&Token::Comma) {
579                break;
580            }
581        }
582        Ok(list)
583    }
584
585    /// Parse an optional column alias list: (col1, col2, ...)
586    ///
587    /// SQL:1999 Feature E051-09: Derived column lists in table aliases
588    /// Example: FROM t AS myalias (x, y) or FROM (SELECT a, b) AS mytemp (x, y)
589    ///
590    /// Returns None if no opening parenthesis is found, otherwise parses
591    /// and returns the list of column aliases as Symbols.
592    pub(crate) fn parse_column_alias_list(
593        &mut self,
594    ) -> Result<Option<bumpalo::collections::Vec<'arena, Symbol>>, ParseError> {
595        // Check for opening parenthesis
596        if !self.try_consume(&Token::LParen) {
597            return Ok(None);
598        }
599
600        let mut aliases = bumpalo::collections::Vec::new_in(self.arena);
601
602        // Handle empty list case: ()
603        if self.try_consume(&Token::RParen) {
604            return Ok(Some(aliases));
605        }
606
607        // Parse first alias (identifiers or keywords allowed)
608        aliases.push(self.parse_alias_name_symbol()?);
609
610        // Parse remaining aliases
611        while self.try_consume(&Token::Comma) {
612            aliases.push(self.parse_alias_name_symbol()?);
613        }
614
615        // Expect closing parenthesis
616        self.expect_token(Token::RParen)?;
617
618        Ok(Some(aliases))
619    }
620
621    /// Parse an identifier or keyword as an alias name, returning a Symbol.
622    fn parse_alias_name_symbol(&mut self) -> Result<Symbol, ParseError> {
623        match self.peek() {
624            Token::Identifier(name) => {
625                let name = name.clone();
626                self.advance();
627                Ok(self.intern(&name))
628            }
629            Token::Keyword(kw) => {
630                // Allow keywords as alias names
631                let name = kw.to_string();
632                self.advance();
633                Ok(self.intern(&name))
634            }
635            _ => Err(ParseError {
636                message: format!("Expected alias name, found {:?}", self.peek()),
637            }),
638        }
639    }
640}
641
642// ============================================================================
643// Standalone parse-to-owned functions
644// ============================================================================
645
646/// Parse SQL and return a heap-allocated (owned) SelectStmt.
647///
648/// This function provides the performance benefits of arena parsing while
649/// returning a standard `SelectStmt` that can be stored and used without
650/// lifetime constraints.
651///
652/// # Performance
653///
654/// This is faster than the standard parser because:
655/// - Arena parsing is 30-40% faster (fewer allocations during parse)
656/// - Conversion allocates fewer, larger chunks (better cache locality)
657/// - Many strings benefit from SSO (Small String Optimization)
658///
659/// # Example
660///
661/// ```ignore
662/// use vibesql_parser::arena_parser::parse_select_to_owned;
663///
664/// let stmt = parse_select_to_owned("SELECT * FROM users")?;
665/// // stmt is a standard SelectStmt, no lifetime constraints
666/// ```
667pub fn parse_select_to_owned(input: &str) -> Result<vibesql_ast::SelectStmt, ParseError> {
668    let arena = Bump::new();
669    let mut lexer = Lexer::new(input);
670    let tokens = lexer
671        .tokenize()
672        .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
673
674    let mut parser = ArenaParser::new(tokens, &arena);
675    let arena_stmt = parser.parse_select_statement()?;
676    let converter = Converter::new(parser.interner());
677    Ok(converter.convert_select(arena_stmt))
678}
679
680/// Parse an expression and return a heap-allocated (owned) Expression.
681///
682/// Like [`parse_select_to_owned`], this provides arena parsing
683/// benefits while returning an owned expression.
684///
685/// # Example
686///
687/// ```ignore
688/// use vibesql_parser::arena_parser::parse_expression_to_owned;
689///
690/// let expr = parse_expression_to_owned("a + b * 2")?;
691/// ```
692pub fn parse_expression_to_owned(input: &str) -> Result<vibesql_ast::Expression, ParseError> {
693    let arena = Bump::new();
694    let mut lexer = Lexer::new(input);
695    let tokens = lexer
696        .tokenize()
697        .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
698
699    let mut parser = ArenaParser::new(tokens, &arena);
700    let arena_expr = parser.parse_expression()?;
701    let converter = Converter::new(parser.interner());
702    Ok(converter.convert_expression(&arena_expr))
703}
704
705/// Parse INSERT SQL and return a heap-allocated (owned) InsertStmt.
706///
707/// Like [`parse_select_to_owned`], this provides arena parsing
708/// benefits while returning a standard `InsertStmt`.
709///
710/// # Example
711///
712/// ```ignore
713/// use vibesql_parser::arena_parser::parse_insert_to_owned;
714///
715/// let stmt = parse_insert_to_owned("INSERT INTO users (name) VALUES ('Alice')")?;
716/// ```
717pub fn parse_insert_to_owned(input: &str) -> Result<vibesql_ast::InsertStmt, ParseError> {
718    let arena = Bump::new();
719    let mut lexer = Lexer::new(input);
720    let tokens = lexer
721        .tokenize()
722        .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
723
724    let mut parser = ArenaParser::new(tokens, &arena);
725    let arena_stmt = parser.parse_insert_statement()?;
726    let converter = Converter::new(parser.interner());
727    Ok(converter.convert_insert(arena_stmt))
728}
729
730/// Parse UPDATE SQL and return a heap-allocated (owned) UpdateStmt.
731///
732/// Like [`parse_select_to_owned`], this provides arena parsing
733/// benefits while returning a standard `UpdateStmt`.
734///
735/// # Example
736///
737/// ```ignore
738/// use vibesql_parser::arena_parser::parse_update_to_owned;
739///
740/// let stmt = parse_update_to_owned("UPDATE users SET name = 'Bob' WHERE id = 1")?;
741/// ```
742pub fn parse_update_to_owned(input: &str) -> Result<vibesql_ast::UpdateStmt, ParseError> {
743    let arena = Bump::new();
744    let mut lexer = Lexer::new(input);
745    let tokens = lexer
746        .tokenize()
747        .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
748
749    let mut parser = ArenaParser::new(tokens, &arena);
750    let arena_stmt = parser.parse_update_statement()?;
751    let converter = Converter::new(parser.interner());
752    Ok(converter.convert_update(arena_stmt))
753}
754
755/// Parse DELETE SQL and return a heap-allocated (owned) DeleteStmt.
756///
757/// Like [`parse_select_to_owned`], this provides arena parsing
758/// benefits while returning a standard `DeleteStmt`.
759///
760/// # Example
761///
762/// ```ignore
763/// use vibesql_parser::arena_parser::parse_delete_to_owned;
764///
765/// let stmt = parse_delete_to_owned("DELETE FROM users WHERE id = 1")?;
766/// ```
767pub fn parse_delete_to_owned(input: &str) -> Result<vibesql_ast::DeleteStmt, ParseError> {
768    let arena = Bump::new();
769    let mut lexer = Lexer::new(input);
770    let tokens = lexer
771        .tokenize()
772        .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
773
774    let mut parser = ArenaParser::new(tokens, &arena);
775    let arena_stmt = parser.parse_delete_statement()?;
776    let converter = Converter::new(parser.interner());
777    Ok(converter.convert_delete(arena_stmt))
778}
779
780#[cfg(test)]
781mod tests {
782    use super::*;
783    use vibesql_ast::arena::Expression;
784    use vibesql_types::SqlValue;
785
786    #[test]
787    fn test_date_literal() {
788        let arena = Bump::new();
789        let expr = ArenaParser::parse_expression_sql("DATE '1998-12-01'", &arena).unwrap();
790        match expr {
791            Expression::Literal(SqlValue::Date(d)) => {
792                assert_eq!(d.year, 1998);
793                assert_eq!(d.month, 12);
794                assert_eq!(d.day, 1);
795            }
796            _ => panic!("Expected Date literal, got {:?}", expr),
797        }
798    }
799
800    #[test]
801    fn test_time_literal() {
802        let arena = Bump::new();
803        let expr = ArenaParser::parse_expression_sql("TIME '12:30:45'", &arena).unwrap();
804        match expr {
805            Expression::Literal(SqlValue::Time(t)) => {
806                assert_eq!(t.hour, 12);
807                assert_eq!(t.minute, 30);
808                assert_eq!(t.second, 45);
809            }
810            _ => panic!("Expected Time literal, got {:?}", expr),
811        }
812    }
813
814    #[test]
815    fn test_timestamp_literal() {
816        let arena = Bump::new();
817        let expr = ArenaParser::parse_expression_sql("TIMESTAMP '2024-01-15 10:30:00'", &arena).unwrap();
818        match expr {
819            Expression::Literal(SqlValue::Timestamp(ts)) => {
820                assert_eq!(ts.date.year, 2024);
821                assert_eq!(ts.date.month, 1);
822                assert_eq!(ts.date.day, 15);
823            }
824            _ => panic!("Expected Timestamp literal, got {:?}", expr),
825        }
826    }
827
828    #[test]
829    fn test_interval_literal() {
830        let arena = Bump::new();
831        let expr = ArenaParser::parse_expression_sql("INTERVAL '90' DAY", &arena).unwrap();
832        // Just verify it parses to an Interval type
833        assert!(matches!(expr, Expression::Literal(SqlValue::Interval(_))));
834    }
835
836    #[test]
837    fn test_date_minus_interval_expression() {
838        let arena = Bump::new();
839        let expr = ArenaParser::parse_expression_sql("DATE '1998-12-01' - INTERVAL '90' DAY", &arena).unwrap();
840        match expr {
841            Expression::BinaryOp { op, left, right } => {
842                assert_eq!(*op, vibesql_ast::BinaryOperator::Minus);
843                assert!(matches!(left, Expression::Literal(SqlValue::Date(_))));
844                assert!(matches!(right, Expression::Literal(SqlValue::Interval(_))));
845            }
846            _ => panic!("Expected BinaryOp, got {:?}", expr),
847        }
848    }
849
850    #[test]
851    fn test_tpch_q1_parses() {
852        let arena = Bump::new();
853        let sql = r#"SELECT
854            l_returnflag,
855            l_linestatus,
856            SUM(l_quantity) AS sum_qty,
857            SUM(l_extendedprice) AS sum_base_price,
858            SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price,
859            SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge,
860            AVG(l_quantity) AS avg_qty,
861            AVG(l_extendedprice) AS avg_price,
862            AVG(l_discount) AS avg_disc,
863            COUNT(*) AS count_order
864        FROM
865            lineitem
866        WHERE
867            l_shipdate <= DATE '1998-12-01' - INTERVAL '90' DAY
868        GROUP BY
869            l_returnflag,
870            l_linestatus
871        ORDER BY
872            l_returnflag,
873            l_linestatus"#;
874
875        // This should parse successfully now with typed literal support
876        let result = ArenaParser::parse_sql(sql, &arena);
877        assert!(result.is_ok(), "TPC-H Q1 should parse successfully: {:?}", result.err());
878    }
879}