use super::base::{parse_identifier, parse_value};
use crate::ast::*;
use nom::{
IResult, Parser,
branch::alt,
bytes::complete::{tag, tag_no_case},
character::complete::multispace0,
combinator::{map, opt},
sequence::{delimited, preceded},
};
pub use super::binary_ops::{parse_additive_expr, parse_concat_expr, parse_multiplicative_expr};
pub use super::case_when::parse_case;
pub use super::functions::{parse_function_arg, parse_function_or_aggregate};
pub use super::special_funcs::parse_special_function;
pub fn parse_expression(input: &str) -> IResult<&str, Expr> {
parse_concat_expr(input)
}
pub fn parse_expression_with_alias(input: &str) -> IResult<&str, Expr> {
let (input, mut expr) = parse_expression(input)?;
let (input, _) = multispace0(input)?;
if let Ok((remaining, _)) = tag_no_case::<_, _, nom::error::Error<&str>>("as").parse(input) {
let (remaining, _) = nom::character::complete::multispace1(remaining)?;
let (remaining, alias) = parse_identifier(remaining)?;
expr = set_expr_alias(expr, alias.to_string());
return Ok((remaining, expr));
}
Ok((input, expr))
}
fn set_expr_alias(expr: Expr, alias: String) -> Expr {
match expr {
Expr::Named(name) => Expr::Aliased { name, alias },
Expr::Case {
when_clauses,
else_value,
..
} => Expr::Case {
when_clauses,
else_value,
alias: Some(alias),
},
Expr::FunctionCall { name, args, .. } => Expr::FunctionCall {
name,
args,
alias: Some(alias),
},
Expr::SpecialFunction { name, args, .. } => Expr::SpecialFunction {
name,
args,
alias: Some(alias),
},
Expr::Binary {
left, op, right, ..
} => Expr::Binary {
left,
op,
right,
alias: Some(alias),
},
Expr::JsonAccess {
column,
path_segments,
..
} => Expr::JsonAccess {
column,
path_segments,
alias: Some(alias),
},
other => other, }
}
pub fn parse_json_or_ident(input: &str) -> IResult<&str, Expr> {
let (mut input, atom) = parse_atom(input)?;
let col_name = match &atom {
Expr::Named(name) => Some(name.clone()),
_ => None,
};
let mut path_segments: Vec<(String, bool)> = Vec::new();
loop {
let (remaining, json_op) = opt(alt((tag("->>"), tag("->")))).parse(input)?;
if let Some(op) = json_op {
let (remaining, _) = multispace0(remaining)?;
let (remaining, key_val) = parse_value(remaining)?;
let path = match key_val {
Value::String(s) => s,
_ => key_val.to_string(),
};
path_segments.push((path, op == "->>"));
input = remaining;
} else {
break;
}
}
let mut expr = if !path_segments.is_empty() {
if let Some(column) = col_name {
Expr::JsonAccess {
column,
path_segments,
alias: None,
}
} else {
atom
}
} else {
atom
};
let (input, cast_type) = opt(preceded(tag("::"), parse_identifier)).parse(input)?;
if let Some(target_type) = cast_type {
expr = Expr::Cast {
expr: Box::new(expr),
target_type: target_type.to_string(),
alias: None,
};
}
Ok((input, expr))
}
fn parse_grouped_expr(input: &str) -> IResult<&str, Expr> {
use nom::character::complete::multispace0;
delimited(
(nom::character::complete::char('('), multispace0),
parse_expression,
(multispace0, nom::character::complete::char(')')),
)
.parse(input)
}
fn parse_atom(input: &str) -> IResult<&str, Expr> {
alt((
parse_grouped_expr, parse_case,
parse_special_function,
parse_function_or_aggregate,
parse_star,
parse_literal,
parse_simple_ident,
))
.parse(input)
}
fn parse_star(input: &str) -> IResult<&str, Expr> {
map(tag("*"), |_| Expr::Star).parse(input)
}
fn parse_literal(input: &str) -> IResult<&str, Expr> {
map(parse_value, |v| match v {
Value::NamedParam(name) => Expr::Named(format!(":{}", name)),
Value::Param(n) => Expr::Named(format!("${}", n)),
Value::String(s) => Expr::Named(format!("'{}'", s)),
Value::Int(n) => Expr::Named(n.to_string()),
Value::Float(f) => {
let s = f.to_string();
if s.contains('.') {
Expr::Named(s)
} else {
Expr::Named(format!("{}.0", s))
}
}
Value::Bool(b) => Expr::Named(if b {
"TRUE".to_string()
} else {
"FALSE".to_string()
}),
Value::Null => Expr::Named("NULL".to_string()),
Value::Interval { amount, unit } => Expr::Named(format!("INTERVAL '{} {}'", amount, unit)),
_ => Expr::Named(v.to_string()),
})
.parse(input)
}
fn parse_simple_ident(input: &str) -> IResult<&str, Expr> {
map(parse_identifier, |s| Expr::Named(s.to_string())).parse(input)
}