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