Skip to main content

qail_core/parser/grammar/
case_when.rs

1//! CASE WHEN ... THEN ... ELSE ... END expression parsing.
2
3use super::base::{parse_operator, parse_value};
4use super::expressions::{parse_expression, parse_multiplicative_expr};
5use crate::ast::*;
6use nom::{
7    IResult, Parser,
8    bytes::complete::tag_no_case,
9    character::complete::{multispace0, multispace1},
10    combinator::opt,
11    multi::separated_list0,
12    sequence::preceded,
13};
14
15/// Parse a `CASE WHEN ... THEN ... [ELSE ...] END` expression into an AST `Expr::Case`.
16pub fn parse_case(input: &str) -> IResult<&str, Expr> {
17    let (input, _) = tag_no_case("case").parse(input)?;
18    let (input, _) = multispace1(input)?;
19
20    let (input, when_clauses) = separated_list0(multispace1, parse_when).parse(input)?;
21    if when_clauses.is_empty() {
22        return Err(nom::Err::Error(nom::error::Error::new(
23            input,
24            nom::error::ErrorKind::Verify,
25        )));
26    }
27
28    let (input, _) = multispace0(input)?;
29
30    // Else - parse as expression (not just value)
31    let (input, else_value) = opt(preceded(
32        (tag_no_case("else"), multispace1),
33        parse_expression,
34    ))
35    .parse(input)?;
36
37    let (input, _) = multispace0(input)?;
38    let (input, _) = tag_no_case("end").parse(input)?;
39
40    Ok((
41        input,
42        Expr::Case {
43            when_clauses,
44            else_value: else_value.map(Box::new),
45            alias: None,
46        },
47    ))
48}
49
50/// Parse a single WHEN condition THEN expression clause
51pub fn parse_when(input: &str) -> IResult<&str, (Condition, Box<Expr>)> {
52    let (input, _) = tag_no_case("when").parse(input)?;
53    let (input, _) = multispace1(input)?;
54
55    let (input, left_expr) = parse_multiplicative_expr(input)?; // Use lower-level to avoid consuming || as OR
56    let (input, _) = multispace0(input)?;
57
58    // Operator
59    let (input, op) = parse_operator(input)?;
60    let (input, _) = multispace0(input)?;
61
62    // For IS NULL / IS NOT NULL, there's no value to parse
63    let (input, val) = if matches!(op, Operator::IsNull | Operator::IsNotNull) {
64        (input, Value::Null)
65    } else {
66        parse_value(input)?
67    };
68
69    // Use multispace0 since IS NULL already consumed trailing space
70    let (input, _) = multispace0(input)?;
71    let (input, _) = tag_no_case("then").parse(input)?;
72    let (input, _) = multispace1(input)?;
73
74    let (input, then_expr) = parse_expression(input)?;
75
76    Ok((
77        input,
78        (
79            Condition {
80                left: left_expr,
81                op,
82                value: val,
83                is_array_unnest: false,
84            },
85            Box::new(then_expr),
86        ),
87    ))
88}