chryso_parser/
lib.rs

1use chryso_core::ast::{
2    BinaryOperator, Expr, Join, JoinType, Literal, OrderByExpr, SelectItem, SelectStatement,
3    Statement, TableRef, UnaryOperator,
4};
5use chryso_core::{ChrysoError, ChrysoResult};
6
7#[derive(Debug, Clone, Copy)]
8pub enum Dialect {
9    Postgres,
10    MySql,
11}
12
13#[derive(Debug, Clone)]
14pub struct ParserConfig {
15    pub dialect: Dialect,
16}
17
18impl Default for ParserConfig {
19    fn default() -> Self {
20        Self {
21            dialect: Dialect::Postgres,
22        }
23    }
24}
25
26pub trait SqlParser {
27    fn parse(&self, sql: &str) -> ChrysoResult<Statement>;
28}
29
30pub struct SimpleParser {
31    config: ParserConfig,
32}
33
34impl SimpleParser {
35    pub fn new(config: ParserConfig) -> Self {
36        Self { config }
37    }
38}
39
40impl SqlParser for SimpleParser {
41    fn parse(&self, sql: &str) -> ChrysoResult<Statement> {
42        let tokens = tokenize(sql, self.config.dialect)?;
43        let mut parser = Parser::new(tokens, self.config.dialect);
44        let stmt = parser.parse_statement()?;
45        Ok(chryso_core::ast::normalize_statement(&stmt))
46    }
47}
48
49#[derive(Debug, Clone, PartialEq)]
50enum Token {
51    Ident(String),
52    Number(String),
53    String(String),
54    Comma,
55    Dot,
56    Star,
57    LParen,
58    RParen,
59    Eq,
60    NotEq,
61    Lt,
62    LtEq,
63    Gt,
64    GtEq,
65    Plus,
66    Minus,
67    Slash,
68    Keyword(Keyword),
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72enum Keyword {
73    Select,
74    Explain,
75    Create,
76    Drop,
77    Truncate,
78    If,
79    Exists,
80    Table,
81    Analyze,
82    Insert,
83    Into,
84    Values,
85    Default,
86    Update,
87    Set,
88    Delete,
89    Over,
90    Partition,
91    In,
92    From,
93    Where,
94    And,
95    Or,
96    Not,
97    As,
98    Join,
99    Cross,
100    Natural,
101    Left,
102    Right,
103    Full,
104    On,
105    Group,
106    By,
107    Having,
108    Order,
109    Offset,
110    Limit,
111    Asc,
112    Desc,
113    Distinct,
114    Union,
115    All,
116    Intersect,
117    Except,
118    With,
119    Recursive,
120    Returning,
121    True,
122    False,
123    Is,
124    Null,
125    Between,
126    Like,
127    ILike,
128    Using,
129    Case,
130    When,
131    Then,
132    Else,
133    End,
134    Nulls,
135    First,
136    Last,
137    Escape,
138}
139
140struct Parser {
141    tokens: Vec<Token>,
142    pos: usize,
143    _dialect: Dialect,
144}
145
146impl Parser {
147    fn new(tokens: Vec<Token>, dialect: Dialect) -> Self {
148        Self {
149            tokens,
150            pos: 0,
151            _dialect: dialect,
152        }
153    }
154
155    fn parse_statement(&mut self) -> ChrysoResult<Statement> {
156        if self.consume_keyword(Keyword::With) {
157            self.parse_with_statement()
158        } else if self.consume_keyword(Keyword::Explain) {
159            let statement = self.parse_explain_statement()?;
160            Ok(Statement::Explain(Box::new(statement)))
161        } else if self.consume_keyword(Keyword::Analyze) {
162            let statement = self.parse_analyze_statement()?;
163            Ok(statement)
164        } else if self.consume_keyword(Keyword::Insert) {
165            let statement = self.parse_insert_statement()?;
166            Ok(statement)
167        } else if self.consume_keyword(Keyword::Update) {
168            let statement = self.parse_update_statement()?;
169            Ok(statement)
170        } else if self.consume_keyword(Keyword::Delete) {
171            let statement = self.parse_delete_statement()?;
172            Ok(statement)
173        } else if self.consume_keyword(Keyword::Create) {
174            let statement = self.parse_create_statement()?;
175            Ok(statement)
176        } else if self.consume_keyword(Keyword::Drop) {
177            let statement = self.parse_drop_statement()?;
178            Ok(statement)
179        } else if self.consume_keyword(Keyword::Truncate) {
180            let statement = self.parse_truncate_statement()?;
181            Ok(statement)
182        } else if self.consume_keyword(Keyword::Select) {
183            let select = self.parse_select()?;
184            self.parse_query_tail(select)
185        } else {
186            Err(ChrysoError::new("unexpected statement"))
187        }
188    }
189
190    fn parse_explain_statement(&mut self) -> ChrysoResult<Statement> {
191        if self.consume_keyword(Keyword::With) {
192            self.parse_with_statement()
193        } else if self.consume_keyword(Keyword::Select) {
194            let select = self.parse_select()?;
195            self.parse_query_tail(select)
196        } else if self.consume_keyword(Keyword::Insert) {
197            self.parse_insert_statement()
198        } else if self.consume_keyword(Keyword::Update) {
199            self.parse_update_statement()
200        } else if self.consume_keyword(Keyword::Delete) {
201            self.parse_delete_statement()
202        } else if self.consume_keyword(Keyword::Create) {
203            self.parse_create_statement()
204        } else if self.consume_keyword(Keyword::Analyze) {
205            self.parse_analyze_statement()
206        } else {
207            Err(ChrysoError::new("EXPLAIN expects a statement"))
208        }
209    }
210
211    fn parse_with_statement(&mut self) -> ChrysoResult<Statement> {
212        let mut ctes = Vec::new();
213        let mut seen_names = std::collections::HashSet::new();
214        let recursive = self.consume_keyword(Keyword::Recursive);
215        loop {
216            let name = self.expect_identifier()?;
217            if !seen_names.insert(name.clone()) {
218                return Err(ChrysoError::new(format!(
219                    "duplicate CTE name {name}"
220                )));
221            }
222            let columns = if self.consume_token(&Token::LParen) {
223                if self.consume_token(&Token::RParen) {
224                    return Err(ChrysoError::new("CTE column list cannot be empty"));
225                }
226                let cols = self.parse_identifier_list()?;
227                self.expect_token(Token::RParen)?;
228                let mut seen_cols = std::collections::HashSet::new();
229                for col in &cols {
230                    if !seen_cols.insert(col.clone()) {
231                        return Err(ChrysoError::new(format!(
232                            "duplicate CTE column {col}"
233                        )));
234                    }
235                }
236                cols
237            } else {
238                Vec::new()
239            };
240            self.expect_keyword(Keyword::As)?;
241            self.expect_token(Token::LParen)?;
242            let stmt = if self.consume_keyword(Keyword::Select) {
243                let select = self.parse_select()?;
244                self.parse_query_tail(select)?
245            } else if self.consume_keyword(Keyword::Insert) {
246                self.parse_insert_statement()?
247            } else if self.consume_keyword(Keyword::Update) {
248                self.parse_update_statement()?
249            } else if self.consume_keyword(Keyword::Delete) {
250                self.parse_delete_statement()?
251            } else if self.consume_keyword(Keyword::With) {
252                self.parse_with_statement()?
253            } else {
254                return Err(ChrysoError::new("WITH expects SELECT/INSERT/UPDATE/DELETE or WITH in CTE"));
255            };
256            self.expect_token(Token::RParen)?;
257            ctes.push(chryso_core::ast::Cte {
258                name,
259                columns,
260                query: Box::new(stmt),
261            });
262            if !self.consume_token(&Token::Comma) {
263                break;
264            }
265        }
266        let statement = if self.consume_keyword(Keyword::Select) {
267            let select = self.parse_select()?;
268            self.parse_query_tail(select)?
269        } else if self.consume_keyword(Keyword::Insert) {
270            self.parse_insert_statement()?
271        } else if self.consume_keyword(Keyword::Update) {
272            self.parse_update_statement()?
273        } else if self.consume_keyword(Keyword::Delete) {
274            self.parse_delete_statement()?
275        } else {
276            return Err(ChrysoError::new("WITH expects SELECT/INSERT/UPDATE/DELETE after CTEs"));
277        };
278        Ok(Statement::With(chryso_core::ast::WithStatement {
279            ctes,
280            recursive,
281            statement: Box::new(statement),
282        }))
283    }
284
285    fn parse_query_tail(&mut self, left: SelectStatement) -> ChrysoResult<Statement> {
286        let mut current = Statement::Select(left);
287        loop {
288            let op = if self.consume_keyword(Keyword::Union) {
289                if self.consume_keyword(Keyword::All) {
290                    chryso_core::ast::SetOperator::UnionAll
291                } else {
292                    chryso_core::ast::SetOperator::Union
293                }
294            } else if self.consume_keyword(Keyword::Intersect) {
295                if self.consume_keyword(Keyword::All) {
296                    chryso_core::ast::SetOperator::IntersectAll
297                } else {
298                    chryso_core::ast::SetOperator::Intersect
299                }
300            } else if self.consume_keyword(Keyword::Except) {
301                if self.consume_keyword(Keyword::All) {
302                    chryso_core::ast::SetOperator::ExceptAll
303                } else {
304                    chryso_core::ast::SetOperator::Except
305                }
306            } else {
307                break;
308            };
309            self.expect_keyword(Keyword::Select)?;
310            let right = self.parse_select()?;
311            current = Statement::SetOp {
312                left: Box::new(current),
313                op,
314                right: Box::new(Statement::Select(right)),
315            };
316        }
317        Ok(current)
318    }
319
320    fn parse_create_statement(&mut self) -> ChrysoResult<Statement> {
321        if self.consume_keyword(Keyword::Table) {
322            let if_not_exists = if self.consume_keyword(Keyword::If) {
323                self.expect_keyword(Keyword::Not)?;
324                self.expect_keyword(Keyword::Exists)?;
325                true
326            } else {
327                false
328            };
329            let name = self.expect_identifier()?;
330            let columns = if self.consume_token(&Token::LParen) {
331                let columns = self.parse_column_definitions()?;
332                self.expect_token(Token::RParen)?;
333                columns
334            } else {
335                Vec::new()
336            };
337            Ok(Statement::CreateTable(chryso_core::ast::CreateTableStatement {
338                name,
339                if_not_exists,
340                columns,
341            }))
342        } else {
343            Err(ChrysoError::new("only CREATE TABLE is supported"))
344        }
345    }
346
347    fn parse_column_definitions(&mut self) -> ChrysoResult<Vec<chryso_core::ast::ColumnDef>> {
348        let mut columns = Vec::new();
349        loop {
350            let name = self.expect_identifier()?;
351            let data_type = self.parse_type_name()?;
352            if data_type.is_empty() {
353                return Err(ChrysoError::new("column expects a data type"));
354            }
355            columns.push(chryso_core::ast::ColumnDef { name, data_type });
356            if !self.consume_token(&Token::Comma) {
357                break;
358            }
359        }
360        Ok(columns)
361    }
362
363    fn parse_type_name(&mut self) -> ChrysoResult<String> {
364        // TODO: Replace this heuristic with a grammar-backed type parser.
365        // TODO: Implement a dedicated type parser for complex type syntax.
366        let mut output = String::new();
367        let mut depth = 0usize;
368        loop {
369            let token = match self.peek().cloned() {
370                Some(token) => token,
371                None => break,
372            };
373            if depth == 0 {
374                if matches!(token, Token::Comma | Token::RParen) {
375                    break;
376                }
377            }
378            let part = match self.next() {
379                Some(Token::Ident(name)) => name,
380                Some(Token::Number(value)) => value,
381                Some(Token::Dot) => ".".to_string(),
382                Some(Token::Comma) => ",".to_string(),
383                Some(Token::LParen) => {
384                    depth += 1;
385                    "(".to_string()
386                }
387                Some(Token::RParen) => {
388                    if depth == 0 {
389                        return Err(ChrysoError::new("unexpected ')' in type"));
390                    }
391                    depth -= 1;
392                    ")".to_string()
393                }
394                Some(Token::Keyword(keyword)) => keyword_label(keyword).to_string(),
395                other => {
396                    return Err(ChrysoError::new(format!(
397                        "unexpected token in type: {}",
398                        other.as_ref().map(token_label).unwrap_or_else(|| "end of input".to_string())
399                    )))
400                }
401            };
402            if output.is_empty() {
403                output.push_str(&part);
404            } else if part == ")" || part == "(" || part == "," || part == "." {
405                output.push_str(&part);
406            } else if output.ends_with('(') || output.ends_with('.') || output.ends_with(',') {
407                output.push_str(&part);
408            } else {
409                output.push(' ');
410                output.push_str(&part);
411            }
412        }
413        Ok(output)
414    }
415
416    fn parse_drop_statement(&mut self) -> ChrysoResult<Statement> {
417        let _ = self.consume_keyword(Keyword::Table);
418        let if_exists = if self.consume_keyword(Keyword::If) {
419            self.expect_keyword(Keyword::Exists)?;
420            true
421        } else {
422            false
423        };
424        let name = self.expect_identifier()?;
425        Ok(Statement::DropTable(chryso_core::ast::DropTableStatement {
426            name,
427            if_exists,
428        }))
429    }
430
431    fn parse_truncate_statement(&mut self) -> ChrysoResult<Statement> {
432        let _ = self.consume_keyword(Keyword::Table);
433        let name = self.expect_identifier()?;
434        Ok(Statement::Truncate(chryso_core::ast::TruncateStatement {
435            table: name,
436        }))
437    }
438
439    fn parse_analyze_statement(&mut self) -> ChrysoResult<Statement> {
440        let _ = self.consume_keyword(Keyword::Table);
441        let name = self.expect_identifier()?;
442        Ok(Statement::Analyze(chryso_core::ast::AnalyzeStatement { table: name }))
443    }
444
445    fn parse_insert_statement(&mut self) -> ChrysoResult<Statement> {
446        self.expect_keyword(Keyword::Into)?;
447        let table = self.expect_identifier()?;
448        let columns = if self.consume_token(&Token::LParen) {
449            let columns = self.parse_identifier_list()?;
450            self.expect_token(Token::RParen)?;
451            columns
452        } else {
453            Vec::new()
454        };
455        if self.consume_keyword(Keyword::Default) {
456            self.expect_keyword(Keyword::Values)?;
457            return Ok(Statement::Insert(chryso_core::ast::InsertStatement {
458                table,
459                columns,
460                source: chryso_core::ast::InsertSource::DefaultValues,
461                returning: self.parse_returning_clause()?,
462            }));
463        }
464        let source = if self.consume_keyword(Keyword::Values) {
465            let mut values = Vec::new();
466            loop {
467                self.expect_token(Token::LParen)?;
468                let row = self.parse_expr_list()?;
469                self.expect_token(Token::RParen)?;
470                values.push(row);
471                if !self.consume_token(&Token::Comma) {
472                    break;
473                }
474            }
475            chryso_core::ast::InsertSource::Values(values)
476        } else if self.consume_keyword(Keyword::Select) {
477            let select = self.parse_select()?;
478            let statement = self.parse_query_tail(select)?;
479            chryso_core::ast::InsertSource::Query(Box::new(statement))
480        } else if self.consume_keyword(Keyword::With) {
481            let statement = self.parse_with_statement()?;
482            chryso_core::ast::InsertSource::Query(Box::new(statement))
483        } else {
484            return Err(ChrysoError::new("INSERT expects VALUES or SELECT"));
485        };
486        Ok(Statement::Insert(chryso_core::ast::InsertStatement {
487            table,
488            columns,
489            source,
490            returning: self.parse_returning_clause()?,
491        }))
492    }
493
494    fn parse_update_statement(&mut self) -> ChrysoResult<Statement> {
495        let table = self.expect_identifier()?;
496        self.expect_keyword(Keyword::Set)?;
497        let assignments = self.parse_assignments()?;
498        let selection = if self.consume_keyword(Keyword::Where) {
499            Some(self.parse_expr()?)
500        } else {
501            None
502        };
503        Ok(Statement::Update(chryso_core::ast::UpdateStatement {
504            table,
505            assignments,
506            selection,
507            returning: self.parse_returning_clause()?,
508        }))
509    }
510
511    fn parse_delete_statement(&mut self) -> ChrysoResult<Statement> {
512        self.expect_keyword(Keyword::From)?;
513        let table = self.expect_identifier()?;
514        let selection = if self.consume_keyword(Keyword::Where) {
515            Some(self.parse_expr()?)
516        } else {
517            None
518        };
519        Ok(Statement::Delete(chryso_core::ast::DeleteStatement {
520            table,
521            selection,
522            returning: self.parse_returning_clause()?,
523        }))
524    }
525
526    fn parse_select(&mut self) -> ChrysoResult<SelectStatement> {
527        let distinct = self.consume_keyword(Keyword::Distinct);
528        let distinct_on = if distinct && self.consume_keyword(Keyword::On) {
529            self.expect_token(Token::LParen)?;
530            let exprs = self.parse_expr_list()?;
531            self.expect_token(Token::RParen)?;
532            exprs
533        } else {
534            Vec::new()
535        };
536        let projection = self.parse_projection()?;
537        let from = if self.consume_keyword(Keyword::From) {
538            Some(self.parse_table_ref()?)
539        } else {
540            None
541        };
542        let selection = if self.consume_keyword(Keyword::Where) {
543            Some(self.parse_expr()?)
544        } else {
545            None
546        };
547        let group_by = if self.consume_keyword(Keyword::Group) {
548            self.expect_keyword(Keyword::By)?;
549            self.parse_expr_list()?
550        } else {
551            Vec::new()
552        };
553        let having = if self.consume_keyword(Keyword::Having) {
554            Some(self.parse_expr()?)
555        } else {
556            None
557        };
558        let order_by = if self.consume_keyword(Keyword::Order) {
559            self.expect_keyword(Keyword::By)?;
560            self.parse_order_by_list()?
561        } else {
562            Vec::new()
563        };
564        let limit = if self.consume_keyword(Keyword::Limit) {
565            Some(self.parse_limit_value()?)
566        } else {
567            None
568        };
569        let offset = if self.consume_keyword(Keyword::Offset) {
570            Some(self.parse_limit_value()?)
571        } else {
572            None
573        };
574        Ok(SelectStatement {
575            distinct,
576            distinct_on,
577            projection,
578            from,
579            selection,
580            group_by,
581            having,
582            order_by,
583            limit,
584            offset,
585        })
586    }
587
588    fn parse_projection(&mut self) -> ChrysoResult<Vec<SelectItem>> {
589        let mut items = Vec::new();
590        loop {
591            let expr = if self.consume_token(&Token::Star) {
592                Expr::Wildcard
593            } else {
594                self.parse_expr()?
595            };
596            let alias = if self.consume_keyword(Keyword::As) {
597                Some(self.expect_identifier()?)
598            } else if let Some(Token::Ident(name)) = self.peek().cloned() {
599                if !self.is_clause_boundary() {
600                    self.next();
601                    Some(name)
602                } else {
603                    None
604                }
605            } else {
606                None
607            };
608            items.push(SelectItem { expr, alias });
609            if !self.consume_token(&Token::Comma) {
610                break;
611            }
612        }
613        Ok(items)
614    }
615
616    fn parse_returning_clause(&mut self) -> ChrysoResult<Vec<SelectItem>> {
617        if self.consume_keyword(Keyword::Returning) {
618            let mut items = Vec::new();
619            loop {
620                let expr = if self.consume_token(&Token::Star) {
621                    Expr::Wildcard
622                } else {
623                    self.parse_expr()?
624                };
625                let alias = if self.consume_keyword(Keyword::As) {
626                    Some(self.expect_identifier()?)
627                } else if let Some(Token::Ident(name)) = self.peek().cloned() {
628                    if !self.is_clause_boundary() {
629                        self.next();
630                        Some(name)
631                    } else {
632                        None
633                    }
634                } else {
635                    None
636                };
637                items.push(SelectItem { expr, alias });
638                if !self.consume_token(&Token::Comma) {
639                    break;
640                }
641            }
642            Ok(items)
643        } else {
644            Ok(Vec::new())
645        }
646    }
647
648    fn parse_table_ref(&mut self) -> ChrysoResult<TableRef> {
649        let factor = self.parse_table_factor()?;
650        let alias = if self.consume_keyword(Keyword::As) {
651            Some(self.expect_identifier()?)
652        } else if let Some(Token::Ident(_)) = self.peek() {
653            if !self.is_join_boundary() {
654                Some(self.expect_identifier()?)
655            } else {
656                None
657            }
658        } else {
659            None
660        };
661        let column_aliases = if self.consume_token(&Token::LParen) {
662            if alias.is_none() {
663                return Err(ChrysoError::new("table alias list requires alias"));
664            }
665            let columns = self.parse_identifier_list()?;
666            self.expect_token(Token::RParen)?;
667            columns
668        } else {
669            Vec::new()
670        };
671        if matches!(factor, chryso_core::ast::TableFactor::Derived { .. }) && alias.is_none()
672        {
673            return Err(ChrysoError::new("subquery in FROM requires alias"));
674        }
675        let mut table = TableRef {
676            factor,
677            alias,
678            column_aliases,
679            joins: Vec::new(),
680        };
681        loop {
682            let mut cross_join = false;
683            let mut natural_join = false;
684            let join_type = if self.consume_keyword(Keyword::Join) {
685                JoinType::Inner
686            } else if self.consume_keyword(Keyword::Cross) {
687                self.expect_keyword(Keyword::Join)?;
688                cross_join = true;
689                JoinType::Inner
690            } else if self.consume_token(&Token::Comma) {
691                cross_join = true;
692                JoinType::Inner
693            } else if self.consume_keyword(Keyword::Natural) {
694                natural_join = true;
695                if self.consume_keyword(Keyword::Left) {
696                    self.expect_keyword(Keyword::Join)?;
697                    JoinType::Left
698                } else if self.consume_keyword(Keyword::Right) {
699                    self.expect_keyword(Keyword::Join)?;
700                    JoinType::Right
701                } else if self.consume_keyword(Keyword::Full) {
702                    self.expect_keyword(Keyword::Join)?;
703                    JoinType::Full
704                } else if self.consume_keyword(Keyword::Join) {
705                    JoinType::Inner
706                } else {
707                    return Err(ChrysoError::new("NATURAL expects JOIN"));
708                }
709            } else if self.consume_keyword(Keyword::Left) {
710                self.expect_keyword(Keyword::Join)?;
711                JoinType::Left
712            } else if self.consume_keyword(Keyword::Right) {
713                self.expect_keyword(Keyword::Join)?;
714                JoinType::Right
715            } else if self.consume_keyword(Keyword::Full) {
716                self.expect_keyword(Keyword::Join)?;
717                JoinType::Full
718            } else {
719                break;
720            };
721            let right = self.parse_table_ref()?;
722            let on = if cross_join || natural_join {
723                if self.peek_is_keyword(Keyword::On) || self.peek_is_keyword(Keyword::Using) {
724                    return Err(ChrysoError::new(
725                        "NATURAL/CROSS JOIN cannot use ON or USING",
726                    ));
727                }
728                Expr::Literal(Literal::Bool(true))
729            } else if self.consume_keyword(Keyword::On) {
730                self.parse_expr()?
731            } else if self.consume_keyword(Keyword::Using) {
732                let left_name = table_ref_name(&table)?;
733                let right_name = table_ref_name(&right)?;
734                self.expect_token(Token::LParen)?;
735                let columns = self.parse_identifier_list()?;
736                self.expect_token(Token::RParen)?;
737                build_using_on(left_name.as_str(), right_name.as_str(), columns)
738            } else {
739                return Err(ChrysoError::new("JOIN expects ON or USING"));
740            };
741            table.joins.push(Join {
742                join_type,
743                right,
744                on,
745            });
746        }
747        Ok(table)
748    }
749
750    fn parse_table_factor(&mut self) -> ChrysoResult<chryso_core::ast::TableFactor> {
751        if self.consume_token(&Token::LParen) {
752            let statement = if self.consume_keyword(Keyword::With) {
753                self.parse_with_statement()?
754            } else if self.consume_keyword(Keyword::Select) {
755                let select = self.parse_select()?;
756                self.parse_query_tail(select)?
757            } else {
758                return Err(ChrysoError::new(
759                    "subquery in FROM expects SELECT or WITH",
760                ));
761            };
762            self.expect_token(Token::RParen)?;
763            return Ok(chryso_core::ast::TableFactor::Derived {
764                query: Box::new(statement),
765            });
766        }
767        let first = self.expect_identifier()?;
768        let name = self.parse_qualified_identifier_from(first)?;
769        Ok(chryso_core::ast::TableFactor::Table { name })
770    }
771
772    fn parse_order_by_list(&mut self) -> ChrysoResult<Vec<OrderByExpr>> {
773        let mut items = Vec::new();
774        loop {
775            let expr = self.parse_expr()?;
776            let asc = if self.consume_keyword(Keyword::Asc) {
777                true
778            } else if self.consume_keyword(Keyword::Desc) {
779                false
780            } else {
781                true
782            };
783            let nulls_first = if self.consume_keyword(Keyword::Nulls) {
784                if self.consume_keyword(Keyword::First) {
785                    Some(true)
786                } else if self.consume_keyword(Keyword::Last) {
787                    Some(false)
788                } else {
789                    return Err(ChrysoError::new("NULLS expects FIRST or LAST"));
790                }
791            } else {
792                None
793            };
794            items.push(OrderByExpr {
795                expr,
796                asc,
797                nulls_first,
798            });
799            if !self.consume_token(&Token::Comma) {
800                break;
801            }
802        }
803        Ok(items)
804    }
805
806    fn parse_limit_value(&mut self) -> ChrysoResult<u64> {
807        if let Some(Token::Number(value)) = self.next() {
808            value
809                .parse()
810                .map_err(|_| ChrysoError::new("invalid LIMIT value"))
811        } else {
812            Err(ChrysoError::new("LIMIT expects a number"))
813        }
814    }
815
816    fn parse_expr_list(&mut self) -> ChrysoResult<Vec<Expr>> {
817        let mut items = Vec::new();
818        loop {
819            items.push(self.parse_expr()?);
820            if !self.consume_token(&Token::Comma) {
821                break;
822            }
823        }
824        Ok(items)
825    }
826
827    fn parse_expr(&mut self) -> ChrysoResult<Expr> {
828        self.parse_or()
829    }
830
831    fn parse_or(&mut self) -> ChrysoResult<Expr> {
832        let mut expr = self.parse_and()?;
833        while self.consume_keyword(Keyword::Or) {
834            let rhs = self.parse_and()?;
835            expr = Expr::BinaryOp {
836                left: Box::new(expr),
837                op: BinaryOperator::Or,
838                right: Box::new(rhs),
839            };
840        }
841        Ok(expr)
842    }
843
844    fn parse_and(&mut self) -> ChrysoResult<Expr> {
845        let mut expr = self.parse_comparison()?;
846        while self.consume_keyword(Keyword::And) {
847            let rhs = self.parse_comparison()?;
848            expr = Expr::BinaryOp {
849                left: Box::new(expr),
850                op: BinaryOperator::And,
851                right: Box::new(rhs),
852            };
853        }
854        Ok(expr)
855    }
856
857    fn parse_comparison(&mut self) -> ChrysoResult<Expr> {
858        let mut expr = self.parse_additive()?;
859        loop {
860            if self.peek_is_keyword(Keyword::Not) && self.peek_is_keyword_n(1, Keyword::Between) {
861                self.next();
862                self.next();
863                let low = self.parse_additive()?;
864                self.expect_keyword(Keyword::And)?;
865                let high = self.parse_additive()?;
866                return Ok(Expr::BinaryOp {
867                    left: Box::new(Expr::BinaryOp {
868                        left: Box::new(expr.clone()),
869                        op: BinaryOperator::Lt,
870                        right: Box::new(low),
871                    }),
872                    op: BinaryOperator::Or,
873                    right: Box::new(Expr::BinaryOp {
874                        left: Box::new(expr),
875                        op: BinaryOperator::Gt,
876                        right: Box::new(high),
877                    }),
878                });
879            }
880            if self.peek_is_keyword(Keyword::Not) && self.peek_is_keyword_n(1, Keyword::In) {
881                self.next();
882                self.next();
883                let in_expr = self.parse_in_payload(expr)?;
884                return Ok(Expr::UnaryOp {
885                    op: UnaryOperator::Not,
886                    expr: Box::new(in_expr),
887                });
888            }
889            if self.peek_is_keyword(Keyword::Not) && self.peek_is_keyword_n(1, Keyword::Like) {
890                self.next();
891                self.next();
892                let like_expr = self.parse_like_payload(expr, "like")?;
893                return Ok(Expr::UnaryOp {
894                    op: UnaryOperator::Not,
895                    expr: Box::new(like_expr),
896                });
897            }
898            if self.peek_is_keyword(Keyword::Not) && self.peek_is_keyword_n(1, Keyword::ILike) {
899                self.next();
900                self.next();
901                let like_expr = self.parse_like_payload(expr, "ilike")?;
902                return Ok(Expr::UnaryOp {
903                    op: UnaryOperator::Not,
904                    expr: Box::new(like_expr),
905                });
906            }
907            if self.consume_keyword(Keyword::Between) {
908                let low = self.parse_additive()?;
909                self.expect_keyword(Keyword::And)?;
910                let high = self.parse_additive()?;
911                return Ok(Expr::BinaryOp {
912                    left: Box::new(Expr::BinaryOp {
913                        left: Box::new(expr.clone()),
914                        op: BinaryOperator::GtEq,
915                        right: Box::new(low),
916                    }),
917                    op: BinaryOperator::And,
918                    right: Box::new(Expr::BinaryOp {
919                        left: Box::new(expr),
920                        op: BinaryOperator::LtEq,
921                        right: Box::new(high),
922                    }),
923                });
924            }
925            let op = if self.consume_token(&Token::Eq) {
926                Some(BinaryOperator::Eq)
927            } else if self.consume_token(&Token::NotEq) {
928                Some(BinaryOperator::NotEq)
929            } else if self.consume_token(&Token::LtEq) {
930                Some(BinaryOperator::LtEq)
931            } else if self.consume_token(&Token::Lt) {
932                Some(BinaryOperator::Lt)
933            } else if self.consume_token(&Token::GtEq) {
934                Some(BinaryOperator::GtEq)
935            } else if self.consume_token(&Token::Gt) {
936                Some(BinaryOperator::Gt)
937            } else if self.consume_keyword(Keyword::In) {
938                return Ok(self.parse_in_payload(expr)?);
939            } else if self.consume_keyword(Keyword::Is) {
940                let negated = self.consume_keyword(Keyword::Not);
941                self.expect_keyword(Keyword::Null)?;
942                return Ok(Expr::IsNull {
943                    expr: Box::new(expr),
944                    negated,
945                });
946            } else if self.consume_keyword(Keyword::Like) {
947                return Ok(self.parse_like_payload(expr, "like")?);
948            } else if self.consume_keyword(Keyword::ILike) {
949                return Ok(self.parse_like_payload(expr, "ilike")?);
950            } else {
951                None
952            };
953            let Some(op) = op else {
954                break;
955            };
956            let rhs = self.parse_additive()?;
957            expr = Expr::BinaryOp {
958                left: Box::new(expr),
959                op,
960                right: Box::new(rhs),
961            };
962        }
963        Ok(expr)
964    }
965
966    fn parse_in_payload(&mut self, expr: Expr) -> ChrysoResult<Expr> {
967        self.expect_token(Token::LParen)?;
968        if self.peek_is_keyword(Keyword::Select) {
969            let subquery = self.parse_subquery_select_after_lparen()?;
970            return Ok(Expr::InSubquery {
971                expr: Box::new(expr),
972                subquery: Box::new(subquery),
973            });
974        }
975        let list = self.parse_expr_list_in_parens()?;
976        Ok(rewrite_in_list(expr, list))
977    }
978
979    fn parse_like_payload(&mut self, expr: Expr, name: &str) -> ChrysoResult<Expr> {
980        let pattern = self.parse_additive()?;
981        let mut args = vec![expr, pattern];
982        if self.consume_keyword(Keyword::Escape) {
983            let escape = self.parse_additive()?;
984            args.push(escape);
985        }
986        Ok(Expr::FunctionCall {
987            name: name.to_string(),
988            args,
989        })
990    }
991
992    fn parse_expr_list_in_parens(&mut self) -> ChrysoResult<Vec<Expr>> {
993        let mut items = Vec::new();
994        if self.consume_token(&Token::RParen) {
995            return Err(ChrysoError::new("IN list cannot be empty"));
996        }
997        loop {
998            items.push(self.parse_expr()?);
999            if self.consume_token(&Token::Comma) {
1000                continue;
1001            }
1002            self.expect_token(Token::RParen)?;
1003            break;
1004        }
1005        Ok(items)
1006    }
1007
1008    fn parse_additive(&mut self) -> ChrysoResult<Expr> {
1009        let mut expr = self.parse_multiplicative()?;
1010        loop {
1011            let op = if self.consume_token(&Token::Plus) {
1012                Some(BinaryOperator::Add)
1013            } else if self.consume_token(&Token::Minus) {
1014                Some(BinaryOperator::Sub)
1015            } else {
1016                None
1017            };
1018            let Some(op) = op else {
1019                break;
1020            };
1021            let rhs = self.parse_multiplicative()?;
1022            expr = Expr::BinaryOp {
1023                left: Box::new(expr),
1024                op,
1025                right: Box::new(rhs),
1026            };
1027        }
1028        Ok(expr)
1029    }
1030
1031    fn parse_multiplicative(&mut self) -> ChrysoResult<Expr> {
1032        let mut expr = self.parse_unary()?;
1033        loop {
1034            let op = if self.consume_token(&Token::Star) {
1035                Some(BinaryOperator::Mul)
1036            } else if self.consume_token(&Token::Slash) {
1037                Some(BinaryOperator::Div)
1038            } else {
1039                None
1040            };
1041            let Some(op) = op else {
1042                break;
1043            };
1044            let rhs = self.parse_unary()?;
1045            expr = Expr::BinaryOp {
1046                left: Box::new(expr),
1047                op,
1048                right: Box::new(rhs),
1049            };
1050        }
1051        Ok(expr)
1052    }
1053
1054    fn parse_unary(&mut self) -> ChrysoResult<Expr> {
1055        if self.consume_keyword(Keyword::Exists) {
1056            let subquery = self.parse_subquery_select()?;
1057            return Ok(Expr::Exists(Box::new(subquery)));
1058        }
1059        if self.consume_keyword(Keyword::Not) {
1060            let expr = self.parse_unary()?;
1061            return Ok(Expr::UnaryOp {
1062                op: UnaryOperator::Not,
1063                expr: Box::new(expr),
1064            });
1065        }
1066        if self.consume_token(&Token::Minus) {
1067            let expr = self.parse_unary()?;
1068            return Ok(Expr::UnaryOp {
1069                op: UnaryOperator::Neg,
1070                expr: Box::new(expr),
1071            });
1072        }
1073        self.parse_primary()
1074    }
1075
1076    fn parse_primary(&mut self) -> ChrysoResult<Expr> {
1077        match self.next() {
1078            Some(Token::Ident(name)) => {
1079                if self.consume_token(&Token::LParen) {
1080                    let args = if self.consume_token(&Token::RParen) {
1081                        Vec::new()
1082                    } else {
1083                        let args = self.parse_expr_list()?;
1084                        self.expect_token(Token::RParen)?;
1085                        args
1086                    };
1087                    let function = Expr::FunctionCall { name, args };
1088                    if self.consume_keyword(Keyword::Over) {
1089                        let spec = self.parse_window_spec()?;
1090                        Ok(Expr::WindowFunction {
1091                            function: Box::new(function),
1092                            spec,
1093                        })
1094                    } else {
1095                        Ok(function)
1096                    }
1097                } else {
1098                    Ok(Expr::Identifier(self.parse_qualified_identifier_from(name)?))
1099                }
1100            }
1101            Some(Token::Keyword(Keyword::True)) => Ok(Expr::Literal(Literal::Bool(true))),
1102            Some(Token::Keyword(Keyword::False)) => Ok(Expr::Literal(Literal::Bool(false))),
1103            Some(Token::Number(value)) => Ok(Expr::Literal(Literal::Number(
1104                value
1105                    .parse()
1106                    .map_err(|_| ChrysoError::new("invalid number"))?,
1107            ))),
1108            Some(Token::String(value)) => Ok(Expr::Literal(Literal::String(value))),
1109            Some(Token::Keyword(Keyword::Case)) => self.parse_case_expr(),
1110            Some(Token::Star) => Ok(Expr::Wildcard),
1111            Some(Token::LParen) => {
1112                if self.peek_is_keyword(Keyword::Select) {
1113                    let select = self.parse_subquery_select_after_lparen()?;
1114                    Ok(Expr::Subquery(Box::new(select)))
1115                } else {
1116                    let expr = self.parse_expr()?;
1117                    self.expect_token(Token::RParen)?;
1118                    Ok(expr)
1119                }
1120            }
1121            _ => Err(ChrysoError::new("unexpected token in expression")),
1122        }
1123    }
1124
1125    fn parse_case_expr(&mut self) -> ChrysoResult<Expr> {
1126        if self.peek_is_keyword(Keyword::End) {
1127            return Err(ChrysoError::new("CASE expects at least one WHEN"));
1128        }
1129        let operand = if self.peek_is_keyword(Keyword::When) {
1130            None
1131        } else {
1132            Some(Box::new(self.parse_expr()?))
1133        };
1134        let mut when_then = Vec::new();
1135        loop {
1136            if !self.consume_keyword(Keyword::When) {
1137                break;
1138            }
1139            let when_expr = self.parse_expr()?;
1140            self.expect_keyword(Keyword::Then)?;
1141            let then_expr = self.parse_expr()?;
1142            when_then.push((when_expr, then_expr));
1143        }
1144        if when_then.is_empty() {
1145            return Err(ChrysoError::new("CASE expects at least one WHEN"));
1146        }
1147        let else_expr = if self.consume_keyword(Keyword::Else) {
1148            Some(Box::new(self.parse_expr()?))
1149        } else {
1150            None
1151        };
1152        self.expect_keyword(Keyword::End)?;
1153        Ok(Expr::Case {
1154            operand,
1155            when_then,
1156            else_expr,
1157        })
1158    }
1159
1160    fn expect_keyword(&mut self, keyword: Keyword) -> ChrysoResult<()> {
1161        if self.consume_keyword(keyword) {
1162            Ok(())
1163        } else {
1164            let found = self.peek().map(token_label).unwrap_or_else(|| "end of input".to_string());
1165            Err(ChrysoError::new(format!(
1166                "expected keyword {} but found {found}",
1167                keyword_label(keyword)
1168            )))
1169        }
1170    }
1171
1172    fn parse_subquery_select(&mut self) -> ChrysoResult<SelectStatement> {
1173        self.expect_token(Token::LParen)?;
1174        self.expect_keyword(Keyword::Select)?;
1175        let select = self.parse_select()?;
1176        self.expect_token(Token::RParen)?;
1177        Ok(select)
1178    }
1179
1180    fn parse_subquery_select_after_lparen(&mut self) -> ChrysoResult<SelectStatement> {
1181        self.expect_keyword(Keyword::Select)?;
1182        let select = self.parse_select()?;
1183        self.expect_token(Token::RParen)?;
1184        Ok(select)
1185    }
1186
1187    fn parse_window_spec(&mut self) -> ChrysoResult<chryso_core::ast::WindowSpec> {
1188        self.expect_token(Token::LParen)?;
1189        let mut partition_by = Vec::new();
1190        let mut order_by = Vec::new();
1191        if self.consume_keyword(Keyword::Partition) {
1192            self.expect_keyword(Keyword::By)?;
1193            partition_by = self.parse_expr_list()?;
1194        }
1195        if self.consume_keyword(Keyword::Order) {
1196            self.expect_keyword(Keyword::By)?;
1197            order_by = self.parse_order_by_list()?;
1198        }
1199        self.expect_token(Token::RParen)?;
1200        Ok(chryso_core::ast::WindowSpec {
1201            partition_by,
1202            order_by,
1203        })
1204    }
1205
1206    fn parse_identifier_list(&mut self) -> ChrysoResult<Vec<String>> {
1207        let mut items = Vec::new();
1208        loop {
1209            items.push(self.expect_identifier()?);
1210            if !self.consume_token(&Token::Comma) {
1211                break;
1212            }
1213        }
1214        Ok(items)
1215    }
1216
1217    fn parse_assignments(&mut self) -> ChrysoResult<Vec<chryso_core::ast::Assignment>> {
1218        let mut items = Vec::new();
1219        loop {
1220            let column = self.expect_identifier()?;
1221            self.expect_token(Token::Eq)?;
1222            let value = self.parse_expr()?;
1223            items.push(chryso_core::ast::Assignment { column, value });
1224            if !self.consume_token(&Token::Comma) {
1225                break;
1226            }
1227        }
1228        Ok(items)
1229    }
1230
1231    fn consume_keyword(&mut self, keyword: Keyword) -> bool {
1232        match self.peek() {
1233            Some(Token::Keyword(kw)) if *kw == keyword => {
1234                self.pos += 1;
1235                true
1236            }
1237            _ => false,
1238        }
1239    }
1240
1241    fn peek_is_keyword(&self, keyword: Keyword) -> bool {
1242        matches!(self.peek(), Some(Token::Keyword(kw)) if *kw == keyword)
1243    }
1244
1245    fn consume_token(&mut self, token: &Token) -> bool {
1246        match self.peek() {
1247            Some(next) if next == token => {
1248                self.pos += 1;
1249                true
1250            }
1251            _ => false,
1252        }
1253    }
1254
1255    fn expect_token(&mut self, token: Token) -> ChrysoResult<()> {
1256        if self.consume_token(&token) {
1257            Ok(())
1258        } else {
1259            let found = self.peek().map(token_label).unwrap_or_else(|| "end of input".to_string());
1260            Err(ChrysoError::new(format!(
1261                "expected token {} but found {found}",
1262                token_label(&token)
1263            )))
1264        }
1265    }
1266
1267    fn expect_identifier(&mut self) -> ChrysoResult<String> {
1268        match self.next() {
1269            Some(Token::Ident(name)) => Ok(name),
1270            other => Err(ChrysoError::new(format!(
1271                "expected identifier but found {}",
1272                other.as_ref().map(token_label).unwrap_or_else(|| "end of input".to_string())
1273            ))),
1274        }
1275    }
1276
1277    fn parse_qualified_identifier_from(&mut self, first: String) -> ChrysoResult<String> {
1278        let mut parts = vec![first];
1279        while self.consume_token(&Token::Dot) {
1280            if self.consume_token(&Token::Star) {
1281                parts.push("*".to_string());
1282                break;
1283            }
1284            parts.push(self.expect_identifier()?);
1285        }
1286        Ok(parts.join("."))
1287    }
1288
1289    fn is_clause_boundary(&self) -> bool {
1290        matches!(
1291            self.peek(),
1292            Some(Token::Keyword(
1293                Keyword::From
1294                    | Keyword::Where
1295                    | Keyword::Group
1296                    | Keyword::Having
1297                    | Keyword::Order
1298                    | Keyword::Offset
1299                    | Keyword::Limit
1300                    | Keyword::Join
1301                    | Keyword::Left
1302                    | Keyword::Cross
1303                    | Keyword::Natural
1304                    | Keyword::Right
1305                    | Keyword::Full
1306            )) | Some(Token::Comma)
1307        )
1308    }
1309
1310    fn is_join_boundary(&self) -> bool {
1311        matches!(
1312            self.peek(),
1313            Some(Token::Keyword(
1314                Keyword::Join
1315                    | Keyword::Left
1316                    | Keyword::Cross
1317                    | Keyword::Natural
1318                    | Keyword::Right
1319                    | Keyword::Full
1320                    | Keyword::Where
1321                    | Keyword::Group
1322                    | Keyword::Having
1323                    | Keyword::Order
1324                    | Keyword::Offset
1325                    | Keyword::Limit
1326            )) | Some(Token::Comma)
1327        )
1328    }
1329
1330    fn peek(&self) -> Option<&Token> {
1331        self.tokens.get(self.pos)
1332    }
1333
1334    fn peek_is_keyword_n(&self, offset: usize, keyword: Keyword) -> bool {
1335        matches!(
1336            self.tokens.get(self.pos + offset),
1337            Some(Token::Keyword(kw)) if *kw == keyword
1338        )
1339    }
1340
1341    fn next(&mut self) -> Option<Token> {
1342        if self.pos >= self.tokens.len() {
1343            None
1344        } else {
1345            let token = self.tokens[self.pos].clone();
1346            self.pos += 1;
1347            Some(token)
1348        }
1349    }
1350}
1351
1352fn rewrite_in_list(expr: Expr, list: Vec<Expr>) -> Expr {
1353    let mut iter = list.into_iter();
1354    let first = iter.next().expect("in list should be non-empty");
1355    let mut combined = Expr::BinaryOp {
1356        left: Box::new(expr.clone()),
1357        op: BinaryOperator::Eq,
1358        right: Box::new(first),
1359    };
1360    for item in iter {
1361        combined = Expr::BinaryOp {
1362            left: Box::new(combined),
1363            op: BinaryOperator::Or,
1364            right: Box::new(Expr::BinaryOp {
1365                left: Box::new(expr.clone()),
1366                op: BinaryOperator::Eq,
1367                right: Box::new(item),
1368            }),
1369        };
1370    }
1371    combined
1372}
1373
1374fn table_ref_name(table: &TableRef) -> ChrysoResult<String> {
1375    if let Some(alias) = &table.alias {
1376        return Ok(alias.clone());
1377    }
1378    match &table.factor {
1379        chryso_core::ast::TableFactor::Table { name } => Ok(name.clone()),
1380        chryso_core::ast::TableFactor::Derived { .. } => {
1381            Err(ChrysoError::new("subquery in FROM requires alias"))
1382        }
1383    }
1384}
1385
1386fn build_using_on(left_name: &str, right_name: &str, columns: Vec<String>) -> Expr {
1387    let mut iter = columns.into_iter();
1388    let first = iter.next().expect("using columns should be non-empty");
1389    let mut expr = Expr::BinaryOp {
1390        left: Box::new(Expr::Identifier(format!("{left_name}.{first}"))),
1391        op: BinaryOperator::Eq,
1392        right: Box::new(Expr::Identifier(format!("{right_name}.{first}"))),
1393    };
1394    for column in iter {
1395        let next = Expr::BinaryOp {
1396            left: Box::new(Expr::Identifier(format!("{left_name}.{column}"))),
1397            op: BinaryOperator::Eq,
1398            right: Box::new(Expr::Identifier(format!("{right_name}.{column}"))),
1399        };
1400        expr = Expr::BinaryOp {
1401            left: Box::new(expr),
1402            op: BinaryOperator::And,
1403            right: Box::new(next),
1404        };
1405    }
1406    expr
1407}
1408
1409fn token_label(token: &Token) -> String {
1410    match token {
1411        Token::Ident(value) => format!("identifier({value})"),
1412        Token::Number(value) => format!("number({value})"),
1413        Token::String(value) => format!("string('{value}')"),
1414        Token::Comma => ",".to_string(),
1415        Token::Dot => ".".to_string(),
1416        Token::Star => "*".to_string(),
1417        Token::LParen => "(".to_string(),
1418        Token::RParen => ")".to_string(),
1419        Token::Eq => "=".to_string(),
1420        Token::NotEq => "!=".to_string(),
1421        Token::Lt => "<".to_string(),
1422        Token::LtEq => "<=".to_string(),
1423        Token::Gt => ">".to_string(),
1424        Token::GtEq => ">=".to_string(),
1425        Token::Plus => "+".to_string(),
1426        Token::Minus => "-".to_string(),
1427        Token::Slash => "/".to_string(),
1428        Token::Keyword(keyword) => keyword_label(*keyword).to_string(),
1429    }
1430}
1431
1432fn keyword_label(keyword: Keyword) -> &'static str {
1433    match keyword {
1434        Keyword::Select => "select",
1435        Keyword::Explain => "explain",
1436        Keyword::Create => "create",
1437        Keyword::Drop => "drop",
1438        Keyword::Truncate => "truncate",
1439        Keyword::If => "if",
1440        Keyword::Table => "table",
1441        Keyword::Insert => "insert",
1442        Keyword::Into => "into",
1443        Keyword::Values => "values",
1444        Keyword::Default => "default",
1445        Keyword::Update => "update",
1446        Keyword::Set => "set",
1447        Keyword::Delete => "delete",
1448        Keyword::From => "from",
1449        Keyword::Where => "where",
1450        Keyword::And => "and",
1451        Keyword::Or => "or",
1452        Keyword::Not => "not",
1453        Keyword::As => "as",
1454        Keyword::Join => "join",
1455        Keyword::Cross => "cross",
1456        Keyword::Natural => "natural",
1457        Keyword::Left => "left",
1458        Keyword::Right => "right",
1459        Keyword::Full => "full",
1460        Keyword::On => "on",
1461        Keyword::Group => "group",
1462        Keyword::By => "by",
1463        Keyword::Having => "having",
1464        Keyword::Order => "order",
1465        Keyword::Offset => "offset",
1466        Keyword::Limit => "limit",
1467        Keyword::Asc => "asc",
1468        Keyword::Desc => "desc",
1469        Keyword::Distinct => "distinct",
1470        Keyword::Union => "union",
1471        Keyword::All => "all",
1472        Keyword::Intersect => "intersect",
1473        Keyword::Except => "except",
1474        Keyword::With => "with",
1475        Keyword::Recursive => "recursive",
1476        Keyword::Returning => "returning",
1477        Keyword::Analyze => "analyze",
1478        Keyword::Over => "over",
1479        Keyword::Partition => "partition",
1480        Keyword::Exists => "exists",
1481        Keyword::In => "in",
1482        Keyword::True => "true",
1483        Keyword::False => "false",
1484        Keyword::Is => "is",
1485        Keyword::Null => "null",
1486        Keyword::Between => "between",
1487        Keyword::Like => "like",
1488        Keyword::ILike => "ilike",
1489        Keyword::Using => "using",
1490        Keyword::Case => "case",
1491        Keyword::When => "when",
1492        Keyword::Then => "then",
1493        Keyword::Else => "else",
1494        Keyword::End => "end",
1495        Keyword::Nulls => "nulls",
1496        Keyword::First => "first",
1497        Keyword::Last => "last",
1498        Keyword::Escape => "escape",
1499    }
1500}
1501
1502fn tokenize(input: &str, _dialect: Dialect) -> ChrysoResult<Vec<Token>> {
1503    let mut tokens = Vec::new();
1504    let chars: Vec<char> = input.trim().chars().collect();
1505    let mut index = 0;
1506    while index < chars.len() {
1507        let c = chars[index];
1508        if c.is_whitespace() {
1509            index += 1;
1510            continue;
1511        }
1512        if c == ',' {
1513            tokens.push(Token::Comma);
1514            index += 1;
1515            continue;
1516        }
1517        if c == '.' {
1518            tokens.push(Token::Dot);
1519            index += 1;
1520            continue;
1521        }
1522        if c == '(' {
1523            tokens.push(Token::LParen);
1524            index += 1;
1525            continue;
1526        }
1527        if c == ')' {
1528            tokens.push(Token::RParen);
1529            index += 1;
1530            continue;
1531        }
1532        if c == '*' {
1533            tokens.push(Token::Star);
1534            index += 1;
1535            continue;
1536        }
1537        if c == '+' {
1538            tokens.push(Token::Plus);
1539            index += 1;
1540            continue;
1541        }
1542        if c == '-' {
1543            tokens.push(Token::Minus);
1544            index += 1;
1545            continue;
1546        }
1547        if c == '/' {
1548            tokens.push(Token::Slash);
1549            index += 1;
1550            continue;
1551        }
1552        if c == '=' {
1553            tokens.push(Token::Eq);
1554            index += 1;
1555            continue;
1556        }
1557        if c == '!' && index + 1 < chars.len() && chars[index + 1] == '=' {
1558            tokens.push(Token::NotEq);
1559            index += 2;
1560            continue;
1561        }
1562        if c == '<' {
1563            if index + 1 < chars.len() && chars[index + 1] == '=' {
1564                tokens.push(Token::LtEq);
1565                index += 2;
1566            } else {
1567                tokens.push(Token::Lt);
1568                index += 1;
1569            }
1570            continue;
1571        }
1572        if c == '>' {
1573            if index + 1 < chars.len() && chars[index + 1] == '=' {
1574                tokens.push(Token::GtEq);
1575                index += 2;
1576            } else {
1577                tokens.push(Token::Gt);
1578                index += 1;
1579            }
1580            continue;
1581        }
1582        if c == '\'' {
1583            let start = index;
1584            let mut end = index + 1;
1585            while end < chars.len() && chars[end] != '\'' {
1586                end += 1;
1587            }
1588            if end >= chars.len() {
1589                return Err(ChrysoError::with_span(
1590                    "unterminated string literal",
1591                    chryso_core::error::Span { start, end },
1592                )
1593                .with_code(chryso_core::error::ErrorCode::ParserError));
1594            }
1595            let value: String = chars[index + 1..end].iter().collect();
1596            tokens.push(Token::String(value));
1597            index = end + 1;
1598            continue;
1599        }
1600        if c == '"' || c == '`' {
1601            let quote = c;
1602            let start = index;
1603            let mut end = index + 1;
1604            while end < chars.len() && chars[end] != quote {
1605                end += 1;
1606            }
1607            if end >= chars.len() {
1608                return Err(ChrysoError::with_span(
1609                    "unterminated quoted identifier",
1610                    chryso_core::error::Span { start, end },
1611                )
1612                .with_code(chryso_core::error::ErrorCode::ParserError));
1613            }
1614            let value: String = chars[index + 1..end].iter().collect();
1615            tokens.push(Token::Ident(value));
1616            index = end + 1;
1617            continue;
1618        }
1619        if c.is_ascii_digit() {
1620            let mut end = index + 1;
1621            while end < chars.len() && (chars[end].is_ascii_digit() || chars[end] == '.') {
1622                end += 1;
1623            }
1624            let value: String = chars[index..end].iter().collect();
1625            tokens.push(Token::Number(value));
1626            index = end;
1627            continue;
1628        }
1629        if is_ident_start(c) {
1630            let mut end = index + 1;
1631            while end < chars.len() && is_ident_part(chars[end]) {
1632                end += 1;
1633            }
1634            let raw: String = chars[index..end].iter().collect();
1635            if let Some(keyword) = keyword_from(&raw) {
1636                tokens.push(Token::Keyword(keyword));
1637            } else {
1638                tokens.push(Token::Ident(raw));
1639            }
1640            index = end;
1641            continue;
1642        }
1643        if c == ';' {
1644            index += 1;
1645            continue;
1646        }
1647        return Err(ChrysoError::with_span(
1648            "unsupported character in SQL",
1649            chryso_core::error::Span {
1650                start: index,
1651                end: index + 1,
1652            },
1653        )
1654        .with_code(chryso_core::error::ErrorCode::ParserError));
1655    }
1656    Ok(tokens)
1657}
1658
1659fn is_ident_start(c: char) -> bool {
1660    c.is_ascii_alphabetic() || c == '_'
1661}
1662
1663fn is_ident_part(c: char) -> bool {
1664    c.is_ascii_alphanumeric() || c == '_' || c == '$'
1665}
1666
1667fn keyword_from(raw: &str) -> Option<Keyword> {
1668    match raw.to_ascii_lowercase().as_str() {
1669        "select" => Some(Keyword::Select),
1670        "explain" => Some(Keyword::Explain),
1671        "create" => Some(Keyword::Create),
1672        "drop" => Some(Keyword::Drop),
1673        "truncate" => Some(Keyword::Truncate),
1674        "if" => Some(Keyword::If),
1675        "table" => Some(Keyword::Table),
1676        "analyze" => Some(Keyword::Analyze),
1677        "insert" => Some(Keyword::Insert),
1678        "into" => Some(Keyword::Into),
1679        "values" => Some(Keyword::Values),
1680        "default" => Some(Keyword::Default),
1681        "update" => Some(Keyword::Update),
1682        "set" => Some(Keyword::Set),
1683        "delete" => Some(Keyword::Delete),
1684        "over" => Some(Keyword::Over),
1685        "partition" => Some(Keyword::Partition),
1686        "exists" => Some(Keyword::Exists),
1687        "from" => Some(Keyword::From),
1688        "where" => Some(Keyword::Where),
1689        "and" => Some(Keyword::And),
1690        "or" => Some(Keyword::Or),
1691        "not" => Some(Keyword::Not),
1692        "as" => Some(Keyword::As),
1693        "join" => Some(Keyword::Join),
1694        "cross" => Some(Keyword::Cross),
1695        "natural" => Some(Keyword::Natural),
1696        "left" => Some(Keyword::Left),
1697        "right" => Some(Keyword::Right),
1698        "full" => Some(Keyword::Full),
1699        "on" => Some(Keyword::On),
1700        "group" => Some(Keyword::Group),
1701        "by" => Some(Keyword::By),
1702        "having" => Some(Keyword::Having),
1703        "order" => Some(Keyword::Order),
1704        "offset" => Some(Keyword::Offset),
1705        "limit" => Some(Keyword::Limit),
1706        "asc" => Some(Keyword::Asc),
1707        "desc" => Some(Keyword::Desc),
1708        "distinct" => Some(Keyword::Distinct),
1709        "union" => Some(Keyword::Union),
1710        "all" => Some(Keyword::All),
1711        "intersect" => Some(Keyword::Intersect),
1712        "except" => Some(Keyword::Except),
1713        "with" => Some(Keyword::With),
1714        "recursive" => Some(Keyword::Recursive),
1715        "returning" => Some(Keyword::Returning),
1716        "in" => Some(Keyword::In),
1717        "true" => Some(Keyword::True),
1718        "false" => Some(Keyword::False),
1719        "is" => Some(Keyword::Is),
1720        "null" => Some(Keyword::Null),
1721        "between" => Some(Keyword::Between),
1722        "like" => Some(Keyword::Like),
1723        "ilike" => Some(Keyword::ILike),
1724        "using" => Some(Keyword::Using),
1725        "case" => Some(Keyword::Case),
1726        "when" => Some(Keyword::When),
1727        "then" => Some(Keyword::Then),
1728        "else" => Some(Keyword::Else),
1729        "end" => Some(Keyword::End),
1730        "nulls" => Some(Keyword::Nulls),
1731        "first" => Some(Keyword::First),
1732        "last" => Some(Keyword::Last),
1733        "escape" => Some(Keyword::Escape),
1734        _ => None,
1735    }
1736}
1737
1738#[cfg(test)]
1739mod tests {
1740    use super::{Dialect, ParserConfig, SimpleParser, SqlParser};
1741    use chryso_core::ast::{
1742        BinaryOperator, Expr, InsertSource, JoinType, Literal, SelectStatement, Statement,
1743        TableFactor, TableRef, UnaryOperator,
1744    };
1745
1746    fn unwrap_from(select: &SelectStatement) -> &TableRef {
1747        select.from.as_ref().expect("expected from")
1748    }
1749
1750    #[test]
1751    fn parse_select_with_and_or() {
1752        let sql = "select id from users where id = 1 and name = 'alice' or age > 2";
1753        let parser = SimpleParser::new(ParserConfig {
1754            dialect: Dialect::Postgres,
1755        });
1756        let stmt = parser.parse(sql).expect("parse");
1757        let Statement::Select(select) = stmt else {
1758            panic!("expected select");
1759        };
1760        let Expr::BinaryOp { op, .. } = select.selection.expect("selection") else {
1761            panic!("expected binary op");
1762        };
1763        assert!(matches!(op, BinaryOperator::Or));
1764    }
1765
1766    #[test]
1767    fn parse_select_with_group_order_limit() {
1768        let sql = "select sum(amount) as total from sales group by region order by total desc limit 10";
1769        let parser = SimpleParser::new(ParserConfig {
1770            dialect: Dialect::Postgres,
1771        });
1772        let stmt = parser.parse(sql).expect("parse");
1773        let Statement::Select(select) = stmt else {
1774            panic!("expected select");
1775        };
1776        assert_eq!(select.group_by.len(), 1);
1777        assert_eq!(select.order_by.len(), 1);
1778        assert_eq!(select.limit, Some(10));
1779    }
1780
1781    #[test]
1782    fn parse_select_without_from() {
1783        let sql = "select 1 + 2";
1784        let parser = SimpleParser::new(ParserConfig {
1785            dialect: Dialect::Postgres,
1786        });
1787        let stmt = parser.parse(sql).expect("parse");
1788        let Statement::Select(select) = stmt else {
1789            panic!("expected select");
1790        };
1791        assert!(select.from.is_none());
1792    }
1793
1794    #[test]
1795    fn parse_select_without_from_with_where() {
1796        let sql = "select 1 where 1 = 1";
1797        let parser = SimpleParser::new(ParserConfig {
1798            dialect: Dialect::Postgres,
1799        });
1800        let stmt = parser.parse(sql).expect("parse");
1801        let Statement::Select(select) = stmt else {
1802            panic!("expected select");
1803        };
1804        assert!(select.from.is_none());
1805        assert!(select.selection.is_some());
1806    }
1807
1808    #[test]
1809    fn parse_select_without_from_with_group_order_limit() {
1810        let sql = "select 1 group by 1 order by 1 limit 2 offset 1";
1811        let parser = SimpleParser::new(ParserConfig {
1812            dialect: Dialect::Postgres,
1813        });
1814        let stmt = parser.parse(sql).expect("parse");
1815        let Statement::Select(select) = stmt else {
1816            panic!("expected select");
1817        };
1818        assert!(select.from.is_none());
1819        assert_eq!(select.group_by.len(), 1);
1820        assert_eq!(select.order_by.len(), 1);
1821        assert_eq!(select.limit, Some(2));
1822        assert_eq!(select.offset, Some(1));
1823    }
1824
1825    #[test]
1826    fn parse_select_with_distinct_offset() {
1827        let sql = "select distinct id from users order by id offset 5";
1828        let parser = SimpleParser::new(ParserConfig {
1829            dialect: Dialect::Postgres,
1830        });
1831        let stmt = parser.parse(sql).expect("parse");
1832        let Statement::Select(select) = stmt else {
1833            panic!("expected select");
1834        };
1835        assert!(select.distinct);
1836        assert!(select.distinct_on.is_empty());
1837        assert_eq!(select.offset, Some(5));
1838    }
1839
1840    #[test]
1841    fn parse_distinct_on() {
1842        let sql = "select distinct on (region) region, id from users";
1843        let parser = SimpleParser::new(ParserConfig {
1844            dialect: Dialect::Postgres,
1845        });
1846        let stmt = parser.parse(sql).expect("parse");
1847        let Statement::Select(select) = stmt else {
1848            panic!("expected select");
1849        };
1850        assert!(select.distinct);
1851        assert_eq!(select.distinct_on.len(), 1);
1852    }
1853
1854    #[test]
1855    fn parse_union_all() {
1856        let sql = "select id from t1 union all select id from t2";
1857        let parser = SimpleParser::new(ParserConfig {
1858            dialect: Dialect::Postgres,
1859        });
1860        let stmt = parser.parse(sql).expect("parse");
1861        let Statement::SetOp { op, .. } = stmt else {
1862            panic!("expected set op");
1863        };
1864        assert!(matches!(op, chryso_core::ast::SetOperator::UnionAll));
1865    }
1866
1867    #[test]
1868    fn parse_intersect_except() {
1869        let sql = "select id from t1 intersect select id from t2";
1870        let parser = SimpleParser::new(ParserConfig {
1871            dialect: Dialect::Postgres,
1872        });
1873        let stmt = parser.parse(sql).expect("parse");
1874        let Statement::SetOp { op, .. } = stmt else {
1875            panic!("expected set op");
1876        };
1877        assert!(matches!(op, chryso_core::ast::SetOperator::Intersect));
1878
1879        let sql = "select id from t1 except all select id from t2";
1880        let stmt = parser.parse(sql).expect("parse");
1881        let Statement::SetOp { op, .. } = stmt else {
1882            panic!("expected set op");
1883        };
1884        assert!(matches!(op, chryso_core::ast::SetOperator::ExceptAll));
1885    }
1886
1887    #[test]
1888    fn parse_with_cte() {
1889        let sql = "with t as (select id from users) select id from t";
1890        let parser = SimpleParser::new(ParserConfig {
1891            dialect: Dialect::Postgres,
1892        });
1893        let stmt = parser.parse(sql).expect("parse");
1894        let Statement::With(with_stmt) = stmt else {
1895            panic!("expected with");
1896        };
1897        assert_eq!(with_stmt.ctes.len(), 1);
1898    }
1899
1900    #[test]
1901    fn parse_with_recursive_cte_columns() {
1902        let sql = "with recursive t(id) as (select id from users) select id from t";
1903        let parser = SimpleParser::new(ParserConfig {
1904            dialect: Dialect::Postgres,
1905        });
1906        let stmt = parser.parse(sql).expect("parse");
1907        let Statement::With(with_stmt) = stmt else {
1908            panic!("expected with");
1909        };
1910        assert!(with_stmt.recursive);
1911        assert_eq!(with_stmt.ctes[0].columns, vec!["id".to_string()]);
1912    }
1913
1914    #[test]
1915    fn parse_with_duplicate_cte_columns() {
1916        let sql = "with t(id, id) as (select id from users) select id from t";
1917        let parser = SimpleParser::new(ParserConfig {
1918            dialect: Dialect::Postgres,
1919        });
1920        let err = parser.parse(sql).expect_err("expected error");
1921        assert!(err.to_string().contains("duplicate CTE column"));
1922    }
1923
1924    #[test]
1925    fn parse_with_empty_cte_columns() {
1926        let sql = "with t() as (select id from users) select id from t";
1927        let parser = SimpleParser::new(ParserConfig {
1928            dialect: Dialect::Postgres,
1929        });
1930        let err = parser.parse(sql).expect_err("expected error");
1931        assert!(err.to_string().contains("CTE column list cannot be empty"));
1932    }
1933
1934    #[test]
1935    fn parse_with_insert() {
1936        let sql = "with t as (select id from users) insert into audit (id) values (1)";
1937        let parser = SimpleParser::new(ParserConfig {
1938            dialect: Dialect::Postgres,
1939        });
1940        let stmt = parser.parse(sql).expect("parse");
1941        let Statement::With(with_stmt) = stmt else {
1942            panic!("expected with");
1943        };
1944        assert!(matches!(*with_stmt.statement, Statement::Insert(_)));
1945    }
1946
1947    #[test]
1948    fn parse_with_delete() {
1949        let sql = "with t as (select id from users) delete from users where id = 1";
1950        let parser = SimpleParser::new(ParserConfig {
1951            dialect: Dialect::Postgres,
1952        });
1953        let stmt = parser.parse(sql).expect("parse");
1954        let Statement::With(with_stmt) = stmt else {
1955            panic!("expected with");
1956        };
1957        assert!(matches!(*with_stmt.statement, Statement::Delete(_)));
1958    }
1959
1960    #[test]
1961    fn parse_with_nested_cte() {
1962        let sql = "with t as (with u as (select id from users) select id from u) select id from t";
1963        let parser = SimpleParser::new(ParserConfig {
1964            dialect: Dialect::Postgres,
1965        });
1966        let stmt = parser.parse(sql).expect("parse");
1967        let Statement::With(with_stmt) = stmt else {
1968            panic!("expected with");
1969        };
1970        assert_eq!(with_stmt.ctes.len(), 1);
1971    }
1972
1973    #[test]
1974    fn parse_with_delete_returning_mixed() {
1975        let sql = "with t as (select id from users) delete from users returning id, users.*";
1976        let parser = SimpleParser::new(ParserConfig {
1977            dialect: Dialect::Postgres,
1978        });
1979        let stmt = parser.parse(sql).expect("parse");
1980        let Statement::With(with_stmt) = stmt else {
1981            panic!("expected with");
1982        };
1983        let Statement::Delete(delete) = with_stmt.statement.as_ref() else {
1984            panic!("expected delete");
1985        };
1986        assert_eq!(delete.returning.len(), 2);
1987        assert!(matches!(delete.returning[1].expr, Expr::Identifier(ref name) if name == "users.*"));
1988    }
1989
1990    #[test]
1991    fn parse_returning_expressions() {
1992        let sql = "update users set name = 'bob' returning id + 1 as next_id, upper(name)";
1993        let parser = SimpleParser::new(ParserConfig {
1994            dialect: Dialect::Postgres,
1995        });
1996        let stmt = parser.parse(sql).expect("parse");
1997        let Statement::Update(update) = stmt else {
1998            panic!("expected update");
1999        };
2000        assert_eq!(update.returning.len(), 2);
2001        assert!(matches!(update.returning[0].alias.as_deref(), Some("next_id")));
2002    }
2003
2004    #[test]
2005    fn parse_with_insert_returning_mixed() {
2006        let sql = "with t as (select id from users) insert into users (id) values (1) returning id, users.*";
2007        let parser = SimpleParser::new(ParserConfig {
2008            dialect: Dialect::Postgres,
2009        });
2010        let stmt = parser.parse(sql).expect("parse");
2011        let Statement::With(with_stmt) = stmt else {
2012            panic!("expected with");
2013        };
2014        let Statement::Insert(insert) = with_stmt.statement.as_ref() else {
2015            panic!("expected insert");
2016        };
2017        assert_eq!(insert.returning.len(), 2);
2018    }
2019
2020    #[test]
2021    fn parse_with_update_returning_mixed() {
2022        let sql = "with t as (select id from users) update users set name = 'bob' returning id, users.*";
2023        let parser = SimpleParser::new(ParserConfig {
2024            dialect: Dialect::Postgres,
2025        });
2026        let stmt = parser.parse(sql).expect("parse");
2027        let Statement::With(with_stmt) = stmt else {
2028            panic!("expected with");
2029        };
2030        let Statement::Update(update) = with_stmt.statement.as_ref() else {
2031            panic!("expected update");
2032        };
2033        assert_eq!(update.returning.len(), 2);
2034    }
2035
2036    #[test]
2037    fn parse_returning_case_expr() {
2038        let sql = "update users set active = true returning case when active then 1 else 0 end as flag";
2039        let parser = SimpleParser::new(ParserConfig {
2040            dialect: Dialect::Postgres,
2041        });
2042        let stmt = parser.parse(sql).expect("parse");
2043        let Statement::Update(update) = stmt else {
2044            panic!("expected update");
2045        };
2046        assert_eq!(update.returning.len(), 1);
2047        assert!(matches!(update.returning[0].alias.as_deref(), Some("flag")));
2048    }
2049
2050    #[test]
2051    fn parse_returning_nested_function() {
2052        let sql = "insert into users (name) values ('alice') returning upper(trim(name))";
2053        let parser = SimpleParser::new(ParserConfig {
2054            dialect: Dialect::Postgres,
2055        });
2056        let stmt = parser.parse(sql).expect("parse");
2057        let Statement::Insert(insert) = stmt else {
2058            panic!("expected insert");
2059        };
2060        assert_eq!(insert.returning.len(), 1);
2061    }
2062
2063    #[test]
2064    fn parse_returning_multiple_aliases() {
2065        let sql = "insert into users (id, name) values (1, 'alice') returning id as id1, name as name1";
2066        let parser = SimpleParser::new(ParserConfig {
2067            dialect: Dialect::Postgres,
2068        });
2069        let stmt = parser.parse(sql).expect("parse");
2070        let Statement::Insert(insert) = stmt else {
2071            panic!("expected insert");
2072        };
2073        assert_eq!(insert.returning.len(), 2);
2074        assert!(matches!(insert.returning[0].alias.as_deref(), Some("id1")));
2075        assert!(matches!(insert.returning[1].alias.as_deref(), Some("name1")));
2076    }
2077
2078    #[test]
2079    fn parse_returning_with_star_and_alias() {
2080        let sql = "delete from users returning *, id as id1";
2081        let parser = SimpleParser::new(ParserConfig {
2082            dialect: Dialect::Postgres,
2083        });
2084        let stmt = parser.parse(sql).expect("parse");
2085        let Statement::Delete(delete) = stmt else {
2086            panic!("expected delete");
2087        };
2088        assert_eq!(delete.returning.len(), 2);
2089        assert!(matches!(delete.returning[0].expr, Expr::Wildcard));
2090        assert!(matches!(delete.returning[1].alias.as_deref(), Some("id1")));
2091    }
2092
2093    #[test]
2094    fn parse_with_recursive_multiple_ctes() {
2095        let sql = "with recursive t(id) as (select id from users), u as (select id from t) select id from u";
2096        let parser = SimpleParser::new(ParserConfig {
2097            dialect: Dialect::Postgres,
2098        });
2099        let stmt = parser.parse(sql).expect("parse");
2100        let Statement::With(with_stmt) = stmt else {
2101            panic!("expected with");
2102        };
2103        assert!(with_stmt.recursive);
2104        assert_eq!(with_stmt.ctes.len(), 2);
2105    }
2106
2107    #[test]
2108    fn parse_returning_complex_expr() {
2109        let sql = "update users set active = true returning case when active then upper(name) else lower(name) end as cname";
2110        let parser = SimpleParser::new(ParserConfig {
2111            dialect: Dialect::Postgres,
2112        });
2113        let stmt = parser.parse(sql).expect("parse");
2114        let Statement::Update(update) = stmt else {
2115            panic!("expected update");
2116        };
2117        assert_eq!(update.returning.len(), 1);
2118        assert!(matches!(update.returning[0].alias.as_deref(), Some("cname")));
2119    }
2120
2121    #[test]
2122    fn parse_with_union_returning() {
2123        let sql = "with t as (select id from t1 union select id from t2) update users set id = 1 returning id";
2124        let parser = SimpleParser::new(ParserConfig {
2125            dialect: Dialect::Postgres,
2126        });
2127        let stmt = parser.parse(sql).expect("parse");
2128        let Statement::With(with_stmt) = stmt else {
2129            panic!("expected with");
2130        };
2131        let Statement::SetOp { op, .. } = with_stmt.ctes[0].query.as_ref() else {
2132            panic!("expected set op");
2133        };
2134        assert!(matches!(op, chryso_core::ast::SetOperator::Union));
2135        let Statement::Update(update) = with_stmt.statement.as_ref() else {
2136            panic!("expected update");
2137        };
2138        assert_eq!(update.returning.len(), 1);
2139    }
2140
2141    #[test]
2142    fn parse_with_intersect_returning() {
2143        let sql = "with t as (select id from t1 intersect select id from t2) delete from users returning id";
2144        let parser = SimpleParser::new(ParserConfig {
2145            dialect: Dialect::Postgres,
2146        });
2147        let stmt = parser.parse(sql).expect("parse");
2148        let Statement::With(with_stmt) = stmt else {
2149            panic!("expected with");
2150        };
2151        let Statement::SetOp { op, .. } = with_stmt.ctes[0].query.as_ref() else {
2152            panic!("expected set op");
2153        };
2154        assert!(matches!(op, chryso_core::ast::SetOperator::Intersect));
2155        let Statement::Delete(delete) = with_stmt.statement.as_ref() else {
2156            panic!("expected delete");
2157        };
2158        assert_eq!(delete.returning.len(), 1);
2159    }
2160
2161    #[test]
2162    fn parse_with_except_returning() {
2163        let sql = "with t as (select id from t1 except select id from t2) insert into users (id) values (1) returning id";
2164        let parser = SimpleParser::new(ParserConfig {
2165            dialect: Dialect::Postgres,
2166        });
2167        let stmt = parser.parse(sql).expect("parse");
2168        let Statement::With(with_stmt) = stmt else {
2169            panic!("expected with");
2170        };
2171        let Statement::SetOp { op, .. } = with_stmt.ctes[0].query.as_ref() else {
2172            panic!("expected set op");
2173        };
2174        assert!(matches!(op, chryso_core::ast::SetOperator::Except));
2175        let Statement::Insert(insert) = with_stmt.statement.as_ref() else {
2176            panic!("expected insert");
2177        };
2178        assert_eq!(insert.returning.len(), 1);
2179    }
2180
2181    #[test]
2182    fn parse_returning_mixed_expressions() {
2183        let sql = "update users set id = 1 returning id, id + 1 as next_id, users.*";
2184        let parser = SimpleParser::new(ParserConfig {
2185            dialect: Dialect::Postgres,
2186        });
2187        let stmt = parser.parse(sql).expect("parse");
2188        let Statement::Update(update) = stmt else {
2189            panic!("expected update");
2190        };
2191        assert_eq!(update.returning.len(), 3);
2192        assert!(matches!(update.returning[1].alias.as_deref(), Some("next_id")));
2193        assert!(matches!(update.returning[2].expr, Expr::Identifier(ref name) if name == "users.*"));
2194    }
2195
2196    #[test]
2197    fn parse_with_intersect_all_returning() {
2198        let sql = "with t as (select id from t1 intersect all select id from t2) delete from users returning id";
2199        let parser = SimpleParser::new(ParserConfig {
2200            dialect: Dialect::Postgres,
2201        });
2202        let stmt = parser.parse(sql).expect("parse");
2203        let Statement::With(with_stmt) = stmt else {
2204            panic!("expected with");
2205        };
2206        let Statement::SetOp { op, .. } = with_stmt.ctes[0].query.as_ref() else {
2207            panic!("expected set op");
2208        };
2209        assert!(matches!(op, chryso_core::ast::SetOperator::IntersectAll));
2210        let Statement::Delete(delete) = with_stmt.statement.as_ref() else {
2211            panic!("expected delete");
2212        };
2213        assert_eq!(delete.returning.len(), 1);
2214    }
2215
2216    #[test]
2217    fn parse_with_except_all_returning() {
2218        let sql = "with t as (select id from t1 except all select id from t2) insert into users (id) values (1) returning id";
2219        let parser = SimpleParser::new(ParserConfig {
2220            dialect: Dialect::Postgres,
2221        });
2222        let stmt = parser.parse(sql).expect("parse");
2223        let Statement::With(with_stmt) = stmt else {
2224            panic!("expected with");
2225        };
2226        let Statement::SetOp { op, .. } = with_stmt.ctes[0].query.as_ref() else {
2227            panic!("expected set op");
2228        };
2229        assert!(matches!(op, chryso_core::ast::SetOperator::ExceptAll));
2230        let Statement::Insert(insert) = with_stmt.statement.as_ref() else {
2231            panic!("expected insert");
2232        };
2233        assert_eq!(insert.returning.len(), 1);
2234    }
2235
2236    #[test]
2237    fn parse_returning_deep_expr() {
2238        let sql = "update users set id = 1 returning case when active then upper(trim(name)) else lower(trim(name)) end as cname";
2239        let parser = SimpleParser::new(ParserConfig {
2240            dialect: Dialect::Postgres,
2241        });
2242        let stmt = parser.parse(sql).expect("parse");
2243        let Statement::Update(update) = stmt else {
2244            panic!("expected update");
2245        };
2246        assert_eq!(update.returning.len(), 1);
2247        assert!(matches!(update.returning[0].alias.as_deref(), Some("cname")));
2248    }
2249
2250    #[test]
2251    fn parse_with_cte_insert_returning() {
2252        let sql = "with t as (insert into users (id) values (1) returning id) select id from t";
2253        let parser = SimpleParser::new(ParserConfig {
2254            dialect: Dialect::Postgres,
2255        });
2256        let stmt = parser.parse(sql).expect("parse");
2257        let Statement::With(with_stmt) = stmt else {
2258            panic!("expected with");
2259        };
2260        let Statement::Insert(insert) = with_stmt.ctes[0].query.as_ref() else {
2261            panic!("expected insert");
2262        };
2263        assert_eq!(insert.returning.len(), 1);
2264    }
2265
2266    #[test]
2267    fn parse_with_cte_update_returning() {
2268        let sql = "with t as (update users set id = 1 returning id) select id from t";
2269        let parser = SimpleParser::new(ParserConfig {
2270            dialect: Dialect::Postgres,
2271        });
2272        let stmt = parser.parse(sql).expect("parse");
2273        let Statement::With(with_stmt) = stmt else {
2274            panic!("expected with");
2275        };
2276        let Statement::Update(update) = with_stmt.ctes[0].query.as_ref() else {
2277            panic!("expected update");
2278        };
2279        assert_eq!(update.returning.len(), 1);
2280    }
2281
2282    #[test]
2283    fn parse_with_cte_delete_returning() {
2284        let sql = "with t as (delete from users returning id) select id from t";
2285        let parser = SimpleParser::new(ParserConfig {
2286            dialect: Dialect::Postgres,
2287        });
2288        let stmt = parser.parse(sql).expect("parse");
2289        let Statement::With(with_stmt) = stmt else {
2290            panic!("expected with");
2291        };
2292        let Statement::Delete(delete) = with_stmt.ctes[0].query.as_ref() else {
2293            panic!("expected delete");
2294        };
2295        assert_eq!(delete.returning.len(), 1);
2296    }
2297
2298    #[test]
2299    fn parse_returning_table_column_expr() {
2300        let sql = "update users set id = 1 returning users.id, id + 1, users.*";
2301        let parser = SimpleParser::new(ParserConfig {
2302            dialect: Dialect::Postgres,
2303        });
2304        let stmt = parser.parse(sql).expect("parse");
2305        let Statement::Update(update) = stmt else {
2306            panic!("expected update");
2307        };
2308        assert_eq!(update.returning.len(), 3);
2309        assert!(matches!(update.returning[0].expr, Expr::Identifier(ref name) if name == "users.id"));
2310        assert!(matches!(update.returning[2].expr, Expr::Identifier(ref name) if name == "users.*"));
2311    }
2312
2313    #[test]
2314    fn parse_with_cte_setop_insert_returning() {
2315        let sql = "with t as (select id from t1 union select id from t2) insert into users (id) values (1) returning id";
2316        let parser = SimpleParser::new(ParserConfig {
2317            dialect: Dialect::Postgres,
2318        });
2319        let stmt = parser.parse(sql).expect("parse");
2320        let Statement::With(with_stmt) = stmt else {
2321            panic!("expected with");
2322        };
2323        let Statement::SetOp { op, .. } = with_stmt.ctes[0].query.as_ref() else {
2324            panic!("expected set op");
2325        };
2326        assert!(matches!(op, chryso_core::ast::SetOperator::Union));
2327        let Statement::Insert(insert) = with_stmt.statement.as_ref() else {
2328            panic!("expected insert");
2329        };
2330        assert_eq!(insert.returning.len(), 1);
2331    }
2332
2333    #[test]
2334    fn parse_with_cte_setop_update_returning() {
2335        let sql = "with t as (select id from t1 intersect select id from t2) update users set id = 1 returning id";
2336        let parser = SimpleParser::new(ParserConfig {
2337            dialect: Dialect::Postgres,
2338        });
2339        let stmt = parser.parse(sql).expect("parse");
2340        let Statement::With(with_stmt) = stmt else {
2341            panic!("expected with");
2342        };
2343        let Statement::SetOp { op, .. } = with_stmt.ctes[0].query.as_ref() else {
2344            panic!("expected set op");
2345        };
2346        assert!(matches!(op, chryso_core::ast::SetOperator::Intersect));
2347        let Statement::Update(update) = with_stmt.statement.as_ref() else {
2348            panic!("expected update");
2349        };
2350        assert_eq!(update.returning.len(), 1);
2351    }
2352
2353    #[test]
2354    fn parse_with_cte_setop_delete_returning() {
2355        let sql = "with t as (select id from t1 except select id from t2) delete from users returning id";
2356        let parser = SimpleParser::new(ParserConfig {
2357            dialect: Dialect::Postgres,
2358        });
2359        let stmt = parser.parse(sql).expect("parse");
2360        let Statement::With(with_stmt) = stmt else {
2361            panic!("expected with");
2362        };
2363        let Statement::SetOp { op, .. } = with_stmt.ctes[0].query.as_ref() else {
2364            panic!("expected set op");
2365        };
2366        assert!(matches!(op, chryso_core::ast::SetOperator::Except));
2367        let Statement::Delete(delete) = with_stmt.statement.as_ref() else {
2368            panic!("expected delete");
2369        };
2370        assert_eq!(delete.returning.len(), 1);
2371    }
2372
2373    #[test]
2374    fn parse_returning_complex_chain() {
2375        let sql = "update users set id = 1 returning upper(trim(lower(name))) as cname, case when id > 0 then id * 2 else id / 2 end";
2376        let parser = SimpleParser::new(ParserConfig {
2377            dialect: Dialect::Postgres,
2378        });
2379        let stmt = parser.parse(sql).expect("parse");
2380        let Statement::Update(update) = stmt else {
2381            panic!("expected update");
2382        };
2383        assert_eq!(update.returning.len(), 2);
2384        assert!(matches!(update.returning[0].alias.as_deref(), Some("cname")));
2385    }
2386
2387    #[test]
2388    fn parse_with_duplicate_cte_names() {
2389        let sql = "with t as (select id from t1), t as (select id from t2) select id from t";
2390        let parser = SimpleParser::new(ParserConfig {
2391            dialect: Dialect::Postgres,
2392        });
2393        let err = parser.parse(sql).expect_err("expected error");
2394        assert!(err.to_string().contains("duplicate CTE name"));
2395    }
2396
2397    #[test]
2398    fn parse_insert_returning() {
2399        let sql = "insert into users (id) values (1) returning id";
2400        let parser = SimpleParser::new(ParserConfig {
2401            dialect: Dialect::Postgres,
2402        });
2403        let stmt = parser.parse(sql).expect("parse");
2404        let Statement::Insert(insert) = stmt else {
2405            panic!("expected insert");
2406        };
2407        assert_eq!(insert.returning.len(), 1);
2408    }
2409
2410    #[test]
2411    fn parse_update_returning() {
2412        let sql = "update users set name = 'bob' returning id";
2413        let parser = SimpleParser::new(ParserConfig {
2414            dialect: Dialect::Postgres,
2415        });
2416        let stmt = parser.parse(sql).expect("parse");
2417        let Statement::Update(update) = stmt else {
2418            panic!("expected update");
2419        };
2420        assert_eq!(update.returning.len(), 1);
2421    }
2422
2423    #[test]
2424    fn parse_delete_returning() {
2425        let sql = "delete from users returning id";
2426        let parser = SimpleParser::new(ParserConfig {
2427            dialect: Dialect::Postgres,
2428        });
2429        let stmt = parser.parse(sql).expect("parse");
2430        let Statement::Delete(delete) = stmt else {
2431            panic!("expected delete");
2432        };
2433        assert_eq!(delete.returning.len(), 1);
2434    }
2435
2436    #[test]
2437    fn parse_with_union_cte() {
2438        let sql = "with t as (select id from t1 union all select id from t2) select id from t";
2439        let parser = SimpleParser::new(ParserConfig {
2440            dialect: Dialect::Postgres,
2441        });
2442        let stmt = parser.parse(sql).expect("parse");
2443        let Statement::With(with_stmt) = stmt else {
2444            panic!("expected with");
2445        };
2446        assert_eq!(with_stmt.ctes.len(), 1);
2447        let Statement::SetOp { op, .. } = with_stmt.ctes[0].query.as_ref() else {
2448            panic!("expected set op");
2449        };
2450        assert!(matches!(op, chryso_core::ast::SetOperator::UnionAll));
2451    }
2452
2453    #[test]
2454    fn parse_with_multiple_ctes() {
2455        let sql = "with t as (select id from t1), u as (select id from t2) select id from t";
2456        let parser = SimpleParser::new(ParserConfig {
2457            dialect: Dialect::Postgres,
2458        });
2459        let stmt = parser.parse(sql).expect("parse");
2460        let Statement::With(with_stmt) = stmt else {
2461            panic!("expected with");
2462        };
2463        assert_eq!(with_stmt.ctes.len(), 2);
2464    }
2465
2466    #[test]
2467    fn parse_order_by_nulls_last() {
2468        let sql = "select id from users order by id desc nulls last";
2469        let parser = SimpleParser::new(ParserConfig {
2470            dialect: Dialect::Postgres,
2471        });
2472        let stmt = parser.parse(sql).expect("parse");
2473        let Statement::Select(select) = stmt else {
2474            panic!("expected select");
2475        };
2476        assert_eq!(select.order_by.len(), 1);
2477        assert_eq!(select.order_by[0].nulls_first, Some(false));
2478    }
2479
2480    #[test]
2481    fn parse_predicate_precedence() {
2482        let sql = "select id from t where a = 1 or b = 2 and c = 3";
2483        let parser = SimpleParser::new(ParserConfig {
2484            dialect: Dialect::Postgres,
2485        });
2486        let stmt = parser.parse(sql).expect("parse");
2487        let Statement::Select(select) = stmt else {
2488            panic!("expected select");
2489        };
2490        let Expr::BinaryOp { op, right, .. } = select.selection.expect("selection") else {
2491            panic!("expected binary op");
2492        };
2493        assert!(matches!(op, BinaryOperator::Or));
2494        let Expr::BinaryOp { op, .. } = right.as_ref() else {
2495            panic!("expected nested binary op");
2496        };
2497        assert!(matches!(op, BinaryOperator::And));
2498    }
2499
2500    #[test]
2501    fn parse_join() {
2502        let sql = "select * from t1 join t2 on t1.id = t2.id";
2503        let parser = SimpleParser::new(ParserConfig {
2504            dialect: Dialect::Postgres,
2505        });
2506        let stmt = parser.parse(sql).expect("parse");
2507        let Statement::Select(select) = stmt else {
2508            panic!("expected select");
2509        };
2510        let from = unwrap_from(&select);
2511        assert_eq!(from.joins.len(), 1);
2512        assert!(matches!(from.joins[0].join_type, JoinType::Inner));
2513    }
2514
2515    #[test]
2516    fn parse_from_subquery() {
2517        let sql = "select * from (select id from users) as u";
2518        let parser = SimpleParser::new(ParserConfig {
2519            dialect: Dialect::Postgres,
2520        });
2521        let stmt = parser.parse(sql).expect("parse");
2522        let Statement::Select(select) = stmt else {
2523            panic!("expected select");
2524        };
2525        let from = unwrap_from(&select);
2526        assert!(matches!(
2527            from.factor,
2528            TableFactor::Derived { .. }
2529        ));
2530        assert_eq!(from.alias.as_deref(), Some("u"));
2531    }
2532
2533    #[test]
2534    fn parse_from_subquery_join() {
2535        let sql = "select * from (select id from users) as u join items on u.id = items.user_id";
2536        let parser = SimpleParser::new(ParserConfig {
2537            dialect: Dialect::Postgres,
2538        });
2539        let stmt = parser.parse(sql).expect("parse");
2540        let Statement::Select(select) = stmt else {
2541            panic!("expected select");
2542        };
2543        let from = unwrap_from(&select);
2544        assert!(matches!(from.factor, TableFactor::Derived { .. }));
2545        assert_eq!(from.joins.len(), 1);
2546        assert!(matches!(from.joins[0].right.factor, TableFactor::Table { .. }));
2547    }
2548
2549    #[test]
2550    fn parse_from_subquery_join_using() {
2551        let sql = "select * from (select id from users) as u join items using (id)";
2552        let parser = SimpleParser::new(ParserConfig {
2553            dialect: Dialect::Postgres,
2554        });
2555        let stmt = parser.parse(sql).expect("parse");
2556        let Statement::Select(select) = stmt else {
2557            panic!("expected select");
2558        };
2559        let on_sql = unwrap_from(&select).joins[0].on.to_sql();
2560        assert!(on_sql.contains("u.id = items.id"));
2561    }
2562
2563    #[test]
2564    fn parse_from_subquery_column_aliases() {
2565        let sql = "select * from (select id from users) as u(user_id)";
2566        let parser = SimpleParser::new(ParserConfig {
2567            dialect: Dialect::Postgres,
2568        });
2569        let stmt = parser.parse(sql).expect("parse");
2570        let Statement::Select(select) = stmt else {
2571            panic!("expected select");
2572        };
2573        let from = unwrap_from(&select);
2574        assert_eq!(from.column_aliases, vec!["user_id"]);
2575    }
2576
2577    #[test]
2578    fn parse_table_alias_list_requires_alias() {
2579        let sql = "select * from users (id)";
2580        let parser = SimpleParser::new(ParserConfig {
2581            dialect: Dialect::Postgres,
2582        });
2583        let err = parser.parse(sql).expect_err("expected error");
2584        assert!(err.to_string().contains("requires alias"));
2585    }
2586
2587    #[test]
2588    fn parse_from_subquery_requires_alias() {
2589        let sql = "select * from (select id from users)";
2590        let parser = SimpleParser::new(ParserConfig {
2591            dialect: Dialect::Postgres,
2592        });
2593        let err = parser.parse(sql).expect_err("expected error");
2594        assert!(err.to_string().contains("requires alias"));
2595    }
2596
2597    #[test]
2598    fn parse_comma_join() {
2599        let sql = "select * from t1, t2";
2600        let parser = SimpleParser::new(ParserConfig {
2601            dialect: Dialect::Postgres,
2602        });
2603        let stmt = parser.parse(sql).expect("parse");
2604        let Statement::Select(select) = stmt else {
2605            panic!("expected select");
2606        };
2607        let from = unwrap_from(&select);
2608        assert!(matches!(
2609            from.factor,
2610            TableFactor::Table { ref name } if name == "t1"
2611        ));
2612        assert_eq!(from.joins.len(), 1);
2613        assert!(matches!(from.joins[0].join_type, JoinType::Inner));
2614        assert!(matches!(
2615            from.joins[0].right.factor,
2616            TableFactor::Table { ref name } if name == "t2"
2617        ));
2618        assert!(matches!(
2619            from.joins[0].on,
2620            Expr::Literal(Literal::Bool(true))
2621        ));
2622    }
2623
2624    #[test]
2625    fn parse_comma_join_precedence() {
2626        let sql = "select * from t1, t2 join t3 on t2.id = t3.id";
2627        let parser = SimpleParser::new(ParserConfig {
2628            dialect: Dialect::Postgres,
2629        });
2630        let stmt = parser.parse(sql).expect("parse");
2631        let Statement::Select(select) = stmt else {
2632            panic!("expected select");
2633        };
2634        let from = unwrap_from(&select);
2635        assert_eq!(from.joins.len(), 1);
2636        assert!(matches!(
2637            from.joins[0].right.factor,
2638            TableFactor::Table { ref name } if name == "t2"
2639        ));
2640        assert_eq!(from.joins[0].right.joins.len(), 1);
2641        assert!(matches!(
2642            from.joins[0].right.joins[0].right.factor,
2643            TableFactor::Table { ref name } if name == "t3"
2644        ));
2645    }
2646
2647    #[test]
2648    fn parse_join_then_comma_join() {
2649        let sql = "select * from t1 join t2 on t1.id = t2.id, t3";
2650        let parser = SimpleParser::new(ParserConfig {
2651            dialect: Dialect::Postgres,
2652        });
2653        let stmt = parser.parse(sql).expect("parse");
2654        let Statement::Select(select) = stmt else {
2655            panic!("expected select");
2656        };
2657        let from = unwrap_from(&select);
2658        assert_eq!(from.joins.len(), 2);
2659        assert!(matches!(
2660            from.joins[1].on,
2661            Expr::Literal(Literal::Bool(true))
2662        ));
2663    }
2664
2665    #[test]
2666    fn parse_join_without_condition_rejected() {
2667        let sql = "select * from t1 join t2";
2668        let parser = SimpleParser::new(ParserConfig {
2669            dialect: Dialect::Postgres,
2670        });
2671        let err = parser.parse(sql).expect_err("expected error");
2672        assert!(err.to_string().contains("JOIN expects ON or USING"));
2673    }
2674
2675    #[test]
2676    fn parse_cross_join() {
2677        let sql = "select * from t1 cross join t2";
2678        let parser = SimpleParser::new(ParserConfig {
2679            dialect: Dialect::Postgres,
2680        });
2681        let stmt = parser.parse(sql).expect("parse");
2682        let Statement::Select(select) = stmt else {
2683            panic!("expected select");
2684        };
2685        let from = unwrap_from(&select);
2686        assert_eq!(from.joins.len(), 1);
2687        assert!(matches!(from.joins[0].join_type, JoinType::Inner));
2688        assert!(matches!(
2689            from.joins[0].on,
2690            Expr::Literal(Literal::Bool(true))
2691        ));
2692    }
2693
2694    #[test]
2695    fn parse_natural_join() {
2696        let sql = "select * from t1 natural join t2";
2697        let parser = SimpleParser::new(ParserConfig {
2698            dialect: Dialect::Postgres,
2699        });
2700        let stmt = parser.parse(sql).expect("parse");
2701        let Statement::Select(select) = stmt else {
2702            panic!("expected select");
2703        };
2704        let from = unwrap_from(&select);
2705        assert_eq!(from.joins.len(), 1);
2706        assert!(matches!(
2707            from.joins[0].on,
2708            Expr::Literal(Literal::Bool(true))
2709        ));
2710    }
2711
2712    #[test]
2713    fn parse_natural_left_join() {
2714        let sql = "select * from t1 natural left join t2";
2715        let parser = SimpleParser::new(ParserConfig {
2716            dialect: Dialect::Postgres,
2717        });
2718        let stmt = parser.parse(sql).expect("parse");
2719        let Statement::Select(select) = stmt else {
2720            panic!("expected select");
2721        };
2722        let from = unwrap_from(&select);
2723        assert_eq!(from.joins.len(), 1);
2724        assert!(matches!(from.joins[0].join_type, JoinType::Left));
2725    }
2726
2727    #[test]
2728    fn parse_natural_join_with_on_rejected() {
2729        let sql = "select * from t1 natural join t2 on t1.id = t2.id";
2730        let parser = SimpleParser::new(ParserConfig {
2731            dialect: Dialect::Postgres,
2732        });
2733        let err = parser.parse(sql).expect_err("expected error");
2734        assert!(err.to_string().contains("NATURAL/CROSS JOIN cannot use ON or USING"));
2735    }
2736
2737    #[test]
2738    fn parse_cross_join_with_using_rejected() {
2739        let sql = "select * from t1 cross join t2 using (id)";
2740        let parser = SimpleParser::new(ParserConfig {
2741            dialect: Dialect::Postgres,
2742        });
2743        let err = parser.parse(sql).expect_err("expected error");
2744        assert!(err.to_string().contains("NATURAL/CROSS JOIN cannot use ON or USING"));
2745    }
2746
2747    #[test]
2748    fn parse_join_using() {
2749        let sql = "select * from t1 join t2 using (id, name)";
2750        let parser = SimpleParser::new(ParserConfig {
2751            dialect: Dialect::Postgres,
2752        });
2753        let stmt = parser.parse(sql).expect("parse");
2754        let Statement::Select(select) = stmt else {
2755            panic!("expected select");
2756        };
2757        let on_sql = unwrap_from(&select).joins[0].on.to_sql();
2758        assert!(on_sql.contains("t1.id = t2.id"));
2759        assert!(on_sql.contains("t1.name = t2.name"));
2760    }
2761
2762    #[test]
2763    fn parse_case_expr() {
2764        let sql = "select case when a = 1 then 'one' else 'other' end from t";
2765        let parser = SimpleParser::new(ParserConfig {
2766            dialect: Dialect::Postgres,
2767        });
2768        let stmt = parser.parse(sql).expect("parse");
2769        let Statement::Select(select) = stmt else {
2770            panic!("expected select");
2771        };
2772        let expr = &select.projection[0].expr;
2773        let Expr::Case { when_then, .. } = expr else {
2774            panic!("expected case");
2775        };
2776        assert_eq!(when_then.len(), 1);
2777    }
2778
2779    #[test]
2780    fn parse_case_with_operand() {
2781        let sql = "select case status when 'ok' then 1 else 0 end from t";
2782        let parser = SimpleParser::new(ParserConfig {
2783            dialect: Dialect::Postgres,
2784        });
2785        let stmt = parser.parse(sql).expect("parse");
2786        let Statement::Select(select) = stmt else {
2787            panic!("expected select");
2788        };
2789        let expr = &select.projection[0].expr;
2790        let Expr::Case { operand, .. } = expr else {
2791            panic!("expected case");
2792        };
2793        assert!(operand.is_some());
2794    }
2795
2796    #[test]
2797    fn parse_case_missing_when() {
2798        let sql = "select case end from t";
2799        let parser = SimpleParser::new(ParserConfig {
2800            dialect: Dialect::Postgres,
2801        });
2802        let err = parser.parse(sql).expect_err("expected error");
2803        assert!(err.to_string().contains("CASE expects"));
2804    }
2805
2806    #[test]
2807    fn parse_right_full_join() {
2808        let sql = "select * from t1 right join t2 on t1.id = t2.id";
2809        let parser = SimpleParser::new(ParserConfig {
2810            dialect: Dialect::Postgres,
2811        });
2812        let stmt = parser.parse(sql).expect("parse");
2813        let Statement::Select(select) = stmt else {
2814            panic!("expected select");
2815        };
2816        assert!(matches!(
2817            unwrap_from(&select).joins[0].join_type,
2818            JoinType::Right
2819        ));
2820        let sql = "select * from t1 full join t2 on t1.id = t2.id";
2821        let stmt = parser.parse(sql).expect("parse");
2822        let Statement::Select(select) = stmt else {
2823            panic!("expected select");
2824        };
2825        assert!(matches!(
2826            unwrap_from(&select).joins[0].join_type,
2827            JoinType::Full
2828        ));
2829    }
2830
2831    #[test]
2832    fn parse_explain_select() {
2833        let sql = "explain select id from users";
2834        let parser = SimpleParser::new(ParserConfig {
2835            dialect: Dialect::Postgres,
2836        });
2837        let stmt = parser.parse(sql).expect("parse");
2838        let Statement::Explain(inner) = stmt else {
2839            panic!("expected explain");
2840        };
2841        let Statement::Select(_) = inner.as_ref() else {
2842            panic!("expected select");
2843        };
2844    }
2845
2846    #[test]
2847    fn parse_create_table() {
2848        let sql = "create table users";
2849        let parser = SimpleParser::new(ParserConfig {
2850            dialect: Dialect::Postgres,
2851        });
2852        let stmt = parser.parse(sql).expect("parse");
2853        let Statement::CreateTable(create) = stmt else {
2854            panic!("expected create table");
2855        };
2856        assert_eq!(create.name, "users");
2857        assert!(!create.if_not_exists);
2858        assert!(create.columns.is_empty());
2859    }
2860
2861    #[test]
2862    fn parse_create_table_if_not_exists() {
2863        let sql = "create table if not exists users";
2864        let parser = SimpleParser::new(ParserConfig {
2865            dialect: Dialect::Postgres,
2866        });
2867        let stmt = parser.parse(sql).expect("parse");
2868        let Statement::CreateTable(create) = stmt else {
2869            panic!("expected create table");
2870        };
2871        assert!(create.if_not_exists);
2872        assert!(create.columns.is_empty());
2873    }
2874
2875    #[test]
2876    fn parse_create_table_with_columns() {
2877        let sql = "create table users (id integer, name varchar(20))";
2878        let parser = SimpleParser::new(ParserConfig {
2879            dialect: Dialect::Postgres,
2880        });
2881        let stmt = parser.parse(sql).expect("parse");
2882        let Statement::CreateTable(create) = stmt else {
2883            panic!("expected create table");
2884        };
2885        assert_eq!(create.columns.len(), 2);
2886        assert_eq!(create.columns[0].name, "id");
2887        assert_eq!(create.columns[0].data_type, "integer");
2888        assert_eq!(create.columns[1].name, "name");
2889        assert_eq!(create.columns[1].data_type, "varchar(20)");
2890    }
2891
2892    #[test]
2893    fn parse_create_table_with_complex_types() {
2894        let sql = "create table metrics (price decimal(10,2), stamp timestamp with time zone, dtype pg_catalog.int4)";
2895        let parser = SimpleParser::new(ParserConfig {
2896            dialect: Dialect::Postgres,
2897        });
2898        let stmt = parser.parse(sql).expect("parse");
2899        let Statement::CreateTable(create) = stmt else {
2900            panic!("expected create table");
2901        };
2902        assert_eq!(create.columns.len(), 3);
2903        assert_eq!(create.columns[0].data_type, "decimal(10,2)");
2904        assert_eq!(create.columns[1].data_type, "timestamp with time zone");
2905        assert_eq!(create.columns[2].data_type, "pg_catalog.int4");
2906    }
2907
2908    #[test]
2909    fn parse_drop_table() {
2910        let sql = "drop table users";
2911        let parser = SimpleParser::new(ParserConfig {
2912            dialect: Dialect::Postgres,
2913        });
2914        let stmt = parser.parse(sql).expect("parse");
2915        let Statement::DropTable(drop) = stmt else {
2916            panic!("expected drop table");
2917        };
2918        assert_eq!(drop.name, "users");
2919        assert!(!drop.if_exists);
2920    }
2921
2922    #[test]
2923    fn parse_drop_table_if_exists() {
2924        let sql = "drop table if exists users";
2925        let parser = SimpleParser::new(ParserConfig {
2926            dialect: Dialect::Postgres,
2927        });
2928        let stmt = parser.parse(sql).expect("parse");
2929        let Statement::DropTable(drop) = stmt else {
2930            panic!("expected drop table");
2931        };
2932        assert!(drop.if_exists);
2933    }
2934
2935    #[test]
2936    fn parse_truncate_table() {
2937        let sql = "truncate table users";
2938        let parser = SimpleParser::new(ParserConfig {
2939            dialect: Dialect::Postgres,
2940        });
2941        let stmt = parser.parse(sql).expect("parse");
2942        let Statement::Truncate(truncate) = stmt else {
2943            panic!("expected truncate");
2944        };
2945        assert_eq!(truncate.table, "users");
2946    }
2947
2948    #[test]
2949    fn parse_quoted_identifiers() {
2950        let sql = "select \"user\" from `users`";
2951        let parser = SimpleParser::new(ParserConfig {
2952            dialect: Dialect::MySql,
2953        });
2954        let stmt = parser.parse(sql).expect("parse");
2955        let Statement::Select(select) = stmt else {
2956            panic!("expected select");
2957        };
2958        let first = select.projection.first().expect("projection");
2959        match &first.expr {
2960            Expr::Identifier(name) => assert_eq!(name, "user"),
2961            _ => panic!("expected identifier"),
2962        }
2963        assert!(matches!(
2964            unwrap_from(&select).factor,
2965            TableFactor::Table { ref name } if name == "users"
2966        ));
2967    }
2968
2969    #[test]
2970    fn parse_analyze() {
2971        let sql = "analyze users";
2972        let parser = SimpleParser::new(ParserConfig {
2973            dialect: Dialect::Postgres,
2974        });
2975        let stmt = parser.parse(sql).expect("parse");
2976        let Statement::Analyze(analyze) = stmt else {
2977            panic!("expected analyze");
2978        };
2979        assert_eq!(analyze.table, "users");
2980    }
2981
2982    #[test]
2983    fn parse_analyze_table() {
2984        let sql = "analyze table users";
2985        let parser = SimpleParser::new(ParserConfig {
2986            dialect: Dialect::Postgres,
2987        });
2988        let stmt = parser.parse(sql).expect("parse");
2989        let Statement::Analyze(analyze) = stmt else {
2990            panic!("expected analyze");
2991        };
2992        assert_eq!(analyze.table, "users");
2993    }
2994
2995    #[test]
2996    fn parse_explain_insert() {
2997        let sql = "explain insert into users (id) values (1)";
2998        let parser = SimpleParser::new(ParserConfig {
2999            dialect: Dialect::Postgres,
3000        });
3001        let stmt = parser.parse(sql).expect("parse");
3002        let Statement::Explain(inner) = stmt else {
3003            panic!("expected explain");
3004        };
3005        assert!(matches!(*inner, Statement::Insert(_)));
3006    }
3007
3008    #[test]
3009    fn parse_explain_delete() {
3010        let sql = "explain delete from users where id = 1";
3011        let parser = SimpleParser::new(ParserConfig {
3012            dialect: Dialect::Postgres,
3013        });
3014        let stmt = parser.parse(sql).expect("parse");
3015        let Statement::Explain(inner) = stmt else {
3016            panic!("expected explain");
3017        };
3018        assert!(matches!(*inner, Statement::Delete(_)));
3019    }
3020
3021    #[test]
3022    fn parse_window_function() {
3023        let sql = "select sum(amount) over (partition by id order by ts) from sales";
3024        let parser = SimpleParser::new(ParserConfig {
3025            dialect: Dialect::Postgres,
3026        });
3027        let stmt = parser.parse(sql).expect("parse");
3028        let Statement::Select(select) = stmt else {
3029            panic!("expected select");
3030        };
3031        let first = select.projection.first().expect("projection");
3032        match &first.expr {
3033            Expr::WindowFunction { .. } => {}
3034            _ => panic!("expected window function"),
3035        }
3036    }
3037
3038    #[test]
3039    fn parse_subquery_expression() {
3040        let sql = "select (select id from t) from users";
3041        let parser = SimpleParser::new(ParserConfig {
3042            dialect: Dialect::Postgres,
3043        });
3044        let stmt = parser.parse(sql).expect("parse");
3045        let Statement::Select(select) = stmt else {
3046            panic!("expected select");
3047        };
3048        let first = select.projection.first().expect("projection");
3049        match &first.expr {
3050            Expr::Subquery(_) => {}
3051            _ => panic!("expected subquery"),
3052        }
3053    }
3054
3055    #[test]
3056    fn parse_exists_subquery() {
3057        let sql = "select id from users where exists (select id from t)";
3058        let parser = SimpleParser::new(ParserConfig {
3059            dialect: Dialect::Postgres,
3060        });
3061        let stmt = parser.parse(sql).expect("parse");
3062        let Statement::Select(select) = stmt else {
3063            panic!("expected select");
3064        };
3065        match select.selection {
3066            Some(Expr::Exists(_)) => {}
3067            _ => panic!("expected exists"),
3068        }
3069    }
3070
3071    #[test]
3072    fn parse_in_subquery() {
3073        let sql = "select id from users where id in (select id from t)";
3074        let parser = SimpleParser::new(ParserConfig {
3075            dialect: Dialect::Postgres,
3076        });
3077        let stmt = parser.parse(sql).expect("parse");
3078        let Statement::Select(select) = stmt else {
3079            panic!("expected select");
3080        };
3081        match select.selection {
3082            Some(Expr::InSubquery { .. }) => {}
3083            _ => panic!("expected in subquery"),
3084        }
3085    }
3086
3087    #[test]
3088    fn parse_in_list() {
3089        let sql = "select * from users where id in (1, 2, 3)";
3090        let parser = SimpleParser::new(ParserConfig {
3091            dialect: Dialect::Postgres,
3092        });
3093        let stmt = parser.parse(sql).expect("parse");
3094        let Statement::Select(select) = stmt else {
3095            panic!("expected select");
3096        };
3097        let expr = select.selection.expect("selection");
3098        assert_eq!(
3099            expr.to_sql(),
3100            "id = 1 or id = 2 or id = 3"
3101        );
3102    }
3103
3104    #[test]
3105    fn parse_boolean_literal() {
3106        let sql = "select * from users where active = true and deleted = false";
3107        let parser = SimpleParser::new(ParserConfig {
3108            dialect: Dialect::Postgres,
3109        });
3110        let stmt = parser.parse(sql).expect("parse");
3111        let Statement::Select(select) = stmt else {
3112            panic!("expected select");
3113        };
3114        let Expr::BinaryOp { op, .. } = select.selection.expect("selection") else {
3115            panic!("expected binary op");
3116        };
3117        assert!(matches!(op, BinaryOperator::And));
3118    }
3119
3120    #[test]
3121    fn parse_is_null() {
3122        let sql = "select * from users where deleted is null or deleted is not null";
3123        let parser = SimpleParser::new(ParserConfig {
3124            dialect: Dialect::Postgres,
3125        });
3126        let stmt = parser.parse(sql).expect("parse");
3127        let Statement::Select(select) = stmt else {
3128            panic!("expected select");
3129        };
3130        let Expr::BinaryOp { op, .. } = select.selection.expect("selection") else {
3131            panic!("expected binary op");
3132        };
3133        assert!(matches!(op, BinaryOperator::Or));
3134    }
3135
3136    #[test]
3137    fn parse_between() {
3138        let sql = "select * from users where age between 18 and 30";
3139        let parser = SimpleParser::new(ParserConfig {
3140            dialect: Dialect::Postgres,
3141        });
3142        let stmt = parser.parse(sql).expect("parse");
3143        let Statement::Select(select) = stmt else {
3144            panic!("expected select");
3145        };
3146        let Expr::BinaryOp { op, .. } = select.selection.expect("selection") else {
3147            panic!("expected binary op");
3148        };
3149        assert!(matches!(op, BinaryOperator::And));
3150    }
3151
3152    #[test]
3153    fn parse_not_between() {
3154        let sql = "select * from users where age not between 18 and 30";
3155        let parser = SimpleParser::new(ParserConfig {
3156            dialect: Dialect::Postgres,
3157        });
3158        let stmt = parser.parse(sql).expect("parse");
3159        let Statement::Select(select) = stmt else {
3160            panic!("expected select");
3161        };
3162        let Expr::BinaryOp { op, .. } = select.selection.expect("selection") else {
3163            panic!("expected binary op");
3164        };
3165        assert!(matches!(op, BinaryOperator::Or));
3166    }
3167
3168    #[test]
3169    fn parse_like() {
3170        let sql = "select * from users where name like 'al%'";
3171        let parser = SimpleParser::new(ParserConfig {
3172            dialect: Dialect::Postgres,
3173        });
3174        let stmt = parser.parse(sql).expect("parse");
3175        let Statement::Select(select) = stmt else {
3176            panic!("expected select");
3177        };
3178        let Expr::FunctionCall { name, args } = select.selection.expect("selection") else {
3179            panic!("expected function call");
3180        };
3181        assert_eq!(name, "like");
3182        assert_eq!(args.len(), 2);
3183    }
3184
3185    #[test]
3186    fn parse_not_like() {
3187        let sql = "select * from users where name not like 'al%'";
3188        let parser = SimpleParser::new(ParserConfig {
3189            dialect: Dialect::Postgres,
3190        });
3191        let stmt = parser.parse(sql).expect("parse");
3192        let Statement::Select(select) = stmt else {
3193            panic!("expected select");
3194        };
3195        let Expr::UnaryOp { op, expr } = select.selection.expect("selection") else {
3196            panic!("expected unary op");
3197        };
3198        assert!(matches!(op, UnaryOperator::Not));
3199        let Expr::FunctionCall { name, args } = expr.as_ref() else {
3200            panic!("expected function call");
3201        };
3202        assert_eq!(name, "like");
3203        assert_eq!(args.len(), 2);
3204    }
3205
3206    #[test]
3207    fn parse_like_escape() {
3208        let sql = "select * from users where name like 'al\\_%' escape '\\\\'";
3209        let parser = SimpleParser::new(ParserConfig {
3210            dialect: Dialect::Postgres,
3211        });
3212        let stmt = parser.parse(sql).expect("parse");
3213        let Statement::Select(select) = stmt else {
3214            panic!("expected select");
3215        };
3216        let Expr::FunctionCall { name, args } = select.selection.expect("selection") else {
3217            panic!("expected function call");
3218        };
3219        assert_eq!(name, "like");
3220        assert_eq!(args.len(), 3);
3221    }
3222
3223    #[test]
3224    fn parse_not_like_escape() {
3225        let sql = "select * from users where name not like 'al\\_%' escape '\\\\'";
3226        let parser = SimpleParser::new(ParserConfig {
3227            dialect: Dialect::Postgres,
3228        });
3229        let stmt = parser.parse(sql).expect("parse");
3230        let Statement::Select(select) = stmt else {
3231            panic!("expected select");
3232        };
3233        let Expr::UnaryOp { op, expr } = select.selection.expect("selection") else {
3234            panic!("expected unary op");
3235        };
3236        assert!(matches!(op, UnaryOperator::Not));
3237        let Expr::FunctionCall { name, args } = expr.as_ref() else {
3238            panic!("expected function call");
3239        };
3240        assert_eq!(name, "like");
3241        assert_eq!(args.len(), 3);
3242    }
3243
3244    #[test]
3245    fn parse_not_in_list() {
3246        let sql = "select * from users where id not in (1, 2, 3)";
3247        let parser = SimpleParser::new(ParserConfig {
3248            dialect: Dialect::Postgres,
3249        });
3250        let stmt = parser.parse(sql).expect("parse");
3251        let Statement::Select(select) = stmt else {
3252            panic!("expected select");
3253        };
3254        let Expr::BinaryOp { op, .. } = select.selection.expect("selection") else {
3255            panic!("expected binary op");
3256        };
3257        assert!(matches!(op, BinaryOperator::And));
3258    }
3259
3260    #[test]
3261    fn parse_ilike() {
3262        let sql = "select * from users where name ilike 'al%'";
3263        let parser = SimpleParser::new(ParserConfig {
3264            dialect: Dialect::Postgres,
3265        });
3266        let stmt = parser.parse(sql).expect("parse");
3267        let Statement::Select(select) = stmt else {
3268            panic!("expected select");
3269        };
3270        let Expr::FunctionCall { name, args } = select.selection.expect("selection") else {
3271            panic!("expected function call");
3272        };
3273        assert_eq!(name, "ilike");
3274        assert_eq!(args.len(), 2);
3275    }
3276
3277    #[test]
3278    fn parse_not_ilike() {
3279        let sql = "select * from users where name not ilike 'al%'";
3280        let parser = SimpleParser::new(ParserConfig {
3281            dialect: Dialect::Postgres,
3282        });
3283        let stmt = parser.parse(sql).expect("parse");
3284        let Statement::Select(select) = stmt else {
3285            panic!("expected select");
3286        };
3287        let Expr::UnaryOp { op, expr } = select.selection.expect("selection") else {
3288            panic!("expected unary op");
3289        };
3290        assert!(matches!(op, UnaryOperator::Not));
3291        let Expr::FunctionCall { name, args } = expr.as_ref() else {
3292            panic!("expected function call");
3293        };
3294        assert_eq!(name, "ilike");
3295        assert_eq!(args.len(), 2);
3296    }
3297
3298    #[test]
3299    fn parse_insert() {
3300        let sql = "insert into users (id, name) values (1, 'alice')";
3301        let parser = SimpleParser::new(ParserConfig {
3302            dialect: Dialect::Postgres,
3303        });
3304        let stmt = parser.parse(sql).expect("parse");
3305        let Statement::Insert(insert) = stmt else {
3306            panic!("expected insert");
3307        };
3308        assert_eq!(insert.table, "users");
3309        assert_eq!(insert.columns.len(), 2);
3310        let InsertSource::Values(values) = insert.source else {
3311            panic!("expected values source");
3312        };
3313        assert_eq!(values.len(), 1);
3314        assert_eq!(values[0].len(), 2);
3315        assert!(insert.returning.is_empty());
3316    }
3317
3318    #[test]
3319    fn parse_insert_select() {
3320        let sql = "insert into users (id) select id from staging";
3321        let parser = SimpleParser::new(ParserConfig {
3322            dialect: Dialect::Postgres,
3323        });
3324        let stmt = parser.parse(sql).expect("parse");
3325        let Statement::Insert(insert) = stmt else {
3326            panic!("expected insert");
3327        };
3328        let InsertSource::Query(statement) = insert.source else {
3329            panic!("expected query source");
3330        };
3331        let Statement::Select(_) = statement.as_ref() else {
3332            panic!("expected select query");
3333        };
3334    }
3335
3336    #[test]
3337    fn parse_insert_select_returning() {
3338        let sql = "insert into users (id) select id from staging returning id";
3339        let parser = SimpleParser::new(ParserConfig {
3340            dialect: Dialect::Postgres,
3341        });
3342        let stmt = parser.parse(sql).expect("parse");
3343        let Statement::Insert(insert) = stmt else {
3344            panic!("expected insert");
3345        };
3346        assert_eq!(insert.returning.len(), 1);
3347        assert!(matches!(insert.source, InsertSource::Query(_)));
3348    }
3349
3350    #[test]
3351    fn parse_insert_default_values() {
3352        let sql = "insert into users default values";
3353        let parser = SimpleParser::new(ParserConfig {
3354            dialect: Dialect::Postgres,
3355        });
3356        let stmt = parser.parse(sql).expect("parse");
3357        let Statement::Insert(insert) = stmt else {
3358            panic!("expected insert");
3359        };
3360        assert!(matches!(insert.source, InsertSource::DefaultValues));
3361        assert!(insert.returning.is_empty());
3362    }
3363
3364    #[test]
3365    fn parse_insert_default_values_returning() {
3366        let sql = "insert into users default values returning id";
3367        let parser = SimpleParser::new(ParserConfig {
3368            dialect: Dialect::Postgres,
3369        });
3370        let stmt = parser.parse(sql).expect("parse");
3371        let Statement::Insert(insert) = stmt else {
3372            panic!("expected insert");
3373        };
3374        assert!(matches!(insert.source, InsertSource::DefaultValues));
3375        assert_eq!(insert.returning.len(), 1);
3376    }
3377
3378    #[test]
3379    fn parse_returning_star() {
3380        let sql = "delete from users returning *";
3381        let parser = SimpleParser::new(ParserConfig {
3382            dialect: Dialect::Postgres,
3383        });
3384        let stmt = parser.parse(sql).expect("parse");
3385        let Statement::Delete(delete) = stmt else {
3386            panic!("expected delete");
3387        };
3388        assert_eq!(delete.returning.len(), 1);
3389        assert!(matches!(delete.returning[0].expr, Expr::Wildcard));
3390    }
3391
3392    #[test]
3393    fn parse_returning_qualified_star() {
3394        let sql = "delete from users returning users.*";
3395        let parser = SimpleParser::new(ParserConfig {
3396            dialect: Dialect::Postgres,
3397        });
3398        let stmt = parser.parse(sql).expect("parse");
3399        let Statement::Delete(delete) = stmt else {
3400            panic!("expected delete");
3401        };
3402        assert_eq!(delete.returning.len(), 1);
3403        assert!(matches!(delete.returning[0].expr, Expr::Identifier(ref name) if name == "users.*"));
3404    }
3405
3406    #[test]
3407    fn parse_schema_qualified_table() {
3408        let sql = "select * from public.users";
3409        let parser = SimpleParser::new(ParserConfig {
3410            dialect: Dialect::Postgres,
3411        });
3412        let stmt = parser.parse(sql).expect("parse");
3413        let Statement::Select(select) = stmt else {
3414            panic!("expected select");
3415        };
3416        let from = select.from.expect("from");
3417        let chryso_core::ast::TableFactor::Table { name } = from.factor else {
3418            panic!("expected table factor");
3419        };
3420        assert_eq!(name, "public.users");
3421    }
3422
3423    #[test]
3424    fn parse_schema_qualified_identifier() {
3425        let sql = "select public.users.id from public.users";
3426        let parser = SimpleParser::new(ParserConfig {
3427            dialect: Dialect::Postgres,
3428        });
3429        let stmt = parser.parse(sql).expect("parse");
3430        let Statement::Select(select) = stmt else {
3431            panic!("expected select");
3432        };
3433        assert_eq!(select.projection.len(), 1);
3434        assert!(matches!(select.projection[0].expr, Expr::Identifier(ref name) if name == "public.users.id"));
3435    }
3436
3437    #[test]
3438    fn parse_schema_qualified_wildcard() {
3439        let sql = "select public.users.* from public.users";
3440        let parser = SimpleParser::new(ParserConfig {
3441            dialect: Dialect::Postgres,
3442        });
3443        let stmt = parser.parse(sql).expect("parse");
3444        let Statement::Select(select) = stmt else {
3445            panic!("expected select");
3446        };
3447        assert_eq!(select.projection.len(), 1);
3448        assert!(matches!(select.projection[0].expr, Expr::Identifier(ref name) if name == "public.users.*"));
3449    }
3450
3451    #[test]
3452    fn parse_returning_alias() {
3453        let sql = "update users set name = 'bob' returning id as user_id";
3454        let parser = SimpleParser::new(ParserConfig {
3455            dialect: Dialect::Postgres,
3456        });
3457        let stmt = parser.parse(sql).expect("parse");
3458        let Statement::Update(update) = stmt else {
3459            panic!("expected update");
3460        };
3461        assert_eq!(update.returning.len(), 1);
3462        assert!(matches!(update.returning[0].alias.as_deref(), Some("user_id")));
3463    }
3464
3465    #[test]
3466    fn parse_returning_mixed_list() {
3467        let sql = "insert into users (id) values (1) returning id, users.*, name as username";
3468        let parser = SimpleParser::new(ParserConfig {
3469            dialect: Dialect::Postgres,
3470        });
3471        let stmt = parser.parse(sql).expect("parse");
3472        let Statement::Insert(insert) = stmt else {
3473            panic!("expected insert");
3474        };
3475        assert_eq!(insert.returning.len(), 3);
3476        assert!(matches!(insert.returning[0].expr, Expr::Identifier(ref name) if name == "id"));
3477        assert!(matches!(insert.returning[1].expr, Expr::Identifier(ref name) if name == "users.*"));
3478        assert!(matches!(insert.returning[2].alias.as_deref(), Some("username")));
3479    }
3480
3481    #[test]
3482    fn parse_update() {
3483        let sql = "update users set name = 'bob' where id = 1";
3484        let parser = SimpleParser::new(ParserConfig {
3485            dialect: Dialect::Postgres,
3486        });
3487        let stmt = parser.parse(sql).expect("parse");
3488        let Statement::Update(update) = stmt else {
3489            panic!("expected update");
3490        };
3491        assert_eq!(update.table, "users");
3492        assert_eq!(update.assignments.len(), 1);
3493    }
3494
3495    #[test]
3496    fn parse_delete() {
3497        let sql = "delete from users where id = 1";
3498        let parser = SimpleParser::new(ParserConfig {
3499            dialect: Dialect::Postgres,
3500        });
3501        let stmt = parser.parse(sql).expect("parse");
3502        let Statement::Delete(delete) = stmt else {
3503            panic!("expected delete");
3504        };
3505        assert_eq!(delete.table, "users");
3506        assert!(delete.selection.is_some());
3507    }
3508
3509    #[test]
3510    fn parse_insert_multi_values() {
3511        let sql = "insert into users (id, name) values (1, 'alice'), (2, 'bob')";
3512        let parser = SimpleParser::new(ParserConfig {
3513            dialect: Dialect::Postgres,
3514        });
3515        let stmt = parser.parse(sql).expect("parse");
3516        let Statement::Insert(insert) = stmt else {
3517            panic!("expected insert");
3518        };
3519        let InsertSource::Values(values) = insert.source else {
3520            panic!("expected values source");
3521        };
3522        assert_eq!(values.len(), 2);
3523    }
3524}