qail_core/parser/grammar/
case_when.rs

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