1use super::error::ParseError;
48use super::Parser;
49use super::PlaceholderMode;
50use crate::ast::{BinOp, Expr, ExprSubquery, FieldRef, Span, UnaryOp};
51use crate::lexer::Token;
52use reddb_types::types::{DataType, Value};
53
54fn is_duration_unit(unit: &str) -> bool {
55 matches!(
56 unit.to_ascii_lowercase().as_str(),
57 "ms" | "msec"
58 | "millisecond"
59 | "milliseconds"
60 | "s"
61 | "sec"
62 | "secs"
63 | "second"
64 | "seconds"
65 | "m"
66 | "min"
67 | "mins"
68 | "minute"
69 | "minutes"
70 | "h"
71 | "hr"
72 | "hrs"
73 | "hour"
74 | "hours"
75 | "d"
76 | "day"
77 | "days"
78 )
79}
80
81fn keyword_function_name(token: &Token) -> Option<&'static str> {
82 match token {
83 Token::Count => Some("COUNT"),
84 Token::Sum => Some("SUM"),
85 Token::Avg => Some("AVG"),
86 Token::Min => Some("MIN"),
87 Token::Max => Some("MAX"),
88 Token::First => Some("FIRST"),
89 Token::Last => Some("LAST"),
90 Token::Left => Some("LEFT"),
91 Token::Right => Some("RIGHT"),
92 Token::Contains => Some("CONTAINS"),
93 Token::Kv => Some("KV"),
94 _ => None,
95 }
96}
97
98fn is_window_eligible_function(name: &str) -> bool {
104 matches!(
105 name.to_ascii_uppercase().as_str(),
106 "LAG"
108 | "LEAD"
109 | "ROW_NUMBER"
110 | "RANK"
111 | "DENSE_RANK"
112 | "PERCENT_RANK"
113 | "CUME_DIST"
114 | "NTILE"
115 | "FIRST_VALUE"
116 | "LAST_VALUE"
117 | "NTH_VALUE"
118 | "COUNT"
120 | "SUM"
121 | "AVG"
122 | "MIN"
123 | "MAX"
124 | "STDDEV"
125 | "VARIANCE"
126 | "MEDIAN"
127 | "PERCENTILE"
128 | "GROUP_CONCAT"
129 | "STRING_AGG"
130 | "FIRST"
131 | "LAST"
132 | "ARRAY_AGG"
133 | "COUNT_DISTINCT"
134 )
135}
136
137fn bare_zero_arg_function_name(name: &str) -> Option<&'static str> {
138 match name.to_ascii_uppercase().as_str() {
139 "CURRENT_TIMESTAMP" => Some("CURRENT_TIMESTAMP"),
140 "CURRENT_DATE" => Some("CURRENT_DATE"),
141 "CURRENT_TIME" => Some("CURRENT_TIME"),
142 _ => None,
143 }
144}
145
146impl<'a> Parser<'a> {
147 pub fn parse_expr(&mut self) -> Result<Expr, ParseError> {
150 self.parse_expr_prec(0)
151 }
152
153 pub(crate) fn parse_expr_with_min_precedence(
154 &mut self,
155 min_prec: u8,
156 ) -> Result<Expr, ParseError> {
157 self.parse_expr_prec(min_prec)
158 }
159
160 pub(crate) fn continue_expr(&mut self, left: Expr, min_prec: u8) -> Result<Expr, ParseError> {
163 self.parse_expr_suffix(left, min_prec)
164 }
165
166 fn parse_expr_prec(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
169 self.enter_depth()?;
174 let result = (|| {
175 let left = self.parse_expr_unary()?;
176 self.parse_expr_suffix(left, min_prec)
177 })();
178 self.exit_depth();
179 result
180 }
181
182 fn parse_expr_suffix(&mut self, mut left: Expr, min_prec: u8) -> Result<Expr, ParseError> {
183 let max_infix_chain = self.depth.max_depth.saturating_mul(8).max(1);
184 let mut infix_chain = 0usize;
185 loop {
186 let Some((op, prec)) = self.peek_binop() else {
187 if min_prec <= 32 {
189 if let Some(node) = self.try_parse_postfix(&left)? {
190 left = node;
191 continue;
192 }
193 }
194 break;
195 };
196 if prec < min_prec {
197 break;
198 }
199 infix_chain += 1;
200 if infix_chain > max_infix_chain {
201 return Err(ParseError::token_limit(
202 "max_tokens",
203 self.max_tokens,
204 self.position(),
205 ));
206 }
207 self.advance()?; let start_span = self.span_start_of(&left);
209 let rhs = self.parse_expr_prec(prec + 1)?;
210 let end_span = self.span_end_of(&rhs);
211 left = Expr::BinaryOp {
212 op,
213 lhs: Box::new(left),
214 rhs: Box::new(rhs),
215 span: Span::new(start_span, end_span),
216 };
217 }
218 Ok(left)
219 }
220
221 fn parse_expr_unary(&mut self) -> Result<Expr, ParseError> {
224 match self.peek() {
225 Token::Not => {
226 let start = self.position();
227 self.advance()?;
228 let operand = self.parse_expr_prec(25)?;
229 let end = self.span_end_of(&operand);
230 Ok(Expr::UnaryOp {
231 op: UnaryOp::Not,
232 operand: Box::new(operand),
233 span: Span::new(start, end),
234 })
235 }
236 Token::Dash => {
237 let start = self.position();
238 self.advance()?;
239 let operand = self.parse_expr_prec(70)?;
240 let end = self.span_end_of(&operand);
241 Ok(Expr::UnaryOp {
242 op: UnaryOp::Neg,
243 operand: Box::new(operand),
244 span: Span::new(start, end),
245 })
246 }
247 Token::Plus => {
248 self.advance()?;
250 self.parse_expr_prec(70)
251 }
252 _ => self.parse_expr_factor(),
253 }
254 }
255
256 fn parse_expr_factor(&mut self) -> Result<Expr, ParseError> {
259 let start = self.position();
260
261 if self.consume(&Token::LParen)? {
263 if self.check(&Token::Select) {
264 let query = self.parse_select_query()?;
265 self.expect(Token::RParen)?;
266 return Ok(Expr::Subquery {
267 query: ExprSubquery {
268 query: Box::new(query),
269 },
270 span: Span::new(start, self.position()),
271 });
272 }
273 let inner = self.parse_expr_prec(0)?;
274 self.expect(Token::RParen)?;
275 return Ok(inner);
276 }
277
278 if self.consume(&Token::True)? {
280 return Ok(Expr::Literal {
281 value: Value::Boolean(true),
282 span: Span::new(start, self.position()),
283 });
284 }
285 if self.consume(&Token::False)? {
286 return Ok(Expr::Literal {
287 value: Value::Boolean(false),
288 span: Span::new(start, self.position()),
289 });
290 }
291 if self.consume(&Token::Null)? {
292 return Ok(Expr::Literal {
293 value: Value::Null,
294 span: Span::new(start, self.position()),
295 });
296 }
297
298 if let Token::Integer(n) = *self.peek() {
302 self.advance()?;
303 if let Token::Ident(ref unit) = *self.peek() {
304 if is_duration_unit(unit) {
305 let duration = format!("{n}{}", unit.to_ascii_lowercase());
306 self.advance()?;
307 return Ok(Expr::Literal {
308 value: Value::text(duration),
309 span: Span::new(start, self.position()),
310 });
311 }
312 }
313 return Ok(Expr::Literal {
314 value: Value::Integer(n),
315 span: Span::new(start, self.position()),
316 });
317 }
318 if let Token::Float(n) = *self.peek() {
319 self.advance()?;
320 return Ok(Expr::Literal {
321 value: Value::Float(n),
322 span: Span::new(start, self.position()),
323 });
324 }
325 if let Token::String(ref s) = *self.peek() {
326 let text = s.clone();
327 self.advance()?;
328 return Ok(Expr::Literal {
329 value: Value::text(text),
330 span: Span::new(start, self.position()),
331 });
332 }
333
334 if matches!(
339 self.peek(),
340 Token::LBrace | Token::LBracket | Token::JsonLiteral(_)
341 ) {
342 let value = self
343 .parse_literal_value()
344 .map_err(|e| ParseError::new(e.message, self.position()))?;
345 return Ok(Expr::Literal {
346 value,
347 span: Span::new(start, self.position()),
348 });
349 }
350
351 if self.check(&Token::Question) {
355 let (index, span) = self.parse_question_param_index()?;
356 return Ok(Expr::Parameter { index, span });
357 }
358
359 if self.consume(&Token::Dollar)? {
360 if let Token::Integer(n) = *self.peek() {
365 if n < 1 {
366 return Err(ParseError::new(
367 "placeholder index must be >= 1".to_string(),
368 self.position(),
369 ));
370 }
371 if self.placeholder_mode == PlaceholderMode::Question {
372 return Err(ParseError::new(
373 "cannot mix `?` and `$N` placeholders in one statement".to_string(),
374 self.position(),
375 ));
376 }
377 self.placeholder_mode = PlaceholderMode::Dollar;
378 self.advance()?;
379 return Ok(Expr::Parameter {
380 index: (n - 1) as usize,
381 span: Span::new(start, self.position()),
382 });
383 }
384 let path = self.parse_dollar_ref_path()?;
385 let path_lc = path.to_ascii_lowercase();
386 let (name, key) = if let Some(rest) = path_lc.strip_prefix("secret.") {
387 ("__SECRET_REF", format!("red.vault/{rest}"))
388 } else if let Some(rest) = path_lc
389 .strip_prefix("red.secret.")
390 .or_else(|| path_lc.strip_prefix("red.secrets."))
391 {
392 ("__SECRET_REF", format!("red.vault/{rest}"))
393 } else if let Some(rest) = path_lc.strip_prefix("config.") {
394 ("CONFIG", format!("red.config/{rest}"))
395 } else if path_lc.starts_with("red.config.") {
396 let rest = path_lc.trim_start_matches("red.config.");
397 ("CONFIG", format!("red.config/{rest}"))
398 } else {
399 return Err(ParseError::new(
400 format!(
401 "unknown $ reference `${path}`; expected $secret.*, $red.secret.*, $red.secrets.*, $config.*, or $red.config.*"
402 ),
403 self.position(),
404 ));
405 };
406 return Ok(Expr::FunctionCall {
407 name: name.to_string(),
408 args: vec![Expr::Literal {
409 value: Value::text(key),
410 span: Span::new(start, self.position()),
411 }],
412 span: Span::new(start, self.position()),
413 });
414 }
415
416 if let Some(name) = keyword_function_name(self.peek()) {
417 if matches!(self.peek_next()?, Token::LParen) {
418 self.advance()?; return self.parse_function_call_expr_with_name(start, name.to_string());
420 }
421 }
422
423 if let Token::Ident(ref name) = *self.peek() {
431 let name_upper = name.to_uppercase();
432
433 if name_upper == "CASE" {
443 return self.parse_case_expr(start);
444 }
445
446 let saved_name = name.clone();
447 self.advance()?; if matches!(self.peek(), Token::LParen) {
451 return self.parse_function_call_expr_with_name(start, saved_name);
452 }
453
454 if let Some(function_name) = bare_zero_arg_function_name(&saved_name) {
455 let end = self.position();
456 return Ok(Expr::FunctionCall {
457 name: function_name.to_string(),
458 args: Vec::new(),
459 span: Span::new(start, end),
460 });
461 }
462
463 if matches!(self.peek(), Token::Dot) {
465 let mut segments = vec![saved_name];
466 while self.consume(&Token::Dot)? {
467 segments.push(self.expect_ident_or_keyword()?);
468 }
469 let field = FieldRef::TableColumn {
470 table: segments.remove(0),
471 column: segments.join("."),
472 };
473 let end = self.position();
474 return Ok(Expr::Column {
475 field,
476 span: Span::new(start, end),
477 });
478 }
479
480 let field = FieldRef::TableColumn {
482 table: String::new(),
483 column: saved_name,
484 };
485 let end = self.position();
486 return Ok(Expr::Column {
487 field,
488 span: Span::new(start, end),
489 });
490 }
491
492 let field = self.parse_field_ref()?;
497 let end = self.position();
498 Ok(Expr::Column {
499 field,
500 span: Span::new(start, end),
501 })
502 }
503
504 fn parse_dollar_ref_path(&mut self) -> Result<String, ParseError> {
505 let mut path = self.expect_ident_or_keyword()?;
506 while self.consume(&Token::Dot)? {
507 let next = self.expect_ident_or_keyword()?;
508 path = format!("{path}.{next}");
509 }
510 Ok(path)
511 }
512
513 fn parse_function_call_expr_with_name(
514 &mut self,
515 start: crate::lexer::Position,
516 function_name: String,
517 ) -> Result<Expr, ParseError> {
518 let call = self.parse_function_call_expr_with_name_inner(start, function_name)?;
519 if matches!(self.peek(), Token::Over) {
525 return self.lift_to_window_call(start, call);
526 }
527 Ok(call)
528 }
529
530 fn parse_function_call_expr_with_name_inner(
531 &mut self,
532 start: crate::lexer::Position,
533 function_name: String,
534 ) -> Result<Expr, ParseError> {
535 self.expect(Token::LParen)?;
536
537 if function_name.eq_ignore_ascii_case("CAST") {
538 let inner = self.parse_expr_prec(0)?;
539 self.expect(Token::As)?;
540 let type_name = self.expect_ident_or_keyword()?;
541 self.expect(Token::RParen)?;
542 let end = self.position();
543 let Some(target) = DataType::from_sql_name(&type_name) else {
544 return Err(ParseError::new(
545 format!("unknown type name {type_name:?} in CAST"),
549 self.position(),
550 ));
551 };
552 return Ok(Expr::Cast {
553 inner: Box::new(inner),
554 target,
555 span: Span::new(start, end),
556 });
557 }
558
559 if function_name.eq_ignore_ascii_case("TRIM") {
560 let (name, args) = self.parse_trim_expr_args()?;
561 self.expect(Token::RParen)?;
562 let end = self.position();
563 return Ok(Expr::FunctionCall {
564 name,
565 args,
566 span: Span::new(start, end),
567 });
568 }
569
570 if function_name.eq_ignore_ascii_case("POSITION") {
571 let args = self.parse_position_expr_args()?;
572 self.expect(Token::RParen)?;
573 let end = self.position();
574 return Ok(Expr::FunctionCall {
575 name: function_name,
576 args,
577 span: Span::new(start, end),
578 });
579 }
580
581 if function_name.eq_ignore_ascii_case("SUBSTRING") {
582 let args = self.parse_substring_expr_args()?;
583 self.expect(Token::RParen)?;
584 let end = self.position();
585 return Ok(Expr::FunctionCall {
586 name: function_name,
587 args,
588 span: Span::new(start, end),
589 });
590 }
591
592 if function_name.eq_ignore_ascii_case("COUNT") {
593 if self.consume(&Token::Distinct)? {
594 let arg = self.parse_expr_prec(0)?;
595 self.expect(Token::RParen)?;
596 let end = self.position();
597 return Ok(Expr::FunctionCall {
598 name: "COUNT_DISTINCT".to_string(),
599 args: vec![arg],
600 span: Span::new(start, end),
601 });
602 }
603
604 if self.consume(&Token::Star)? {
605 self.expect(Token::RParen)?;
606 let end = self.position();
607 return Ok(Expr::FunctionCall {
608 name: function_name,
609 args: vec![Expr::Column {
610 field: FieldRef::TableColumn {
611 table: String::new(),
612 column: "*".to_string(),
613 },
614 span: Span::synthetic(),
615 }],
616 span: Span::new(start, end),
617 });
618 }
619 }
620
621 if function_name.eq_ignore_ascii_case("CONFIG") || function_name.eq_ignore_ascii_case("KV")
632 {
633 let mut args = Vec::new();
634 if !self.check(&Token::RParen) {
635 loop {
636 args.push(self.parse_config_kv_arg(start)?);
637 if !self.consume(&Token::Comma)? {
638 break;
639 }
640 }
641 }
642 self.expect(Token::RParen)?;
643 let end = self.position();
644 return Ok(Expr::FunctionCall {
645 name: function_name,
646 args,
647 span: Span::new(start, end),
648 });
649 }
650
651 let mut args = Vec::new();
652 if !self.check(&Token::RParen) {
653 loop {
654 args.push(self.parse_expr_prec(0)?);
655 if !self.consume(&Token::Comma)? {
656 break;
657 }
658 }
659 }
660 self.expect(Token::RParen)?;
661 let end = self.position();
662 Ok(Expr::FunctionCall {
663 name: function_name,
664 args,
665 span: Span::new(start, end),
666 })
667 }
668
669 fn parse_config_kv_arg(&mut self, start: crate::lexer::Position) -> Result<Expr, ParseError> {
676 let mut is_expression_start = matches!(
681 self.peek(),
682 Token::String(_)
683 | Token::Integer(_)
684 | Token::Float(_)
685 | Token::Dollar
686 | Token::Question
687 | Token::LParen
688 );
689 if matches!(self.peek(), Token::Ident(_)) && matches!(self.peek_next()?, Token::LParen) {
692 is_expression_start = true;
693 }
694 if !is_expression_start && !self.check(&Token::RParen) {
695 let mut path = self.expect_ident_or_keyword()?;
696 while self.consume(&Token::Dot)? {
697 let next = self.expect_ident_or_keyword()?;
698 path = format!("{path}.{next}");
699 }
700 let end = self.position();
701 return Ok(Expr::Literal {
702 value: Value::text(path.to_ascii_lowercase()),
703 span: Span::new(start, end),
704 });
705 }
706 self.parse_expr_prec(0)
707 }
708
709 fn lift_to_window_call(
716 &mut self,
717 start: crate::lexer::Position,
718 call: Expr,
719 ) -> Result<Expr, ParseError> {
720 let (name, args) = match call {
721 Expr::FunctionCall { name, args, .. } => (name, args),
722 other => {
723 return Err(ParseError::new(
724 format!(
725 "OVER may only follow a function call, got {:?}",
726 std::mem::discriminant(&other)
727 ),
728 self.position(),
729 ));
730 }
731 };
732 if !is_window_eligible_function(&name) {
733 return Err(ParseError::new(
734 format!(
735 "function `{}` cannot be used with an OVER clause; \
736 expected a window function (LAG, LEAD, ROW_NUMBER, \
737 RANK, DENSE_RANK) or an aggregate",
738 name.to_uppercase()
739 ),
740 self.position(),
741 ));
742 }
743 let window = self.parse_over_clause()?;
744 let end = self.position();
745 Ok(Expr::WindowFunctionCall {
746 name,
747 args,
748 window,
749 span: Span::new(start, end),
750 })
751 }
752
753 fn parse_over_clause(&mut self) -> Result<crate::ast::WindowSpec, ParseError> {
756 self.expect(Token::Over)?;
757 self.expect(Token::LParen)?;
758
759 let mut spec = crate::ast::WindowSpec::default();
760
761 if self.consume(&Token::Partition)? {
762 self.expect(Token::By)?;
763 loop {
764 spec.partition_by.push(self.parse_expr_prec(0)?);
765 if !self.consume(&Token::Comma)? {
766 break;
767 }
768 }
769 }
770
771 if self.consume(&Token::Order)? {
772 self.expect(Token::By)?;
773 loop {
774 let expr = self.parse_expr_prec(0)?;
775 let ascending = if self.consume(&Token::Desc)? {
776 false
777 } else {
778 self.consume(&Token::Asc)?;
779 true
780 };
781 let mut nulls_first = !ascending;
784 if self.consume(&Token::Nulls)? {
785 if self.consume(&Token::First)? {
786 nulls_first = true;
787 } else if self.consume(&Token::Last)? {
788 nulls_first = false;
789 } else {
790 return Err(ParseError::new(
791 "expected FIRST or LAST after NULLS".to_string(),
792 self.position(),
793 ));
794 }
795 }
796 spec.order_by.push(crate::ast::WindowOrderItem {
797 expr,
798 ascending,
799 nulls_first,
800 });
801 if !self.consume(&Token::Comma)? {
802 break;
803 }
804 }
805 }
806
807 if matches!(self.peek(), Token::Rows | Token::Range) {
808 spec.frame = Some(self.parse_window_frame()?);
809 }
810
811 self.expect(Token::RParen)?;
812 Ok(spec)
813 }
814
815 fn parse_window_frame(&mut self) -> Result<crate::ast::WindowFrame, ParseError> {
816 let unit = if self.consume(&Token::Rows)? {
817 crate::ast::WindowFrameUnit::Rows
818 } else if self.consume(&Token::Range)? {
819 crate::ast::WindowFrameUnit::Range
820 } else {
821 return Err(ParseError::new(
822 "expected ROWS or RANGE in window frame".to_string(),
823 self.position(),
824 ));
825 };
826
827 if self.consume(&Token::Between)? {
828 let start = self.parse_window_frame_bound()?;
829 self.expect(Token::And)?;
830 let end = self.parse_window_frame_bound()?;
831 Ok(crate::ast::WindowFrame {
832 unit,
833 start,
834 end: Some(end),
835 })
836 } else {
837 let start = self.parse_window_frame_bound()?;
838 Ok(crate::ast::WindowFrame {
839 unit,
840 start,
841 end: None,
842 })
843 }
844 }
845
846 fn parse_window_frame_bound(&mut self) -> Result<crate::ast::WindowFrameBound, ParseError> {
847 use crate::ast::WindowFrameBound;
848 if self.consume(&Token::Unbounded)? {
849 if self.consume(&Token::Preceding)? {
850 return Ok(WindowFrameBound::UnboundedPreceding);
851 }
852 if self.consume(&Token::Following)? {
853 return Ok(WindowFrameBound::UnboundedFollowing);
854 }
855 return Err(ParseError::new(
856 "expected PRECEDING or FOLLOWING after UNBOUNDED".to_string(),
857 self.position(),
858 ));
859 }
860 if self.consume(&Token::Current)? {
861 self.expect(Token::Row)?;
862 return Ok(WindowFrameBound::CurrentRow);
863 }
864 let offset = self.parse_expr_prec(0)?;
866 if self.consume(&Token::Preceding)? {
867 return Ok(WindowFrameBound::Preceding(Box::new(offset)));
868 }
869 if self.consume(&Token::Following)? {
870 return Ok(WindowFrameBound::Following(Box::new(offset)));
871 }
872 Err(ParseError::new(
873 "expected PRECEDING or FOLLOWING after frame offset".to_string(),
874 self.position(),
875 ))
876 }
877
878 fn parse_case_expr(&mut self, start: crate::lexer::Position) -> Result<Expr, ParseError> {
890 self.advance()?; let selector = if matches!(self.peek(), Token::Ident(id) if id.eq_ignore_ascii_case("WHEN"))
893 {
894 None
895 } else {
896 Some(self.parse_expr_prec(0)?)
897 };
898 let mut branches: Vec<(Expr, Expr)> = Vec::new();
899 loop {
900 if !self.consume_ident_ci("WHEN")? {
901 break;
902 }
903 let when_val = self.parse_expr_prec(0)?;
904 let cond = match &selector {
907 None => when_val,
908 Some(sel) => {
909 let span = Span::new(sel.span().start, when_val.span().end);
910 Expr::BinaryOp {
911 op: BinOp::Eq,
912 lhs: Box::new(sel.clone()),
913 rhs: Box::new(when_val),
914 span,
915 }
916 }
917 };
918 if !self.consume_ident_ci("THEN")? {
919 return Err(ParseError::new(
920 "expected THEN after CASE WHEN condition".to_string(),
921 self.position(),
922 ));
923 }
924 let then_val = self.parse_expr_prec(0)?;
925 branches.push((cond, then_val));
926 }
927 if branches.is_empty() {
928 return Err(ParseError::new(
929 "CASE must have at least one WHEN branch".to_string(),
930 self.position(),
931 ));
932 }
933 let else_ = if self.consume_ident_ci("ELSE")? {
934 Some(Box::new(self.parse_expr_prec(0)?))
935 } else {
936 None
937 };
938 if !self.consume_ident_ci("END")? {
939 return Err(ParseError::new(
940 "expected END to close CASE expression".to_string(),
941 self.position(),
942 ));
943 }
944 let end = self.position();
945 Ok(Expr::Case {
946 branches,
947 else_,
948 span: Span::new(start, end),
949 })
950 }
951
952 fn parse_trim_expr_args(&mut self) -> Result<(String, Vec<Expr>), ParseError> {
953 let mut function_name = "TRIM".to_string();
954
955 if self.consume_ident_ci("LEADING")? {
956 function_name = "LTRIM".to_string();
957 } else if self.consume_ident_ci("TRAILING")? {
958 function_name = "RTRIM".to_string();
959 } else if self.consume_ident_ci("BOTH")? {
960 function_name = "TRIM".to_string();
961 }
962
963 if self.consume(&Token::From)? {
964 let source = self.parse_expr_prec(0)?;
965 return Ok((function_name, vec![source]));
966 }
967
968 let first = self.parse_expr_prec(0)?;
969
970 if self.consume(&Token::Comma)? {
971 let second = self.parse_expr_prec(0)?;
972 return Ok((function_name, vec![first, second]));
973 }
974
975 if self.consume(&Token::From)? {
976 let source = self.parse_expr_prec(0)?;
977 return Ok((function_name, vec![source, first]));
978 }
979
980 Ok((function_name, vec![first]))
981 }
982
983 fn parse_position_expr_args(&mut self) -> Result<Vec<Expr>, ParseError> {
987 let needle = self.parse_expr_prec(35)?;
991 if !self.consume(&Token::Comma)? {
992 self.expect(Token::In)?;
993 }
994 let haystack = self.parse_expr_prec(0)?;
995 Ok(vec![needle, haystack])
996 }
997
998 fn parse_substring_expr_args(&mut self) -> Result<Vec<Expr>, ParseError> {
1006 let source = self.parse_expr_prec(0)?;
1007
1008 if self.consume(&Token::Comma)? {
1009 let mut args = vec![source];
1010 loop {
1011 args.push(self.parse_expr_prec(0)?);
1012 if !self.consume(&Token::Comma)? {
1013 break;
1014 }
1015 }
1016 return Ok(args);
1017 }
1018
1019 if self.consume(&Token::From)? {
1020 let start = self.parse_expr_prec(0)?;
1021 if self.consume(&Token::For)? {
1022 let count = self.parse_expr_prec(0)?;
1023 return Ok(vec![source, start, count]);
1024 }
1025 return Ok(vec![source, start]);
1026 }
1027
1028 if self.consume(&Token::For)? {
1029 let count = self.parse_expr_prec(0)?;
1030 if self.consume(&Token::From)? {
1031 let start = self.parse_expr_prec(0)?;
1032 return Ok(vec![source, start, count]);
1033 }
1034 return Ok(vec![source, Expr::lit(Value::Integer(1)), count]);
1035 }
1036
1037 Ok(vec![source])
1038 }
1039
1040 fn try_parse_postfix(&mut self, left: &Expr) -> Result<Option<Expr>, ParseError> {
1050 let start = self.span_start_of(left);
1051
1052 if self.consume(&Token::Is)? {
1054 let negated = self.consume(&Token::Not)?;
1055 self.expect(Token::Null)?;
1056 let end = self.position();
1057 return Ok(Some(Expr::IsNull {
1058 operand: Box::new(left.clone()),
1059 negated,
1060 span: Span::new(start, end),
1061 }));
1062 }
1063
1064 let negated = if matches!(self.peek(), Token::Not) {
1068 self.advance()?;
1069 if !matches!(self.peek(), Token::Between | Token::In) {
1070 return Err(ParseError::new(
1071 "expected BETWEEN or IN after postfix NOT".to_string(),
1072 self.position(),
1073 ));
1074 }
1075 true
1076 } else {
1077 false
1078 };
1079
1080 if self.consume(&Token::Between)? {
1082 let low = self.parse_expr_prec(34)?;
1083 self.expect(Token::And)?;
1084 let high = self.parse_expr_prec(34)?;
1085 let end = self.position();
1086 return Ok(Some(Expr::Between {
1087 target: Box::new(left.clone()),
1088 low: Box::new(low),
1089 high: Box::new(high),
1090 negated,
1091 span: Span::new(start, end),
1092 }));
1093 }
1094
1095 if self.consume(&Token::In)? {
1097 self.expect(Token::LParen)?;
1098 let mut values = Vec::new();
1099 if self.check(&Token::Select) {
1100 let query = self.parse_select_query()?;
1101 values.push(Expr::Subquery {
1102 query: ExprSubquery {
1103 query: Box::new(query),
1104 },
1105 span: Span::new(self.span_start_of(left), self.position()),
1106 });
1107 } else if !self.check(&Token::RParen) {
1108 loop {
1109 values.push(self.parse_expr_prec(0)?);
1110 if !self.consume(&Token::Comma)? {
1111 break;
1112 }
1113 }
1114 }
1115 self.expect(Token::RParen)?;
1116 let end = self.position();
1117 return Ok(Some(Expr::InList {
1118 target: Box::new(left.clone()),
1119 values,
1120 negated,
1121 span: Span::new(start, end),
1122 }));
1123 }
1124
1125 if negated {
1126 return Err(ParseError::new(
1130 "internal: NOT consumed without BETWEEN/IN follow".to_string(),
1131 self.position(),
1132 ));
1133 }
1134 Ok(None)
1135 }
1136
1137 fn peek_binop(&self) -> Option<(BinOp, u8)> {
1141 let op = match self.peek() {
1142 Token::Or => BinOp::Or,
1143 Token::And => BinOp::And,
1144 Token::Eq => BinOp::Eq,
1145 Token::Ne => BinOp::Ne,
1146 Token::Lt => BinOp::Lt,
1147 Token::Le => BinOp::Le,
1148 Token::Gt => BinOp::Gt,
1149 Token::Ge => BinOp::Ge,
1150 Token::DoublePipe => BinOp::Concat,
1151 Token::Plus => BinOp::Add,
1152 Token::Dash => BinOp::Sub,
1153 Token::Star => BinOp::Mul,
1154 Token::Slash => BinOp::Div,
1155 Token::Percent => BinOp::Mod,
1156 _ => return None,
1157 };
1158 Some((op, op.precedence()))
1159 }
1160
1161 fn span_start_of(&self, expr: &Expr) -> crate::lexer::Position {
1166 let s = expr.span();
1167 if s.is_synthetic() {
1168 self.position()
1169 } else {
1170 s.start
1171 }
1172 }
1173
1174 fn span_end_of(&self, expr: &Expr) -> crate::lexer::Position {
1177 let s = expr.span();
1178 if s.is_synthetic() {
1179 self.position()
1180 } else {
1181 s.end
1182 }
1183 }
1184}
1185
1186#[allow(dead_code)]
1189fn _expr_module_used(_: Expr) {}
1190
1191#[cfg(test)]
1192mod tests {
1193 use super::*;
1194 use crate::ast::FieldRef;
1195
1196 fn parse(input: &str) -> Expr {
1197 let mut parser = Parser::new(input).expect("lexer init");
1198 let expr = parser.parse_expr().expect("parse_expr");
1199 expr
1200 }
1201
1202 #[test]
1203 fn literal_integer() {
1204 let e = parse("42");
1205 match e {
1206 Expr::Literal {
1207 value: Value::Integer(42),
1208 ..
1209 } => {}
1210 other => panic!("expected Integer(42), got {other:?}"),
1211 }
1212 }
1213
1214 #[test]
1215 fn literal_float() {
1216 let e = parse("3.14");
1217 match e {
1218 Expr::Literal {
1219 value: Value::Float(f),
1220 ..
1221 } => assert!((f - 3.14).abs() < 1e-9),
1222 other => panic!("expected float literal, got {other:?}"),
1223 }
1224 }
1225
1226 #[test]
1227 fn literal_string() {
1228 let e = parse("'hello'");
1229 match e {
1230 Expr::Literal {
1231 value: Value::Text(ref s),
1232 ..
1233 } if s.as_ref() == "hello" => {}
1234 other => panic!("expected Text(hello), got {other:?}"),
1235 }
1236 }
1237
1238 #[test]
1239 fn literal_booleans_and_null() {
1240 assert!(matches!(
1241 parse("TRUE"),
1242 Expr::Literal {
1243 value: Value::Boolean(true),
1244 ..
1245 }
1246 ));
1247 assert!(matches!(
1248 parse("FALSE"),
1249 Expr::Literal {
1250 value: Value::Boolean(false),
1251 ..
1252 }
1253 ));
1254 assert!(matches!(
1255 parse("NULL"),
1256 Expr::Literal {
1257 value: Value::Null,
1258 ..
1259 }
1260 ));
1261 }
1262
1263 #[test]
1264 fn bare_column() {
1265 let e = parse("user_id");
1266 match e {
1267 Expr::Column {
1268 field: FieldRef::TableColumn { column, .. },
1269 ..
1270 } => {
1271 assert_eq!(column, "user_id");
1272 }
1273 other => panic!("expected column, got {other:?}"),
1274 }
1275 }
1276
1277 #[test]
1278 fn arithmetic_precedence_mul_over_add() {
1279 let e = parse("a + b * c");
1281 let Expr::BinaryOp {
1282 op: BinOp::Add,
1283 rhs,
1284 ..
1285 } = e
1286 else {
1287 panic!("root must be Add");
1288 };
1289 let Expr::BinaryOp { op: BinOp::Mul, .. } = *rhs else {
1290 panic!("rhs must be Mul");
1291 };
1292 }
1293
1294 #[test]
1295 fn arithmetic_left_associativity() {
1296 let e = parse("a - b - c");
1298 let Expr::BinaryOp {
1299 op: BinOp::Sub,
1300 lhs,
1301 ..
1302 } = e
1303 else {
1304 panic!("root must be Sub");
1305 };
1306 let Expr::BinaryOp { op: BinOp::Sub, .. } = *lhs else {
1307 panic!("lhs must be Sub (left-assoc)");
1308 };
1309 }
1310
1311 #[test]
1312 fn parenthesised_override() {
1313 let e = parse("(a + b) * c");
1315 let Expr::BinaryOp {
1316 op: BinOp::Mul,
1317 lhs,
1318 ..
1319 } = e
1320 else {
1321 panic!("root must be Mul");
1322 };
1323 let Expr::BinaryOp { op: BinOp::Add, .. } = *lhs else {
1324 panic!("lhs must be Add");
1325 };
1326 }
1327
1328 #[test]
1329 fn comparison_binds_weaker_than_arith() {
1330 let e = parse("a + 1 = b - 2");
1333 let Expr::BinaryOp {
1334 op: BinOp::Eq,
1335 lhs,
1336 rhs,
1337 ..
1338 } = e
1339 else {
1340 panic!("root must be Eq");
1341 };
1342 assert!(matches!(*lhs, Expr::BinaryOp { op: BinOp::Add, .. }));
1343 assert!(matches!(*rhs, Expr::BinaryOp { op: BinOp::Sub, .. }));
1344 }
1345
1346 #[test]
1347 fn and_binds_tighter_than_or() {
1348 let e = parse("a OR b AND c");
1350 let Expr::BinaryOp {
1351 op: BinOp::Or, rhs, ..
1352 } = e
1353 else {
1354 panic!("root must be Or");
1355 };
1356 assert!(matches!(*rhs, Expr::BinaryOp { op: BinOp::And, .. }));
1357 }
1358
1359 #[test]
1360 fn unary_negation() {
1361 let e = parse("-a");
1362 let Expr::UnaryOp {
1363 op: UnaryOp::Neg, ..
1364 } = e
1365 else {
1366 panic!("expected unary Neg");
1367 };
1368 }
1369
1370 #[test]
1371 fn unary_not() {
1372 let e = parse("NOT a");
1373 let Expr::UnaryOp {
1374 op: UnaryOp::Not, ..
1375 } = e
1376 else {
1377 panic!("expected unary Not");
1378 };
1379 }
1380
1381 #[test]
1382 fn concat_operator() {
1383 let e = parse("'hello' || name");
1384 let Expr::BinaryOp {
1385 op: BinOp::Concat, ..
1386 } = e
1387 else {
1388 panic!("expected Concat");
1389 };
1390 }
1391
1392 #[test]
1393 fn cast_expr() {
1394 let e = parse("CAST(age AS TEXT)");
1395 let Expr::Cast { target, .. } = e else {
1396 panic!("expected Cast");
1397 };
1398 assert_eq!(target, DataType::Text);
1399 }
1400
1401 #[test]
1402 fn case_expr() {
1403 let e = parse("CASE WHEN a = 1 THEN 'one' WHEN a = 2 THEN 'two' ELSE 'other' END");
1404 let Expr::Case {
1405 branches, else_, ..
1406 } = e
1407 else {
1408 panic!("expected Case");
1409 };
1410 assert_eq!(branches.len(), 2);
1411 assert!(else_.is_some());
1412 }
1413
1414 #[test]
1415 fn simple_case_desugars_to_equality() {
1416 let e = parse("CASE id WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 'many' END");
1417 let Expr::Case {
1418 branches, else_, ..
1419 } = e
1420 else {
1421 panic!("expected Case");
1422 };
1423 assert_eq!(branches.len(), 2);
1424 assert!(else_.is_some());
1425 for (cond, _) in &branches {
1427 let Expr::BinaryOp { op, lhs, .. } = cond else {
1428 panic!("expected desugared equality condition");
1429 };
1430 assert_eq!(*op, BinOp::Eq);
1431 assert!(matches!(**lhs, Expr::Column { .. }));
1432 }
1433 }
1434
1435 #[test]
1436 fn is_null_postfix() {
1437 let e = parse("name IS NULL");
1438 assert!(matches!(e, Expr::IsNull { negated: false, .. }));
1439 }
1440
1441 #[test]
1442 fn is_not_null_postfix() {
1443 let e = parse("name IS NOT NULL");
1444 assert!(matches!(e, Expr::IsNull { negated: true, .. }));
1445 }
1446
1447 #[test]
1448 fn between_with_columns() {
1449 let e = parse("temp BETWEEN min_t AND max_t");
1450 let Expr::Between {
1451 target,
1452 low,
1453 high,
1454 negated,
1455 ..
1456 } = e
1457 else {
1458 panic!("expected Between");
1459 };
1460 assert!(!negated);
1461 assert!(matches!(*target, Expr::Column { .. }));
1462 assert!(matches!(*low, Expr::Column { .. }));
1463 assert!(matches!(*high, Expr::Column { .. }));
1464 }
1465
1466 #[test]
1467 fn not_between_negates() {
1468 let e = parse("temp NOT BETWEEN 0 AND 100");
1469 let Expr::Between { negated: true, .. } = e else {
1470 panic!("expected negated Between");
1471 };
1472 }
1473
1474 #[test]
1475 fn in_list_literal() {
1476 let e = parse("status IN (1, 2, 3)");
1477 let Expr::InList {
1478 values, negated, ..
1479 } = e
1480 else {
1481 panic!("expected InList");
1482 };
1483 assert!(!negated);
1484 assert_eq!(values.len(), 3);
1485 }
1486
1487 #[test]
1488 fn not_in_list() {
1489 let e = parse("status NOT IN (1, 2)");
1490 let Expr::InList { negated: true, .. } = e else {
1491 panic!("expected negated InList");
1492 };
1493 }
1494
1495 #[test]
1496 fn function_call_with_args() {
1497 let e = parse("UPPER(name)");
1498 let Expr::FunctionCall { name, args, .. } = e else {
1499 panic!("expected FunctionCall");
1500 };
1501 assert_eq!(name, "UPPER");
1502 assert_eq!(args.len(), 1);
1503 }
1504
1505 #[test]
1506 fn nested_function_call() {
1507 let e = parse("COALESCE(a, UPPER(b))");
1508 let Expr::FunctionCall { name, args, .. } = e else {
1509 panic!("expected FunctionCall");
1510 };
1511 assert_eq!(name, "COALESCE");
1512 assert_eq!(args.len(), 2);
1513 assert!(matches!(&args[1], Expr::FunctionCall { .. }));
1514 }
1515
1516 #[test]
1517 fn duration_literal_parses_as_text() {
1518 let e = parse("time_bucket(5m)");
1519 let Expr::FunctionCall { name, args, .. } = e else {
1520 panic!("expected FunctionCall, got {e:?}");
1521 };
1522 assert_eq!(name.to_uppercase(), "TIME_BUCKET");
1523 assert_eq!(args.len(), 1);
1524 assert!(
1525 matches!(&args[0], Expr::Literal { value: Value::Text(s), .. } if s.as_ref() == "5m"),
1526 "expected Text(\"5m\"), got {:?}",
1527 args[0]
1528 );
1529 }
1530
1531 #[test]
1532 fn placeholder_dollar_one() {
1533 let e = parse("$1");
1534 match e {
1535 Expr::Parameter { index: 0, .. } => {}
1536 other => panic!("expected Parameter(0), got {other:?}"),
1537 }
1538 }
1539
1540 #[test]
1541 fn placeholder_dollar_n() {
1542 let e = parse("$7");
1543 match e {
1544 Expr::Parameter { index: 6, .. } => {}
1545 other => panic!("expected Parameter(6), got {other:?}"),
1546 }
1547 }
1548
1549 #[test]
1550 fn placeholder_in_string_literal_is_text() {
1551 let e = parse("'$1'");
1553 match e {
1554 Expr::Literal {
1555 value: Value::Text(s),
1556 ..
1557 } if s.as_ref() == "$1" => {}
1558 other => panic!("expected text literal '$1', got {other:?}"),
1559 }
1560 }
1561
1562 #[test]
1563 fn placeholder_in_comparison() {
1564 let e = parse("id = $1");
1566 let Expr::BinaryOp {
1567 op: BinOp::Eq, rhs, ..
1568 } = e
1569 else {
1570 panic!("root must be Eq");
1571 };
1572 assert!(matches!(*rhs, Expr::Parameter { index: 0, .. }));
1573 }
1574
1575 #[test]
1576 fn placeholder_zero_rejected() {
1577 let mut parser = Parser::new("$0").expect("lexer");
1578 let err = parser.parse_expr().unwrap_err();
1579 assert!(err.to_string().contains("placeholder"));
1580 }
1581
1582 #[test]
1583 fn placeholder_question_single() {
1584 let e = parse("?");
1586 match e {
1587 Expr::Parameter { index: 0, .. } => {}
1588 other => panic!("expected Parameter(0), got {other:?}"),
1589 }
1590 }
1591
1592 #[test]
1593 fn placeholder_question_numbered() {
1594 let e = parse("?7");
1595 match e {
1596 Expr::Parameter { index: 6, .. } => {}
1597 other => panic!("expected Parameter(6), got {other:?}"),
1598 }
1599 }
1600
1601 #[test]
1602 fn placeholder_question_numbered_zero_rejected() {
1603 let mut parser = Parser::new("?0").expect("lexer");
1604 let err = parser.parse_expr().unwrap_err();
1605 assert!(err.to_string().contains("placeholder"));
1606 }
1607
1608 #[test]
1609 fn placeholder_question_left_to_right() {
1610 let e = parse("id = ? AND name = ?");
1612 let Expr::BinaryOp {
1613 op: BinOp::And,
1614 lhs,
1615 rhs,
1616 ..
1617 } = e
1618 else {
1619 panic!("root must be And");
1620 };
1621 let Expr::BinaryOp {
1622 op: BinOp::Eq,
1623 rhs: r1,
1624 ..
1625 } = *lhs
1626 else {
1627 panic!("lhs must be Eq");
1628 };
1629 assert!(matches!(*r1, Expr::Parameter { index: 0, .. }));
1630 let Expr::BinaryOp {
1631 op: BinOp::Eq,
1632 rhs: r2,
1633 ..
1634 } = *rhs
1635 else {
1636 panic!("rhs must be Eq");
1637 };
1638 assert!(matches!(*r2, Expr::Parameter { index: 1, .. }));
1639 }
1640
1641 #[test]
1642 fn placeholder_question_in_string_literal_is_text() {
1643 let e = parse("'?'");
1644 match e {
1645 Expr::Literal {
1646 value: Value::Text(s),
1647 ..
1648 } if s.as_ref() == "?" => {}
1649 other => panic!("expected text literal '?', got {other:?}"),
1650 }
1651 }
1652
1653 #[test]
1654 fn placeholder_mixing_question_then_dollar_rejected() {
1655 let mut parser = Parser::new("id = ? AND x = $2").expect("lexer");
1656 let err = parser.parse_expr().err().expect("should fail");
1657 assert!(
1658 err.to_string().contains("mix"),
1659 "expected mixing error, got: {err}"
1660 );
1661 }
1662
1663 #[test]
1664 fn placeholder_mixing_dollar_then_question_rejected() {
1665 let mut parser = Parser::new("id = $1 AND x = ?").expect("lexer");
1666 let err = parser.parse_expr().err().expect("should fail");
1667 assert!(
1668 err.to_string().contains("mix"),
1669 "expected mixing error, got: {err}"
1670 );
1671 }
1672
1673 #[test]
1674 fn placeholder_question_in_comment_ignored() {
1675 let mut parser = Parser::new("-- ? ignored\n ?").expect("lexer");
1678 let e = parser.parse_expr().expect("parse_expr");
1679 match e {
1680 Expr::Parameter { index: 0, .. } => {}
1681 other => panic!("expected Parameter(0), got {other:?}"),
1682 }
1683 }
1684
1685 #[test]
1686 fn unary_plus_is_noop() {
1687 let e = parse("+42");
1688 assert!(matches!(
1689 e,
1690 Expr::Literal {
1691 value: Value::Integer(42),
1692 ..
1693 }
1694 ));
1695 }
1696
1697 #[test]
1698 fn parenthesised_select_becomes_subquery_expr() {
1699 let e = parse("(SELECT 1)");
1700 assert!(matches!(e, Expr::Subquery { .. }));
1701 }
1702
1703 #[test]
1704 fn bare_zero_arg_current_functions_parse_as_calls() {
1705 for (input, expected) in [
1706 ("CURRENT_TIMESTAMP", "CURRENT_TIMESTAMP"),
1707 ("CURRENT_DATE", "CURRENT_DATE"),
1708 ("CURRENT_TIME", "CURRENT_TIME"),
1709 ] {
1710 let e = parse(input);
1711 let Expr::FunctionCall { name, args, .. } = e else {
1712 panic!("expected FunctionCall for {input}");
1713 };
1714 assert_eq!(name, expected);
1715 assert!(args.is_empty());
1716 }
1717 }
1718
1719 #[test]
1720 fn keyword_function_names_parse_as_calls() {
1721 for (input, expected_len) in [
1722 ("COUNT(*)", 1),
1723 ("SUM(amount)", 1),
1724 ("LEFT(name, 2)", 2),
1725 ("RIGHT(name, 2)", 2),
1726 ("CONTAINS(body, 'red')", 2),
1727 ("KV(cfg, path)", 2),
1728 ] {
1729 let e = parse(input);
1730 let Expr::FunctionCall { args, .. } = e else {
1731 panic!("expected FunctionCall for {input}");
1732 };
1733 assert_eq!(args.len(), expected_len, "{input}");
1734 }
1735 }
1736
1737 #[test]
1738 fn count_distinct_lowers_to_count_distinct_function() {
1739 let e = parse("COUNT(DISTINCT user_id)");
1740 let Expr::FunctionCall { name, args, .. } = e else {
1741 panic!("expected FunctionCall");
1742 };
1743 assert_eq!(name, "COUNT_DISTINCT");
1744 assert_eq!(args.len(), 1);
1745 }
1746
1747 #[test]
1748 fn dollar_secret_and_config_refs_become_function_calls() {
1749 for (input, expected_name, expected_key) in [
1750 ("$secret.api_key", "__SECRET_REF", "red.vault/api_key"),
1751 ("$red.secret.api_key", "__SECRET_REF", "red.vault/api_key"),
1752 ("$red.secrets.api_key", "__SECRET_REF", "red.vault/api_key"),
1753 ("$config.ai.provider", "CONFIG", "red.config/ai.provider"),
1754 (
1755 "$red.config.ai.provider",
1756 "CONFIG",
1757 "red.config/ai.provider",
1758 ),
1759 ] {
1760 let e = parse(input);
1761 let Expr::FunctionCall { name, args, .. } = e else {
1762 panic!("expected FunctionCall for {input}");
1763 };
1764 assert_eq!(name, expected_name);
1765 assert!(matches!(
1766 &args[..],
1767 [Expr::Literal { value: Value::Text(key), .. }] if key.as_ref() == expected_key
1768 ));
1769 }
1770 }
1771
1772 #[test]
1773 fn dollar_ref_rejects_unknown_namespace() {
1774 let mut parser = Parser::new("$tenant.id").expect("lexer");
1775 let err = parser
1776 .parse_expr()
1777 .expect_err("unknown namespace should fail");
1778 assert!(err.to_string().contains("unknown $ reference"));
1779 }
1780
1781 #[test]
1782 fn config_and_kv_bare_path_args_lowercase_to_text() {
1783 let e = parse("CONFIG(Red.AI.Default.Provider, 'openai')");
1784 let Expr::FunctionCall { name, args, .. } = e else {
1785 panic!("expected FunctionCall");
1786 };
1787 assert_eq!(name, "CONFIG");
1788 assert_eq!(args.len(), 2);
1789 assert!(matches!(
1790 &args[0],
1791 Expr::Literal { value: Value::Text(path), .. }
1792 if path.as_ref() == "red.ai.default.provider"
1793 ));
1794 assert!(matches!(
1795 &args[1],
1796 Expr::Literal { value: Value::Text(provider), .. } if provider.as_ref() == "openai"
1797 ));
1798
1799 let e = parse("KV(cfg, default.role, LOWER(name))");
1800 let Expr::FunctionCall { name, args, .. } = e else {
1801 panic!("expected FunctionCall");
1802 };
1803 assert_eq!(name, "KV");
1804 assert!(matches!(
1805 &args[0],
1806 Expr::Literal { value: Value::Text(path), .. } if path.as_ref() == "cfg"
1807 ));
1808 assert!(matches!(
1809 &args[1],
1810 Expr::Literal { value: Value::Text(path), .. } if path.as_ref() == "default.role"
1811 ));
1812 assert!(matches!(&args[2], Expr::FunctionCall { name, .. } if name == "LOWER"));
1813 }
1814
1815 #[test]
1816 fn cast_rejects_unknown_type_name() {
1817 let mut parser = Parser::new("CAST(age AS BOGUS_TYPE)").expect("lexer");
1818 let err = parser
1819 .parse_expr()
1820 .expect_err("unknown cast target should fail");
1821 assert!(err.to_string().contains("unknown type name"));
1822 }
1823
1824 #[test]
1825 fn trim_position_and_substring_sql_forms_lower_to_function_args() {
1826 let e = parse("TRIM(LEADING 'x' FROM name)");
1827 let Expr::FunctionCall { name, args, .. } = e else {
1828 panic!("expected trim function");
1829 };
1830 assert_eq!(name, "LTRIM");
1831 assert_eq!(args.len(), 2);
1832
1833 let e = parse("TRIM(TRAILING FROM name)");
1834 let Expr::FunctionCall { name, args, .. } = e else {
1835 panic!("expected trim function");
1836 };
1837 assert_eq!(name, "RTRIM");
1838 assert_eq!(args.len(), 1);
1839
1840 let e = parse("POSITION('x' IN name)");
1841 let Expr::FunctionCall { name, args, .. } = e else {
1842 panic!("expected position function");
1843 };
1844 assert_eq!(name, "POSITION");
1845 assert_eq!(args.len(), 2);
1846
1847 let e = parse("POSITION('x', name)");
1848 let Expr::FunctionCall { args, .. } = e else {
1849 panic!("expected position function");
1850 };
1851 assert_eq!(args.len(), 2);
1852
1853 let e = parse("SUBSTRING(name FROM 2 FOR 3)");
1854 let Expr::FunctionCall { name, args, .. } = e else {
1855 panic!("expected substring function");
1856 };
1857 assert_eq!(name, "SUBSTRING");
1858 assert_eq!(args.len(), 3);
1859
1860 let e = parse("SUBSTRING(name FOR 3)");
1861 let Expr::FunctionCall { args, .. } = e else {
1862 panic!("expected substring function");
1863 };
1864 assert_eq!(args.len(), 3);
1865 assert!(matches!(
1866 args[1],
1867 Expr::Literal {
1868 value: Value::Integer(1),
1869 ..
1870 }
1871 ));
1872 }
1873
1874 #[test]
1875 fn postfix_in_accepts_subquery_and_empty_list() {
1876 let e = parse("id IN (SELECT user_id FROM users)");
1877 let Expr::InList { values, .. } = e else {
1878 panic!("expected InList");
1879 };
1880 assert!(matches!(&values[..], [Expr::Subquery { .. }]));
1881
1882 let e = parse("id IN ()");
1883 let Expr::InList { values, .. } = e else {
1884 panic!("expected InList");
1885 };
1886 assert!(values.is_empty());
1887 }
1888
1889 #[test]
1890 fn postfix_not_requires_between_or_in() {
1891 let mut parser = Parser::new("status NOT NULL").expect("lexer");
1892 let err = parser.parse_expr().expect_err("postfix NOT should fail");
1893 assert!(err.to_string().contains("BETWEEN or IN"));
1894 }
1895
1896 #[test]
1897 fn case_reports_missing_then_end_and_empty_branch() {
1898 for input in [
1899 "CASE END",
1900 "CASE WHEN a = 1 'one' END",
1901 "CASE WHEN a = 1 THEN 'one'",
1902 ] {
1903 let mut parser = Parser::new(input).expect("lexer");
1904 assert!(
1905 parser.parse_expr().is_err(),
1906 "expected CASE parse failure for {input}"
1907 );
1908 }
1909 }
1910
1911 #[test]
1912 fn span_tracks_token_range() {
1913 let mut parser = Parser::new("123 + 456").expect("lexer");
1915 let e = parser.parse_expr().expect("parse_expr");
1916 let span = e.span();
1917 assert!(!span.is_synthetic(), "root span must be real");
1918 assert!(span.start.offset < span.end.offset);
1919 }
1920
1921 fn try_parse(input: &str) -> Result<Expr, ParseError> {
1926 let mut parser = Parser::new(input).expect("lexer init");
1927 parser.parse_expr()
1928 }
1929
1930 #[test]
1931 fn window_lag_partition_and_order() {
1932 let e = parse("LAG(ts) OVER (PARTITION BY user_id ORDER BY ts)");
1933 let Expr::WindowFunctionCall {
1934 name, args, window, ..
1935 } = e
1936 else {
1937 panic!("expected WindowFunctionCall");
1938 };
1939 assert_eq!(name.to_uppercase(), "LAG");
1940 assert_eq!(args.len(), 1);
1941 assert_eq!(window.partition_by.len(), 1);
1942 assert_eq!(window.order_by.len(), 1);
1943 assert!(window.order_by[0].ascending);
1944 assert!(window.frame.is_none());
1945 }
1946
1947 #[test]
1948 fn window_row_number_empty_over() {
1949 let e = parse("ROW_NUMBER() OVER ()");
1950 let Expr::WindowFunctionCall {
1951 name, args, window, ..
1952 } = e
1953 else {
1954 panic!("expected WindowFunctionCall");
1955 };
1956 assert_eq!(name.to_uppercase(), "ROW_NUMBER");
1957 assert!(args.is_empty());
1958 assert!(window.partition_by.is_empty());
1959 assert!(window.order_by.is_empty());
1960 assert!(window.frame.is_none());
1961 }
1962
1963 #[test]
1964 fn window_sum_with_frame_rows_between() {
1965 let e = parse(
1966 "SUM(amount) OVER (PARTITION BY user_id ORDER BY ts \
1967 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)",
1968 );
1969 let Expr::WindowFunctionCall { name, window, .. } = e else {
1970 panic!("expected WindowFunctionCall");
1971 };
1972 assert_eq!(name.to_uppercase(), "SUM");
1973 let frame = window.frame.expect("frame present");
1974 assert!(matches!(frame.unit, crate::ast::WindowFrameUnit::Rows));
1975 assert!(matches!(
1976 frame.start,
1977 crate::ast::WindowFrameBound::Preceding(_)
1978 ));
1979 assert!(matches!(
1980 frame.end,
1981 Some(crate::ast::WindowFrameBound::CurrentRow)
1982 ));
1983 }
1984
1985 #[test]
1986 fn window_rank_order_desc_multiple_keys() {
1987 let e = parse("RANK() OVER (ORDER BY score DESC, ts)");
1988 let Expr::WindowFunctionCall { window, .. } = e else {
1989 panic!("expected WindowFunctionCall");
1990 };
1991 assert_eq!(window.order_by.len(), 2);
1992 assert!(!window.order_by[0].ascending);
1993 assert!(window.order_by[1].ascending);
1994 }
1995
1996 #[test]
1997 fn window_unbounded_preceding_following_frame() {
1998 let e = parse(
1999 "AVG(x) OVER (ORDER BY t \
2000 RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)",
2001 );
2002 let Expr::WindowFunctionCall { window, .. } = e else {
2003 panic!("expected WindowFunctionCall");
2004 };
2005 let frame = window.frame.expect("frame present");
2006 assert!(matches!(frame.unit, crate::ast::WindowFrameUnit::Range));
2007 assert!(matches!(
2008 frame.start,
2009 crate::ast::WindowFrameBound::UnboundedPreceding
2010 ));
2011 assert!(matches!(
2012 frame.end,
2013 Some(crate::ast::WindowFrameBound::UnboundedFollowing)
2014 ));
2015 }
2016
2017 #[test]
2018 fn window_rejects_non_window_function() {
2019 let err = try_parse("UPPER(name) OVER (PARTITION BY id)")
2021 .err()
2022 .expect("should reject scalar OVER");
2023 let msg = err.to_string();
2024 assert!(
2025 msg.contains("UPPER") || msg.contains("upper"),
2026 "error should mention function name, got: {msg}"
2027 );
2028 assert!(msg.to_ascii_uppercase().contains("OVER") || msg.contains("window"));
2029 }
2030
2031 #[test]
2032 fn window_rejects_missing_open_paren() {
2033 let err = try_parse("LAG(ts) OVER PARTITION BY user_id")
2034 .err()
2035 .expect("should reject");
2036 let msg = err.to_string();
2037 assert!(
2038 msg.contains("(") || msg.to_ascii_uppercase().contains("EXPECTED"),
2039 "got: {msg}"
2040 );
2041 }
2042
2043 #[test]
2044 fn window_rejects_invalid_frame_syntax() {
2045 let err = try_parse("LAG(ts) OVER (ORDER BY ts ROWS CURRENT)")
2047 .err()
2048 .expect("should reject");
2049 let msg = err.to_string();
2050 assert!(
2051 !msg.is_empty(),
2052 "expected non-empty error for malformed frame"
2053 );
2054 }
2055
2056 #[test]
2057 fn window_first_value_with_partition_only() {
2058 let e = parse("FIRST_VALUE(price) OVER (PARTITION BY symbol)");
2059 let Expr::WindowFunctionCall {
2060 name, window, args, ..
2061 } = e
2062 else {
2063 panic!("expected WindowFunctionCall");
2064 };
2065 assert_eq!(name.to_uppercase(), "FIRST_VALUE");
2066 assert_eq!(args.len(), 1);
2067 assert_eq!(window.partition_by.len(), 1);
2068 assert!(window.order_by.is_empty());
2069 }
2070
2071 #[test]
2072 fn window_order_nulls_first_and_last() {
2073 let e = parse("SUM(x) OVER (ORDER BY score ASC NULLS FIRST, ts DESC NULLS LAST)");
2074 let Expr::WindowFunctionCall { window, .. } = e else {
2075 panic!("expected WindowFunctionCall");
2076 };
2077 assert_eq!(window.order_by.len(), 2);
2078 assert!(window.order_by[0].ascending);
2079 assert!(window.order_by[0].nulls_first);
2080 assert!(!window.order_by[1].ascending);
2081 assert!(!window.order_by[1].nulls_first);
2082 }
2083
2084 #[test]
2085 fn window_single_bound_frames() {
2086 let e = parse("SUM(x) OVER (ORDER BY ts ROWS 3 PRECEDING)");
2087 let Expr::WindowFunctionCall { window, .. } = e else {
2088 panic!("expected WindowFunctionCall");
2089 };
2090 let frame = window.frame.expect("frame");
2091 assert!(matches!(
2092 frame.start,
2093 crate::ast::WindowFrameBound::Preceding(_)
2094 ));
2095 assert!(frame.end.is_none());
2096
2097 let e = parse("SUM(x) OVER (ORDER BY ts RANGE 1 FOLLOWING)");
2098 let Expr::WindowFunctionCall { window, .. } = e else {
2099 panic!("expected WindowFunctionCall");
2100 };
2101 let frame = window.frame.expect("frame");
2102 assert!(matches!(
2103 frame.start,
2104 crate::ast::WindowFrameBound::Following(_)
2105 ));
2106 assert!(frame.end.is_none());
2107 }
2108
2109 #[test]
2110 fn window_reports_nulls_and_frame_bound_errors() {
2111 for input in [
2112 "SUM(x) OVER (ORDER BY score NULLS MIDDLE)",
2113 "SUM(x) OVER (ORDER BY score ROWS UNBOUNDED)",
2114 "SUM(x) OVER (ORDER BY score ROWS 3)",
2115 ] {
2116 let err = try_parse(input).expect_err("window syntax should fail");
2117 assert!(!err.to_string().is_empty(), "{input}");
2118 }
2119 }
2120}