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 if let Some(rest) = path_lc
399 .strip_prefix("kv.")
400 .or_else(|| path_lc.strip_prefix("red.kv."))
401 {
402 ("__KV_REF", format!("red.kv/{rest}"))
406 } else {
407 return Err(ParseError::new(
408 format!(
409 "unknown $ reference `${path}`; expected $secret.*, $red.secret.*, $red.secrets.*, $config.*, $red.config.*, $kv.*, or $red.kv.*"
410 ),
411 self.position(),
412 ));
413 };
414 return Ok(Expr::FunctionCall {
415 name: name.to_string(),
416 args: vec![Expr::Literal {
417 value: Value::text(key),
418 span: Span::new(start, self.position()),
419 }],
420 span: Span::new(start, self.position()),
421 });
422 }
423
424 if let Some(name) = keyword_function_name(self.peek()) {
425 if matches!(self.peek_next()?, Token::LParen) {
426 self.advance()?; return self.parse_function_call_expr_with_name(start, name.to_string());
428 }
429 }
430
431 if let Token::Ident(ref name) = *self.peek() {
439 let name_upper = name.to_uppercase();
440
441 if name_upper == "CASE" {
451 return self.parse_case_expr(start);
452 }
453
454 let saved_name = name.clone();
455 self.advance()?; if matches!(self.peek(), Token::LParen) {
459 return self.parse_function_call_expr_with_name(start, saved_name);
460 }
461
462 if let Some(function_name) = bare_zero_arg_function_name(&saved_name) {
463 let end = self.position();
464 return Ok(Expr::FunctionCall {
465 name: function_name.to_string(),
466 args: Vec::new(),
467 span: Span::new(start, end),
468 });
469 }
470
471 if matches!(self.peek(), Token::Dot) {
473 let mut segments = vec![saved_name];
474 while self.consume(&Token::Dot)? {
475 segments.push(self.expect_ident_or_keyword()?);
476 }
477 if matches!(self.peek(), Token::LParen) {
478 return self.parse_function_call_expr_with_name(start, segments.join("."));
479 }
480 let field = FieldRef::TableColumn {
481 table: segments.remove(0),
482 column: segments.join("."),
483 };
484 let end = self.position();
485 return Ok(Expr::Column {
486 field,
487 span: Span::new(start, end),
488 });
489 }
490
491 let field = FieldRef::TableColumn {
493 table: String::new(),
494 column: saved_name,
495 };
496 let end = self.position();
497 return Ok(Expr::Column {
498 field,
499 span: Span::new(start, end),
500 });
501 }
502
503 let field = self.parse_field_ref()?;
508 let end = self.position();
509 Ok(Expr::Column {
510 field,
511 span: Span::new(start, end),
512 })
513 }
514
515 fn parse_dollar_ref_path(&mut self) -> Result<String, ParseError> {
516 let mut path = self.expect_ident_or_keyword()?;
517 while self.consume(&Token::Dot)? {
518 let next = self.expect_ident_or_keyword()?;
519 path = format!("{path}.{next}");
520 }
521 Ok(path)
522 }
523
524 fn parse_function_call_expr_with_name(
525 &mut self,
526 start: crate::lexer::Position,
527 function_name: String,
528 ) -> Result<Expr, ParseError> {
529 let call = self.parse_function_call_expr_with_name_inner(start, function_name)?;
530 if matches!(self.peek(), Token::Over) {
536 return self.lift_to_window_call(start, call);
537 }
538 Ok(call)
539 }
540
541 fn parse_function_call_expr_with_name_inner(
542 &mut self,
543 start: crate::lexer::Position,
544 function_name: String,
545 ) -> Result<Expr, ParseError> {
546 self.expect(Token::LParen)?;
547
548 if function_name.eq_ignore_ascii_case("CAST") {
549 let inner = self.parse_expr_prec(0)?;
550 self.expect(Token::As)?;
551 let type_name = self.expect_ident_or_keyword()?;
552 self.expect(Token::RParen)?;
553 let end = self.position();
554 let Some(target) = DataType::from_sql_name(&type_name) else {
555 return Err(ParseError::new(
556 format!("unknown type name {type_name:?} in CAST"),
560 self.position(),
561 ));
562 };
563 return Ok(Expr::Cast {
564 inner: Box::new(inner),
565 target,
566 span: Span::new(start, end),
567 });
568 }
569
570 if function_name.eq_ignore_ascii_case("TRIM") {
571 let (name, args) = self.parse_trim_expr_args()?;
572 self.expect(Token::RParen)?;
573 let end = self.position();
574 return Ok(Expr::FunctionCall {
575 name,
576 args,
577 span: Span::new(start, end),
578 });
579 }
580
581 if function_name.eq_ignore_ascii_case("POSITION") {
582 let args = self.parse_position_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("SUBSTRING") {
593 let args = self.parse_substring_expr_args()?;
594 self.expect(Token::RParen)?;
595 let end = self.position();
596 return Ok(Expr::FunctionCall {
597 name: function_name,
598 args,
599 span: Span::new(start, end),
600 });
601 }
602
603 if function_name.eq_ignore_ascii_case("COUNT") {
604 if self.consume(&Token::Distinct)? {
605 let arg = self.parse_expr_prec(0)?;
606 self.expect(Token::RParen)?;
607 let end = self.position();
608 return Ok(Expr::FunctionCall {
609 name: "COUNT_DISTINCT".to_string(),
610 args: vec![arg],
611 span: Span::new(start, end),
612 });
613 }
614
615 if self.consume(&Token::Star)? {
616 self.expect(Token::RParen)?;
617 let end = self.position();
618 return Ok(Expr::FunctionCall {
619 name: function_name,
620 args: vec![Expr::Column {
621 field: FieldRef::TableColumn {
622 table: String::new(),
623 column: "*".to_string(),
624 },
625 span: Span::synthetic(),
626 }],
627 span: Span::new(start, end),
628 });
629 }
630 }
631
632 if function_name.eq_ignore_ascii_case("CONFIG") || function_name.eq_ignore_ascii_case("KV")
643 {
644 let mut args = Vec::new();
645 if !self.check(&Token::RParen) {
646 loop {
647 args.push(self.parse_config_kv_arg(start)?);
648 if !self.consume(&Token::Comma)? {
649 break;
650 }
651 }
652 }
653 self.expect(Token::RParen)?;
654 let end = self.position();
655 return Ok(Expr::FunctionCall {
656 name: function_name,
657 args,
658 span: Span::new(start, end),
659 });
660 }
661
662 let mut args = Vec::new();
663 if !self.check(&Token::RParen) {
664 loop {
665 args.push(self.parse_expr_prec(0)?);
666 if !self.consume(&Token::Comma)? {
667 break;
668 }
669 }
670 }
671 self.expect(Token::RParen)?;
672 let end = self.position();
673 Ok(Expr::FunctionCall {
674 name: function_name,
675 args,
676 span: Span::new(start, end),
677 })
678 }
679
680 fn parse_config_kv_arg(&mut self, start: crate::lexer::Position) -> Result<Expr, ParseError> {
687 let mut is_expression_start = matches!(
692 self.peek(),
693 Token::String(_)
694 | Token::Integer(_)
695 | Token::Float(_)
696 | Token::Dollar
697 | Token::Question
698 | Token::LParen
699 );
700 if matches!(self.peek(), Token::Ident(_)) && matches!(self.peek_next()?, Token::LParen) {
703 is_expression_start = true;
704 }
705 if !is_expression_start && !self.check(&Token::RParen) {
706 let mut path = self.expect_ident_or_keyword()?;
707 while self.consume(&Token::Dot)? {
708 let next = self.expect_ident_or_keyword()?;
709 path = format!("{path}.{next}");
710 }
711 let end = self.position();
712 return Ok(Expr::Literal {
713 value: Value::text(path.to_ascii_lowercase()),
714 span: Span::new(start, end),
715 });
716 }
717 self.parse_expr_prec(0)
718 }
719
720 fn lift_to_window_call(
727 &mut self,
728 start: crate::lexer::Position,
729 call: Expr,
730 ) -> Result<Expr, ParseError> {
731 let (name, args) = match call {
732 Expr::FunctionCall { name, args, .. } => (name, args),
733 other => {
734 return Err(ParseError::new(
735 format!(
736 "OVER may only follow a function call, got {:?}",
737 std::mem::discriminant(&other)
738 ),
739 self.position(),
740 ));
741 }
742 };
743 if !is_window_eligible_function(&name) {
744 return Err(ParseError::new(
745 format!(
746 "function `{}` cannot be used with an OVER clause; \
747 expected a window function (LAG, LEAD, ROW_NUMBER, \
748 RANK, DENSE_RANK) or an aggregate",
749 name.to_uppercase()
750 ),
751 self.position(),
752 ));
753 }
754 let window = self.parse_over_clause()?;
755 let end = self.position();
756 Ok(Expr::WindowFunctionCall {
757 name,
758 args,
759 window,
760 span: Span::new(start, end),
761 })
762 }
763
764 fn parse_over_clause(&mut self) -> Result<crate::ast::WindowSpec, ParseError> {
767 self.expect(Token::Over)?;
768 self.expect(Token::LParen)?;
769
770 let mut spec = crate::ast::WindowSpec::default();
771
772 if self.consume(&Token::Partition)? {
773 self.expect(Token::By)?;
774 loop {
775 spec.partition_by.push(self.parse_expr_prec(0)?);
776 if !self.consume(&Token::Comma)? {
777 break;
778 }
779 }
780 }
781
782 if self.consume(&Token::Order)? {
783 self.expect(Token::By)?;
784 loop {
785 let expr = self.parse_expr_prec(0)?;
786 let ascending = if self.consume(&Token::Desc)? {
787 false
788 } else {
789 self.consume(&Token::Asc)?;
790 true
791 };
792 let mut nulls_first = !ascending;
795 if self.consume(&Token::Nulls)? {
796 if self.consume(&Token::First)? {
797 nulls_first = true;
798 } else if self.consume(&Token::Last)? {
799 nulls_first = false;
800 } else {
801 return Err(ParseError::new(
802 "expected FIRST or LAST after NULLS".to_string(),
803 self.position(),
804 ));
805 }
806 }
807 spec.order_by.push(crate::ast::WindowOrderItem {
808 expr,
809 ascending,
810 nulls_first,
811 });
812 if !self.consume(&Token::Comma)? {
813 break;
814 }
815 }
816 }
817
818 if matches!(self.peek(), Token::Rows | Token::Range) {
819 spec.frame = Some(self.parse_window_frame()?);
820 }
821
822 self.expect(Token::RParen)?;
823 Ok(spec)
824 }
825
826 fn parse_window_frame(&mut self) -> Result<crate::ast::WindowFrame, ParseError> {
827 let unit = if self.consume(&Token::Rows)? {
828 crate::ast::WindowFrameUnit::Rows
829 } else if self.consume(&Token::Range)? {
830 crate::ast::WindowFrameUnit::Range
831 } else {
832 return Err(ParseError::new(
833 "expected ROWS or RANGE in window frame".to_string(),
834 self.position(),
835 ));
836 };
837
838 if self.consume(&Token::Between)? {
839 let start = self.parse_window_frame_bound()?;
840 self.expect(Token::And)?;
841 let end = self.parse_window_frame_bound()?;
842 Ok(crate::ast::WindowFrame {
843 unit,
844 start,
845 end: Some(end),
846 })
847 } else {
848 let start = self.parse_window_frame_bound()?;
849 Ok(crate::ast::WindowFrame {
850 unit,
851 start,
852 end: None,
853 })
854 }
855 }
856
857 fn parse_window_frame_bound(&mut self) -> Result<crate::ast::WindowFrameBound, ParseError> {
858 use crate::ast::WindowFrameBound;
859 if self.consume(&Token::Unbounded)? {
860 if self.consume(&Token::Preceding)? {
861 return Ok(WindowFrameBound::UnboundedPreceding);
862 }
863 if self.consume(&Token::Following)? {
864 return Ok(WindowFrameBound::UnboundedFollowing);
865 }
866 return Err(ParseError::new(
867 "expected PRECEDING or FOLLOWING after UNBOUNDED".to_string(),
868 self.position(),
869 ));
870 }
871 if self.consume(&Token::Current)? {
872 self.expect(Token::Row)?;
873 return Ok(WindowFrameBound::CurrentRow);
874 }
875 let offset = self.parse_expr_prec(0)?;
877 if self.consume(&Token::Preceding)? {
878 return Ok(WindowFrameBound::Preceding(Box::new(offset)));
879 }
880 if self.consume(&Token::Following)? {
881 return Ok(WindowFrameBound::Following(Box::new(offset)));
882 }
883 Err(ParseError::new(
884 "expected PRECEDING or FOLLOWING after frame offset".to_string(),
885 self.position(),
886 ))
887 }
888
889 fn parse_case_expr(&mut self, start: crate::lexer::Position) -> Result<Expr, ParseError> {
901 self.advance()?; let selector = if matches!(self.peek(), Token::Ident(id) if id.eq_ignore_ascii_case("WHEN"))
904 {
905 None
906 } else {
907 Some(self.parse_expr_prec(0)?)
908 };
909 let mut branches: Vec<(Expr, Expr)> = Vec::new();
910 loop {
911 if !self.consume_ident_ci("WHEN")? {
912 break;
913 }
914 let when_val = self.parse_expr_prec(0)?;
915 let cond = match &selector {
918 None => when_val,
919 Some(sel) => {
920 let span = Span::new(sel.span().start, when_val.span().end);
921 Expr::BinaryOp {
922 op: BinOp::Eq,
923 lhs: Box::new(sel.clone()),
924 rhs: Box::new(when_val),
925 span,
926 }
927 }
928 };
929 if !self.consume_ident_ci("THEN")? {
930 return Err(ParseError::new(
931 "expected THEN after CASE WHEN condition".to_string(),
932 self.position(),
933 ));
934 }
935 let then_val = self.parse_expr_prec(0)?;
936 branches.push((cond, then_val));
937 }
938 if branches.is_empty() {
939 return Err(ParseError::new(
940 "CASE must have at least one WHEN branch".to_string(),
941 self.position(),
942 ));
943 }
944 let else_ = if self.consume_ident_ci("ELSE")? {
945 Some(Box::new(self.parse_expr_prec(0)?))
946 } else {
947 None
948 };
949 if !self.consume_ident_ci("END")? {
950 return Err(ParseError::new(
951 "expected END to close CASE expression".to_string(),
952 self.position(),
953 ));
954 }
955 let end = self.position();
956 Ok(Expr::Case {
957 branches,
958 else_,
959 span: Span::new(start, end),
960 })
961 }
962
963 fn parse_trim_expr_args(&mut self) -> Result<(String, Vec<Expr>), ParseError> {
964 let mut function_name = "TRIM".to_string();
965
966 if self.consume_ident_ci("LEADING")? {
967 function_name = "LTRIM".to_string();
968 } else if self.consume_ident_ci("TRAILING")? {
969 function_name = "RTRIM".to_string();
970 } else if self.consume_ident_ci("BOTH")? {
971 function_name = "TRIM".to_string();
972 }
973
974 if self.consume(&Token::From)? {
975 let source = self.parse_expr_prec(0)?;
976 return Ok((function_name, vec![source]));
977 }
978
979 let first = self.parse_expr_prec(0)?;
980
981 if self.consume(&Token::Comma)? {
982 let second = self.parse_expr_prec(0)?;
983 return Ok((function_name, vec![first, second]));
984 }
985
986 if self.consume(&Token::From)? {
987 let source = self.parse_expr_prec(0)?;
988 return Ok((function_name, vec![source, first]));
989 }
990
991 Ok((function_name, vec![first]))
992 }
993
994 fn parse_position_expr_args(&mut self) -> Result<Vec<Expr>, ParseError> {
998 let needle = self.parse_expr_prec(35)?;
1002 if !self.consume(&Token::Comma)? {
1003 self.expect(Token::In)?;
1004 }
1005 let haystack = self.parse_expr_prec(0)?;
1006 Ok(vec![needle, haystack])
1007 }
1008
1009 fn parse_substring_expr_args(&mut self) -> Result<Vec<Expr>, ParseError> {
1017 let source = self.parse_expr_prec(0)?;
1018
1019 if self.consume(&Token::Comma)? {
1020 let mut args = vec![source];
1021 loop {
1022 args.push(self.parse_expr_prec(0)?);
1023 if !self.consume(&Token::Comma)? {
1024 break;
1025 }
1026 }
1027 return Ok(args);
1028 }
1029
1030 if self.consume(&Token::From)? {
1031 let start = self.parse_expr_prec(0)?;
1032 if self.consume(&Token::For)? {
1033 let count = self.parse_expr_prec(0)?;
1034 return Ok(vec![source, start, count]);
1035 }
1036 return Ok(vec![source, start]);
1037 }
1038
1039 if self.consume(&Token::For)? {
1040 let count = self.parse_expr_prec(0)?;
1041 if self.consume(&Token::From)? {
1042 let start = self.parse_expr_prec(0)?;
1043 return Ok(vec![source, start, count]);
1044 }
1045 return Ok(vec![source, Expr::lit(Value::Integer(1)), count]);
1046 }
1047
1048 Ok(vec![source])
1049 }
1050
1051 fn try_parse_postfix(&mut self, left: &Expr) -> Result<Option<Expr>, ParseError> {
1061 let start = self.span_start_of(left);
1062
1063 if self.consume(&Token::Is)? {
1065 let negated = self.consume(&Token::Not)?;
1066 self.expect(Token::Null)?;
1067 let end = self.position();
1068 return Ok(Some(Expr::IsNull {
1069 operand: Box::new(left.clone()),
1070 negated,
1071 span: Span::new(start, end),
1072 }));
1073 }
1074
1075 let negated = if matches!(self.peek(), Token::Not) {
1079 self.advance()?;
1080 if !matches!(self.peek(), Token::Between | Token::In) {
1081 return Err(ParseError::new(
1082 "expected BETWEEN or IN after postfix NOT".to_string(),
1083 self.position(),
1084 ));
1085 }
1086 true
1087 } else {
1088 false
1089 };
1090
1091 if self.consume(&Token::Between)? {
1093 let low = self.parse_expr_prec(34)?;
1094 self.expect(Token::And)?;
1095 let high = self.parse_expr_prec(34)?;
1096 let end = self.position();
1097 return Ok(Some(Expr::Between {
1098 target: Box::new(left.clone()),
1099 low: Box::new(low),
1100 high: Box::new(high),
1101 negated,
1102 span: Span::new(start, end),
1103 }));
1104 }
1105
1106 if self.consume(&Token::In)? {
1108 self.expect(Token::LParen)?;
1109 let mut values = Vec::new();
1110 if self.check(&Token::Select) {
1111 let query = self.parse_select_query()?;
1112 values.push(Expr::Subquery {
1113 query: ExprSubquery {
1114 query: Box::new(query),
1115 },
1116 span: Span::new(self.span_start_of(left), self.position()),
1117 });
1118 } else if !self.check(&Token::RParen) {
1119 loop {
1120 values.push(self.parse_expr_prec(0)?);
1121 if !self.consume(&Token::Comma)? {
1122 break;
1123 }
1124 }
1125 }
1126 self.expect(Token::RParen)?;
1127 let end = self.position();
1128 return Ok(Some(Expr::InList {
1129 target: Box::new(left.clone()),
1130 values,
1131 negated,
1132 span: Span::new(start, end),
1133 }));
1134 }
1135
1136 if negated {
1137 return Err(ParseError::new(
1141 "internal: NOT consumed without BETWEEN/IN follow".to_string(),
1142 self.position(),
1143 ));
1144 }
1145 Ok(None)
1146 }
1147
1148 fn peek_binop(&self) -> Option<(BinOp, u8)> {
1152 let op = match self.peek() {
1153 Token::Or => BinOp::Or,
1154 Token::And => BinOp::And,
1155 Token::Eq => BinOp::Eq,
1156 Token::Ne => BinOp::Ne,
1157 Token::Lt => BinOp::Lt,
1158 Token::Le => BinOp::Le,
1159 Token::Gt => BinOp::Gt,
1160 Token::Ge => BinOp::Ge,
1161 Token::DoublePipe => BinOp::Concat,
1162 Token::Plus => BinOp::Add,
1163 Token::Dash => BinOp::Sub,
1164 Token::Star => BinOp::Mul,
1165 Token::Slash => BinOp::Div,
1166 Token::Percent => BinOp::Mod,
1167 _ => return None,
1168 };
1169 Some((op, op.precedence()))
1170 }
1171
1172 fn span_start_of(&self, expr: &Expr) -> crate::lexer::Position {
1177 let s = expr.span();
1178 if s.is_synthetic() {
1179 self.position()
1180 } else {
1181 s.start
1182 }
1183 }
1184
1185 fn span_end_of(&self, expr: &Expr) -> crate::lexer::Position {
1188 let s = expr.span();
1189 if s.is_synthetic() {
1190 self.position()
1191 } else {
1192 s.end
1193 }
1194 }
1195}
1196
1197#[allow(dead_code)]
1200fn _expr_module_used(_: Expr) {}
1201
1202#[cfg(test)]
1203mod tests {
1204 use super::*;
1205 use crate::ast::FieldRef;
1206
1207 fn parse(input: &str) -> Expr {
1208 let mut parser = Parser::new(input).expect("lexer init");
1209 let expr = parser.parse_expr().expect("parse_expr");
1210 expr
1211 }
1212
1213 #[test]
1214 fn literal_integer() {
1215 let e = parse("42");
1216 match e {
1217 Expr::Literal {
1218 value: Value::Integer(42),
1219 ..
1220 } => {}
1221 other => panic!("expected Integer(42), got {other:?}"),
1222 }
1223 }
1224
1225 #[test]
1226 fn literal_float() {
1227 let e = parse("3.14");
1228 match e {
1229 Expr::Literal {
1230 value: Value::Float(f),
1231 ..
1232 } => assert!((f - 3.14).abs() < 1e-9),
1233 other => panic!("expected float literal, got {other:?}"),
1234 }
1235 }
1236
1237 #[test]
1238 fn literal_string() {
1239 let e = parse("'hello'");
1240 match e {
1241 Expr::Literal {
1242 value: Value::Text(ref s),
1243 ..
1244 } if s.as_ref() == "hello" => {}
1245 other => panic!("expected Text(hello), got {other:?}"),
1246 }
1247 }
1248
1249 #[test]
1250 fn literal_booleans_and_null() {
1251 assert!(matches!(
1252 parse("TRUE"),
1253 Expr::Literal {
1254 value: Value::Boolean(true),
1255 ..
1256 }
1257 ));
1258 assert!(matches!(
1259 parse("FALSE"),
1260 Expr::Literal {
1261 value: Value::Boolean(false),
1262 ..
1263 }
1264 ));
1265 assert!(matches!(
1266 parse("NULL"),
1267 Expr::Literal {
1268 value: Value::Null,
1269 ..
1270 }
1271 ));
1272 }
1273
1274 #[test]
1275 fn bare_column() {
1276 let e = parse("user_id");
1277 match e {
1278 Expr::Column {
1279 field: FieldRef::TableColumn { column, .. },
1280 ..
1281 } => {
1282 assert_eq!(column, "user_id");
1283 }
1284 other => panic!("expected column, got {other:?}"),
1285 }
1286 }
1287
1288 #[test]
1289 fn arithmetic_precedence_mul_over_add() {
1290 let e = parse("a + b * c");
1292 let Expr::BinaryOp {
1293 op: BinOp::Add,
1294 rhs,
1295 ..
1296 } = e
1297 else {
1298 panic!("root must be Add");
1299 };
1300 let Expr::BinaryOp { op: BinOp::Mul, .. } = *rhs else {
1301 panic!("rhs must be Mul");
1302 };
1303 }
1304
1305 #[test]
1306 fn arithmetic_left_associativity() {
1307 let e = parse("a - b - c");
1309 let Expr::BinaryOp {
1310 op: BinOp::Sub,
1311 lhs,
1312 ..
1313 } = e
1314 else {
1315 panic!("root must be Sub");
1316 };
1317 let Expr::BinaryOp { op: BinOp::Sub, .. } = *lhs else {
1318 panic!("lhs must be Sub (left-assoc)");
1319 };
1320 }
1321
1322 #[test]
1323 fn parenthesised_override() {
1324 let e = parse("(a + b) * c");
1326 let Expr::BinaryOp {
1327 op: BinOp::Mul,
1328 lhs,
1329 ..
1330 } = e
1331 else {
1332 panic!("root must be Mul");
1333 };
1334 let Expr::BinaryOp { op: BinOp::Add, .. } = *lhs else {
1335 panic!("lhs must be Add");
1336 };
1337 }
1338
1339 #[test]
1340 fn comparison_binds_weaker_than_arith() {
1341 let e = parse("a + 1 = b - 2");
1344 let Expr::BinaryOp {
1345 op: BinOp::Eq,
1346 lhs,
1347 rhs,
1348 ..
1349 } = e
1350 else {
1351 panic!("root must be Eq");
1352 };
1353 assert!(matches!(*lhs, Expr::BinaryOp { op: BinOp::Add, .. }));
1354 assert!(matches!(*rhs, Expr::BinaryOp { op: BinOp::Sub, .. }));
1355 }
1356
1357 #[test]
1358 fn and_binds_tighter_than_or() {
1359 let e = parse("a OR b AND c");
1361 let Expr::BinaryOp {
1362 op: BinOp::Or, rhs, ..
1363 } = e
1364 else {
1365 panic!("root must be Or");
1366 };
1367 assert!(matches!(*rhs, Expr::BinaryOp { op: BinOp::And, .. }));
1368 }
1369
1370 #[test]
1371 fn unary_negation() {
1372 let e = parse("-a");
1373 let Expr::UnaryOp {
1374 op: UnaryOp::Neg, ..
1375 } = e
1376 else {
1377 panic!("expected unary Neg");
1378 };
1379 }
1380
1381 #[test]
1382 fn unary_not() {
1383 let e = parse("NOT a");
1384 let Expr::UnaryOp {
1385 op: UnaryOp::Not, ..
1386 } = e
1387 else {
1388 panic!("expected unary Not");
1389 };
1390 }
1391
1392 #[test]
1393 fn concat_operator() {
1394 let e = parse("'hello' || name");
1395 let Expr::BinaryOp {
1396 op: BinOp::Concat, ..
1397 } = e
1398 else {
1399 panic!("expected Concat");
1400 };
1401 }
1402
1403 #[test]
1404 fn cast_expr() {
1405 let e = parse("CAST(age AS TEXT)");
1406 let Expr::Cast { target, .. } = e else {
1407 panic!("expected Cast");
1408 };
1409 assert_eq!(target, DataType::Text);
1410 }
1411
1412 #[test]
1413 fn case_expr() {
1414 let e = parse("CASE WHEN a = 1 THEN 'one' WHEN a = 2 THEN 'two' ELSE 'other' END");
1415 let Expr::Case {
1416 branches, else_, ..
1417 } = e
1418 else {
1419 panic!("expected Case");
1420 };
1421 assert_eq!(branches.len(), 2);
1422 assert!(else_.is_some());
1423 }
1424
1425 #[test]
1426 fn simple_case_desugars_to_equality() {
1427 let e = parse("CASE id WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 'many' END");
1428 let Expr::Case {
1429 branches, else_, ..
1430 } = e
1431 else {
1432 panic!("expected Case");
1433 };
1434 assert_eq!(branches.len(), 2);
1435 assert!(else_.is_some());
1436 for (cond, _) in &branches {
1438 let Expr::BinaryOp { op, lhs, .. } = cond else {
1439 panic!("expected desugared equality condition");
1440 };
1441 assert_eq!(*op, BinOp::Eq);
1442 assert!(matches!(**lhs, Expr::Column { .. }));
1443 }
1444 }
1445
1446 #[test]
1447 fn is_null_postfix() {
1448 let e = parse("name IS NULL");
1449 assert!(matches!(e, Expr::IsNull { negated: false, .. }));
1450 }
1451
1452 #[test]
1453 fn is_not_null_postfix() {
1454 let e = parse("name IS NOT NULL");
1455 assert!(matches!(e, Expr::IsNull { negated: true, .. }));
1456 }
1457
1458 #[test]
1459 fn between_with_columns() {
1460 let e = parse("temp BETWEEN min_t AND max_t");
1461 let Expr::Between {
1462 target,
1463 low,
1464 high,
1465 negated,
1466 ..
1467 } = e
1468 else {
1469 panic!("expected Between");
1470 };
1471 assert!(!negated);
1472 assert!(matches!(*target, Expr::Column { .. }));
1473 assert!(matches!(*low, Expr::Column { .. }));
1474 assert!(matches!(*high, Expr::Column { .. }));
1475 }
1476
1477 #[test]
1478 fn not_between_negates() {
1479 let e = parse("temp NOT BETWEEN 0 AND 100");
1480 let Expr::Between { negated: true, .. } = e else {
1481 panic!("expected negated Between");
1482 };
1483 }
1484
1485 #[test]
1486 fn in_list_literal() {
1487 let e = parse("status IN (1, 2, 3)");
1488 let Expr::InList {
1489 values, negated, ..
1490 } = e
1491 else {
1492 panic!("expected InList");
1493 };
1494 assert!(!negated);
1495 assert_eq!(values.len(), 3);
1496 }
1497
1498 #[test]
1499 fn not_in_list() {
1500 let e = parse("status NOT IN (1, 2)");
1501 let Expr::InList { negated: true, .. } = e else {
1502 panic!("expected negated InList");
1503 };
1504 }
1505
1506 #[test]
1507 fn function_call_with_args() {
1508 let e = parse("UPPER(name)");
1509 let Expr::FunctionCall { name, args, .. } = e else {
1510 panic!("expected FunctionCall");
1511 };
1512 assert_eq!(name, "UPPER");
1513 assert_eq!(args.len(), 1);
1514 }
1515
1516 #[test]
1517 fn nested_function_call() {
1518 let e = parse("COALESCE(a, UPPER(b))");
1519 let Expr::FunctionCall { name, args, .. } = e else {
1520 panic!("expected FunctionCall");
1521 };
1522 assert_eq!(name, "COALESCE");
1523 assert_eq!(args.len(), 2);
1524 assert!(matches!(&args[1], Expr::FunctionCall { .. }));
1525 }
1526
1527 #[test]
1528 fn duration_literal_parses_as_text() {
1529 let e = parse("time_bucket(5m)");
1530 let Expr::FunctionCall { name, args, .. } = e else {
1531 panic!("expected FunctionCall, got {e:?}");
1532 };
1533 assert_eq!(name.to_uppercase(), "TIME_BUCKET");
1534 assert_eq!(args.len(), 1);
1535 assert!(
1536 matches!(&args[0], Expr::Literal { value: Value::Text(s), .. } if s.as_ref() == "5m"),
1537 "expected Text(\"5m\"), got {:?}",
1538 args[0]
1539 );
1540 }
1541
1542 #[test]
1543 fn placeholder_dollar_one() {
1544 let e = parse("$1");
1545 match e {
1546 Expr::Parameter { index: 0, .. } => {}
1547 other => panic!("expected Parameter(0), got {other:?}"),
1548 }
1549 }
1550
1551 #[test]
1552 fn placeholder_dollar_n() {
1553 let e = parse("$7");
1554 match e {
1555 Expr::Parameter { index: 6, .. } => {}
1556 other => panic!("expected Parameter(6), got {other:?}"),
1557 }
1558 }
1559
1560 #[test]
1561 fn placeholder_in_string_literal_is_text() {
1562 let e = parse("'$1'");
1564 match e {
1565 Expr::Literal {
1566 value: Value::Text(s),
1567 ..
1568 } if s.as_ref() == "$1" => {}
1569 other => panic!("expected text literal '$1', got {other:?}"),
1570 }
1571 }
1572
1573 #[test]
1574 fn placeholder_in_comparison() {
1575 let e = parse("id = $1");
1577 let Expr::BinaryOp {
1578 op: BinOp::Eq, rhs, ..
1579 } = e
1580 else {
1581 panic!("root must be Eq");
1582 };
1583 assert!(matches!(*rhs, Expr::Parameter { index: 0, .. }));
1584 }
1585
1586 #[test]
1587 fn placeholder_zero_rejected() {
1588 let mut parser = Parser::new("$0").expect("lexer");
1589 let err = parser.parse_expr().unwrap_err();
1590 assert!(err.to_string().contains("placeholder"));
1591 }
1592
1593 #[test]
1594 fn placeholder_question_single() {
1595 let e = parse("?");
1597 match e {
1598 Expr::Parameter { index: 0, .. } => {}
1599 other => panic!("expected Parameter(0), got {other:?}"),
1600 }
1601 }
1602
1603 #[test]
1604 fn placeholder_question_numbered() {
1605 let e = parse("?7");
1606 match e {
1607 Expr::Parameter { index: 6, .. } => {}
1608 other => panic!("expected Parameter(6), got {other:?}"),
1609 }
1610 }
1611
1612 #[test]
1613 fn placeholder_question_numbered_zero_rejected() {
1614 let mut parser = Parser::new("?0").expect("lexer");
1615 let err = parser.parse_expr().unwrap_err();
1616 assert!(err.to_string().contains("placeholder"));
1617 }
1618
1619 #[test]
1620 fn placeholder_question_left_to_right() {
1621 let e = parse("id = ? AND name = ?");
1623 let Expr::BinaryOp {
1624 op: BinOp::And,
1625 lhs,
1626 rhs,
1627 ..
1628 } = e
1629 else {
1630 panic!("root must be And");
1631 };
1632 let Expr::BinaryOp {
1633 op: BinOp::Eq,
1634 rhs: r1,
1635 ..
1636 } = *lhs
1637 else {
1638 panic!("lhs must be Eq");
1639 };
1640 assert!(matches!(*r1, Expr::Parameter { index: 0, .. }));
1641 let Expr::BinaryOp {
1642 op: BinOp::Eq,
1643 rhs: r2,
1644 ..
1645 } = *rhs
1646 else {
1647 panic!("rhs must be Eq");
1648 };
1649 assert!(matches!(*r2, Expr::Parameter { index: 1, .. }));
1650 }
1651
1652 #[test]
1653 fn placeholder_question_in_string_literal_is_text() {
1654 let e = parse("'?'");
1655 match e {
1656 Expr::Literal {
1657 value: Value::Text(s),
1658 ..
1659 } if s.as_ref() == "?" => {}
1660 other => panic!("expected text literal '?', got {other:?}"),
1661 }
1662 }
1663
1664 #[test]
1665 fn placeholder_mixing_question_then_dollar_rejected() {
1666 let mut parser = Parser::new("id = ? AND x = $2").expect("lexer");
1667 let err = parser.parse_expr().err().expect("should fail");
1668 assert!(
1669 err.to_string().contains("mix"),
1670 "expected mixing error, got: {err}"
1671 );
1672 }
1673
1674 #[test]
1675 fn placeholder_mixing_dollar_then_question_rejected() {
1676 let mut parser = Parser::new("id = $1 AND x = ?").expect("lexer");
1677 let err = parser.parse_expr().err().expect("should fail");
1678 assert!(
1679 err.to_string().contains("mix"),
1680 "expected mixing error, got: {err}"
1681 );
1682 }
1683
1684 #[test]
1685 fn placeholder_question_in_comment_ignored() {
1686 let mut parser = Parser::new("-- ? ignored\n ?").expect("lexer");
1689 let e = parser.parse_expr().expect("parse_expr");
1690 match e {
1691 Expr::Parameter { index: 0, .. } => {}
1692 other => panic!("expected Parameter(0), got {other:?}"),
1693 }
1694 }
1695
1696 #[test]
1697 fn unary_plus_is_noop() {
1698 let e = parse("+42");
1699 assert!(matches!(
1700 e,
1701 Expr::Literal {
1702 value: Value::Integer(42),
1703 ..
1704 }
1705 ));
1706 }
1707
1708 #[test]
1709 fn parenthesised_select_becomes_subquery_expr() {
1710 let e = parse("(SELECT 1)");
1711 assert!(matches!(e, Expr::Subquery { .. }));
1712 }
1713
1714 #[test]
1715 fn bare_zero_arg_current_functions_parse_as_calls() {
1716 for (input, expected) in [
1717 ("CURRENT_TIMESTAMP", "CURRENT_TIMESTAMP"),
1718 ("CURRENT_DATE", "CURRENT_DATE"),
1719 ("CURRENT_TIME", "CURRENT_TIME"),
1720 ] {
1721 let e = parse(input);
1722 let Expr::FunctionCall { name, args, .. } = e else {
1723 panic!("expected FunctionCall for {input}");
1724 };
1725 assert_eq!(name, expected);
1726 assert!(args.is_empty());
1727 }
1728 }
1729
1730 #[test]
1731 fn keyword_function_names_parse_as_calls() {
1732 for (input, expected_len) in [
1733 ("COUNT(*)", 1),
1734 ("SUM(amount)", 1),
1735 ("LEFT(name, 2)", 2),
1736 ("RIGHT(name, 2)", 2),
1737 ("CONTAINS(body, 'red')", 2),
1738 ("KV(cfg, path)", 2),
1739 ] {
1740 let e = parse(input);
1741 let Expr::FunctionCall { args, .. } = e else {
1742 panic!("expected FunctionCall for {input}");
1743 };
1744 assert_eq!(args.len(), expected_len, "{input}");
1745 }
1746 }
1747
1748 #[test]
1749 fn count_distinct_lowers_to_count_distinct_function() {
1750 let e = parse("COUNT(DISTINCT user_id)");
1751 let Expr::FunctionCall { name, args, .. } = e else {
1752 panic!("expected FunctionCall");
1753 };
1754 assert_eq!(name, "COUNT_DISTINCT");
1755 assert_eq!(args.len(), 1);
1756 }
1757
1758 #[test]
1759 fn dollar_secret_and_config_refs_become_function_calls() {
1760 for (input, expected_name, expected_key) in [
1761 ("$secret.api_key", "__SECRET_REF", "red.vault/api_key"),
1762 ("$red.secret.api_key", "__SECRET_REF", "red.vault/api_key"),
1763 ("$red.secrets.api_key", "__SECRET_REF", "red.vault/api_key"),
1764 ("$config.ai.provider", "CONFIG", "red.config/ai.provider"),
1765 (
1766 "$red.config.ai.provider",
1767 "CONFIG",
1768 "red.config/ai.provider",
1769 ),
1770 ] {
1771 let e = parse(input);
1772 let Expr::FunctionCall { name, args, .. } = e else {
1773 panic!("expected FunctionCall for {input}");
1774 };
1775 assert_eq!(name, expected_name);
1776 assert!(matches!(
1777 &args[..],
1778 [Expr::Literal { value: Value::Text(key), .. }] if key.as_ref() == expected_key
1779 ));
1780 }
1781 }
1782
1783 #[test]
1784 fn dollar_ref_rejects_unknown_namespace() {
1785 let mut parser = Parser::new("$tenant.id").expect("lexer");
1786 let err = parser
1787 .parse_expr()
1788 .expect_err("unknown namespace should fail");
1789 assert!(err.to_string().contains("unknown $ reference"));
1790 }
1791
1792 #[test]
1793 fn config_and_kv_bare_path_args_lowercase_to_text() {
1794 let e = parse("CONFIG(Red.AI.Default.Provider, 'openai')");
1795 let Expr::FunctionCall { name, args, .. } = e else {
1796 panic!("expected FunctionCall");
1797 };
1798 assert_eq!(name, "CONFIG");
1799 assert_eq!(args.len(), 2);
1800 assert!(matches!(
1801 &args[0],
1802 Expr::Literal { value: Value::Text(path), .. }
1803 if path.as_ref() == "red.ai.default.provider"
1804 ));
1805 assert!(matches!(
1806 &args[1],
1807 Expr::Literal { value: Value::Text(provider), .. } if provider.as_ref() == "openai"
1808 ));
1809
1810 let e = parse("KV(cfg, default.role, LOWER(name))");
1811 let Expr::FunctionCall { name, args, .. } = e else {
1812 panic!("expected FunctionCall");
1813 };
1814 assert_eq!(name, "KV");
1815 assert!(matches!(
1816 &args[0],
1817 Expr::Literal { value: Value::Text(path), .. } if path.as_ref() == "cfg"
1818 ));
1819 assert!(matches!(
1820 &args[1],
1821 Expr::Literal { value: Value::Text(path), .. } if path.as_ref() == "default.role"
1822 ));
1823 assert!(matches!(&args[2], Expr::FunctionCall { name, .. } if name == "LOWER"));
1824 }
1825
1826 #[test]
1827 fn cast_rejects_unknown_type_name() {
1828 let mut parser = Parser::new("CAST(age AS BOGUS_TYPE)").expect("lexer");
1829 let err = parser
1830 .parse_expr()
1831 .expect_err("unknown cast target should fail");
1832 assert!(err.to_string().contains("unknown type name"));
1833 }
1834
1835 #[test]
1836 fn trim_position_and_substring_sql_forms_lower_to_function_args() {
1837 let e = parse("TRIM(LEADING 'x' FROM name)");
1838 let Expr::FunctionCall { name, args, .. } = e else {
1839 panic!("expected trim function");
1840 };
1841 assert_eq!(name, "LTRIM");
1842 assert_eq!(args.len(), 2);
1843
1844 let e = parse("TRIM(TRAILING FROM name)");
1845 let Expr::FunctionCall { name, args, .. } = e else {
1846 panic!("expected trim function");
1847 };
1848 assert_eq!(name, "RTRIM");
1849 assert_eq!(args.len(), 1);
1850
1851 let e = parse("POSITION('x' IN name)");
1852 let Expr::FunctionCall { name, args, .. } = e else {
1853 panic!("expected position function");
1854 };
1855 assert_eq!(name, "POSITION");
1856 assert_eq!(args.len(), 2);
1857
1858 let e = parse("POSITION('x', name)");
1859 let Expr::FunctionCall { args, .. } = e else {
1860 panic!("expected position function");
1861 };
1862 assert_eq!(args.len(), 2);
1863
1864 let e = parse("SUBSTRING(name FROM 2 FOR 3)");
1865 let Expr::FunctionCall { name, args, .. } = e else {
1866 panic!("expected substring function");
1867 };
1868 assert_eq!(name, "SUBSTRING");
1869 assert_eq!(args.len(), 3);
1870
1871 let e = parse("SUBSTRING(name FOR 3)");
1872 let Expr::FunctionCall { args, .. } = e else {
1873 panic!("expected substring function");
1874 };
1875 assert_eq!(args.len(), 3);
1876 assert!(matches!(
1877 args[1],
1878 Expr::Literal {
1879 value: Value::Integer(1),
1880 ..
1881 }
1882 ));
1883 }
1884
1885 #[test]
1886 fn postfix_in_accepts_subquery_and_empty_list() {
1887 let e = parse("id IN (SELECT user_id FROM users)");
1888 let Expr::InList { values, .. } = e else {
1889 panic!("expected InList");
1890 };
1891 assert!(matches!(&values[..], [Expr::Subquery { .. }]));
1892
1893 let e = parse("id IN ()");
1894 let Expr::InList { values, .. } = e else {
1895 panic!("expected InList");
1896 };
1897 assert!(values.is_empty());
1898 }
1899
1900 #[test]
1901 fn postfix_not_requires_between_or_in() {
1902 let mut parser = Parser::new("status NOT NULL").expect("lexer");
1903 let err = parser.parse_expr().expect_err("postfix NOT should fail");
1904 assert!(err.to_string().contains("BETWEEN or IN"));
1905 }
1906
1907 #[test]
1908 fn case_reports_missing_then_end_and_empty_branch() {
1909 for input in [
1910 "CASE END",
1911 "CASE WHEN a = 1 'one' END",
1912 "CASE WHEN a = 1 THEN 'one'",
1913 ] {
1914 let mut parser = Parser::new(input).expect("lexer");
1915 assert!(
1916 parser.parse_expr().is_err(),
1917 "expected CASE parse failure for {input}"
1918 );
1919 }
1920 }
1921
1922 #[test]
1923 fn span_tracks_token_range() {
1924 let mut parser = Parser::new("123 + 456").expect("lexer");
1926 let e = parser.parse_expr().expect("parse_expr");
1927 let span = e.span();
1928 assert!(!span.is_synthetic(), "root span must be real");
1929 assert!(span.start.offset < span.end.offset);
1930 }
1931
1932 fn try_parse(input: &str) -> Result<Expr, ParseError> {
1937 let mut parser = Parser::new(input).expect("lexer init");
1938 parser.parse_expr()
1939 }
1940
1941 #[test]
1942 fn window_lag_partition_and_order() {
1943 let e = parse("LAG(ts) OVER (PARTITION BY user_id ORDER BY ts)");
1944 let Expr::WindowFunctionCall {
1945 name, args, window, ..
1946 } = e
1947 else {
1948 panic!("expected WindowFunctionCall");
1949 };
1950 assert_eq!(name.to_uppercase(), "LAG");
1951 assert_eq!(args.len(), 1);
1952 assert_eq!(window.partition_by.len(), 1);
1953 assert_eq!(window.order_by.len(), 1);
1954 assert!(window.order_by[0].ascending);
1955 assert!(window.frame.is_none());
1956 }
1957
1958 #[test]
1959 fn window_row_number_empty_over() {
1960 let e = parse("ROW_NUMBER() OVER ()");
1961 let Expr::WindowFunctionCall {
1962 name, args, window, ..
1963 } = e
1964 else {
1965 panic!("expected WindowFunctionCall");
1966 };
1967 assert_eq!(name.to_uppercase(), "ROW_NUMBER");
1968 assert!(args.is_empty());
1969 assert!(window.partition_by.is_empty());
1970 assert!(window.order_by.is_empty());
1971 assert!(window.frame.is_none());
1972 }
1973
1974 #[test]
1975 fn window_sum_with_frame_rows_between() {
1976 let e = parse(
1977 "SUM(amount) OVER (PARTITION BY user_id ORDER BY ts \
1978 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)",
1979 );
1980 let Expr::WindowFunctionCall { name, window, .. } = e else {
1981 panic!("expected WindowFunctionCall");
1982 };
1983 assert_eq!(name.to_uppercase(), "SUM");
1984 let frame = window.frame.expect("frame present");
1985 assert!(matches!(frame.unit, crate::ast::WindowFrameUnit::Rows));
1986 assert!(matches!(
1987 frame.start,
1988 crate::ast::WindowFrameBound::Preceding(_)
1989 ));
1990 assert!(matches!(
1991 frame.end,
1992 Some(crate::ast::WindowFrameBound::CurrentRow)
1993 ));
1994 }
1995
1996 #[test]
1997 fn window_rank_order_desc_multiple_keys() {
1998 let e = parse("RANK() OVER (ORDER BY score DESC, ts)");
1999 let Expr::WindowFunctionCall { window, .. } = e else {
2000 panic!("expected WindowFunctionCall");
2001 };
2002 assert_eq!(window.order_by.len(), 2);
2003 assert!(!window.order_by[0].ascending);
2004 assert!(window.order_by[1].ascending);
2005 }
2006
2007 #[test]
2008 fn window_unbounded_preceding_following_frame() {
2009 let e = parse(
2010 "AVG(x) OVER (ORDER BY t \
2011 RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)",
2012 );
2013 let Expr::WindowFunctionCall { window, .. } = e else {
2014 panic!("expected WindowFunctionCall");
2015 };
2016 let frame = window.frame.expect("frame present");
2017 assert!(matches!(frame.unit, crate::ast::WindowFrameUnit::Range));
2018 assert!(matches!(
2019 frame.start,
2020 crate::ast::WindowFrameBound::UnboundedPreceding
2021 ));
2022 assert!(matches!(
2023 frame.end,
2024 Some(crate::ast::WindowFrameBound::UnboundedFollowing)
2025 ));
2026 }
2027
2028 #[test]
2029 fn window_rejects_non_window_function() {
2030 let err = try_parse("UPPER(name) OVER (PARTITION BY id)")
2032 .err()
2033 .expect("should reject scalar OVER");
2034 let msg = err.to_string();
2035 assert!(
2036 msg.contains("UPPER") || msg.contains("upper"),
2037 "error should mention function name, got: {msg}"
2038 );
2039 assert!(msg.to_ascii_uppercase().contains("OVER") || msg.contains("window"));
2040 }
2041
2042 #[test]
2043 fn window_rejects_missing_open_paren() {
2044 let err = try_parse("LAG(ts) OVER PARTITION BY user_id")
2045 .err()
2046 .expect("should reject");
2047 let msg = err.to_string();
2048 assert!(
2049 msg.contains("(") || msg.to_ascii_uppercase().contains("EXPECTED"),
2050 "got: {msg}"
2051 );
2052 }
2053
2054 #[test]
2055 fn window_rejects_invalid_frame_syntax() {
2056 let err = try_parse("LAG(ts) OVER (ORDER BY ts ROWS CURRENT)")
2058 .err()
2059 .expect("should reject");
2060 let msg = err.to_string();
2061 assert!(
2062 !msg.is_empty(),
2063 "expected non-empty error for malformed frame"
2064 );
2065 }
2066
2067 #[test]
2068 fn window_first_value_with_partition_only() {
2069 let e = parse("FIRST_VALUE(price) OVER (PARTITION BY symbol)");
2070 let Expr::WindowFunctionCall {
2071 name, window, args, ..
2072 } = e
2073 else {
2074 panic!("expected WindowFunctionCall");
2075 };
2076 assert_eq!(name.to_uppercase(), "FIRST_VALUE");
2077 assert_eq!(args.len(), 1);
2078 assert_eq!(window.partition_by.len(), 1);
2079 assert!(window.order_by.is_empty());
2080 }
2081
2082 #[test]
2083 fn window_order_nulls_first_and_last() {
2084 let e = parse("SUM(x) OVER (ORDER BY score ASC NULLS FIRST, ts DESC NULLS LAST)");
2085 let Expr::WindowFunctionCall { window, .. } = e else {
2086 panic!("expected WindowFunctionCall");
2087 };
2088 assert_eq!(window.order_by.len(), 2);
2089 assert!(window.order_by[0].ascending);
2090 assert!(window.order_by[0].nulls_first);
2091 assert!(!window.order_by[1].ascending);
2092 assert!(!window.order_by[1].nulls_first);
2093 }
2094
2095 #[test]
2096 fn window_single_bound_frames() {
2097 let e = parse("SUM(x) OVER (ORDER BY ts ROWS 3 PRECEDING)");
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::Preceding(_)
2105 ));
2106 assert!(frame.end.is_none());
2107
2108 let e = parse("SUM(x) OVER (ORDER BY ts RANGE 1 FOLLOWING)");
2109 let Expr::WindowFunctionCall { window, .. } = e else {
2110 panic!("expected WindowFunctionCall");
2111 };
2112 let frame = window.frame.expect("frame");
2113 assert!(matches!(
2114 frame.start,
2115 crate::ast::WindowFrameBound::Following(_)
2116 ));
2117 assert!(frame.end.is_none());
2118 }
2119
2120 #[test]
2121 fn window_reports_nulls_and_frame_bound_errors() {
2122 for input in [
2123 "SUM(x) OVER (ORDER BY score NULLS MIDDLE)",
2124 "SUM(x) OVER (ORDER BY score ROWS UNBOUNDED)",
2125 "SUM(x) OVER (ORDER BY score ROWS 3)",
2126 ] {
2127 let err = try_parse(input).expect_err("window syntax should fail");
2128 assert!(!err.to_string().is_empty(), "{input}");
2129 }
2130 }
2131}