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