Skip to main content

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