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