vibesql_parser/
lib.rs

1//! SQL:1999 Parser crate.
2//!
3//! Provides tokenization and parsing of SQL statements into the shared AST.
4//!
5//! # Arena-allocated Parser
6//!
7//! For performance-critical code paths, the [`arena_parser`] module provides
8//! an arena-based parser that allocates AST nodes from a bump allocator.
9//!
10//! # Arena Fallback Parsing
11//!
12//! The [`parse_with_arena_fallback`] function provides optimized parsing by
13//! using arena allocation for supported statement types (SELECT) while falling
14//! back to standard heap allocation for other statements. This provides the
15//! best of both worlds: arena performance where it helps most (complex queries)
16//! and full feature support for all SQL statements.
17//!
18//! ```ignore
19//! use vibesql_parser::parse_with_arena_fallback;
20//!
21//! let stmt = parse_with_arena_fallback("SELECT * FROM users")?;
22//! // Uses arena parsing internally, converts to standard Statement
23//! ```
24
25pub mod arena_parser;
26
27mod keywords;
28mod lexer;
29mod parser;
30#[cfg(test)]
31mod tests;
32mod token;
33
34pub use keywords::Keyword;
35pub use lexer::{Lexer, LexerError};
36pub use parser::{ParseError, Parser};
37pub use token::Token;
38
39use vibesql_ast::Statement;
40
41/// Parse SQL using arena allocation where supported, falling back to standard parsing.
42///
43/// This function provides optimized parsing by:
44/// 1. Detecting the statement type from the first token
45/// 2. Using arena-allocated parsing for SELECT statements
46/// 3. Converting arena AST to standard heap-allocated AST
47/// 4. Falling back to standard parsing for unsupported statement types
48///
49/// # Performance
50///
51/// Arena parsing can provide 10-15% improvement for complex SELECT statements
52/// due to reduced allocation overhead and better cache locality. The conversion
53/// to standard AST types adds minimal overhead.
54///
55/// # Supported Statement Types
56///
57/// Currently uses arena parsing for:
58/// - SELECT statements (including CTEs, subqueries, joins)
59///
60/// Falls back to standard parsing for:
61/// - INSERT, UPDATE, DELETE (arena support planned for future)
62/// - DDL statements (CREATE, ALTER, DROP)
63/// - Transaction statements (BEGIN, COMMIT, ROLLBACK)
64/// - Other SQL statements
65///
66/// # Example
67///
68/// ```ignore
69/// use vibesql_parser::parse_with_arena_fallback;
70///
71/// // Uses arena parsing
72/// let select = parse_with_arena_fallback("SELECT * FROM users WHERE id = 1")?;
73///
74/// // Falls back to standard parsing
75/// let insert = parse_with_arena_fallback("INSERT INTO users VALUES (1, 'Alice')")?;
76/// ```
77pub fn parse_with_arena_fallback(sql: &str) -> Result<Statement, ParseError> {
78    // Tokenize to detect statement type
79    let mut lexer = Lexer::new(sql);
80    let tokens = lexer
81        .tokenize()
82        .map_err(|e| ParseError { message: format!("Lexer error: {}", e) })?;
83
84    // Check first token to determine statement type
85    if let Some(first_token) = tokens.first() {
86        if matches!(first_token, Token::Keyword(Keyword::Select) | Token::Keyword(Keyword::With)) {
87            // Use arena parsing for SELECT statements (including WITH CTEs)
88            match arena_parser::parse_select_to_owned(sql) {
89                Ok(select_stmt) => {
90                    return Ok(Statement::Select(Box::new(select_stmt)));
91                }
92                Err(_) => {
93                    // Arena parsing failed, fall back to standard parser
94                    // This can happen with edge cases the arena parser doesn't support yet
95                }
96            }
97        }
98    }
99
100    // Fall back to standard parser for all other statements
101    // or if arena parsing failed
102    Parser::parse_sql(sql)
103}
104
105#[cfg(test)]
106mod arena_fallback_tests {
107    use super::*;
108
109    #[test]
110    fn test_arena_fallback_simple_select() {
111        let result = parse_with_arena_fallback("SELECT * FROM users");
112        assert!(result.is_ok());
113        assert!(matches!(result.unwrap(), Statement::Select(_)));
114    }
115
116    #[test]
117    fn test_arena_fallback_select_with_where() {
118        let result = parse_with_arena_fallback("SELECT id, name FROM users WHERE active = TRUE");
119        assert!(result.is_ok());
120        assert!(matches!(result.unwrap(), Statement::Select(_)));
121    }
122
123    #[test]
124    fn test_arena_fallback_select_with_cte() {
125        let result = parse_with_arena_fallback(
126            "WITH active_users AS (SELECT * FROM users WHERE active = TRUE) \
127             SELECT * FROM active_users"
128        );
129        assert!(result.is_ok());
130        assert!(matches!(result.unwrap(), Statement::Select(_)));
131    }
132
133    #[test]
134    fn test_arena_fallback_insert() {
135        let result = parse_with_arena_fallback("INSERT INTO users (id, name) VALUES (1, 'Alice')");
136        assert!(result.is_ok());
137        assert!(matches!(result.unwrap(), Statement::Insert(_)));
138    }
139
140    #[test]
141    fn test_arena_fallback_update() {
142        let result = parse_with_arena_fallback("UPDATE users SET name = 'Bob' WHERE id = 1");
143        assert!(result.is_ok());
144        assert!(matches!(result.unwrap(), Statement::Update(_)));
145    }
146
147    #[test]
148    fn test_arena_fallback_delete() {
149        let result = parse_with_arena_fallback("DELETE FROM users WHERE id = 1");
150        assert!(result.is_ok());
151        assert!(matches!(result.unwrap(), Statement::Delete(_)));
152    }
153
154    #[test]
155    fn test_arena_fallback_create_table() {
156        let result = parse_with_arena_fallback("CREATE TABLE users (id INT PRIMARY KEY)");
157        assert!(result.is_ok());
158        assert!(matches!(result.unwrap(), Statement::CreateTable(_)));
159    }
160}