use super::base::{parse_identifier, parse_value};
use crate::ast::*;
use nom::{
IResult, Parser,
bytes::complete::tag_no_case,
character::complete::{char, multispace0, multispace1},
multi::separated_list1,
};
pub fn parse_values_clause(input: &str) -> IResult<&str, Cage> {
let (input, _) = tag_no_case("values").parse(input)?;
let (input, _) = multispace1(input)?;
let (input, conditions) = parse_set_assignments(input)?;
Ok((
input,
Cage {
kind: CageKind::Payload,
conditions,
logical_op: LogicalOp::And,
},
))
}
pub fn parse_insert_values(input: &str) -> IResult<&str, Cage> {
let (input, _) = tag_no_case("values").parse(input)?;
let (input, _) = multispace1(input)?;
let (input, values) =
separated_list1((multispace0, char(','), multispace0), parse_value).parse(input)?;
let conditions: Vec<Condition> = values
.into_iter()
.enumerate()
.map(|(i, val)| {
Condition {
left: Expr::Named(format!("${}", i + 1)), op: Operator::Eq,
value: val,
is_array_unnest: false,
}
})
.collect();
Ok((
input,
Cage {
kind: CageKind::Payload,
conditions,
logical_op: LogicalOp::And,
},
))
}
pub fn parse_set_assignments(input: &str) -> IResult<&str, Vec<Condition>> {
separated_list1((multispace0, char(','), multispace0), parse_assignment).parse(input)
}
pub fn parse_assignment(input: &str) -> IResult<&str, Condition> {
use super::expressions::parse_expression;
use nom::branch::alt;
let (input, column) = parse_identifier(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char('=').parse(input)?;
let (input, _) = multispace0(input)?;
let (input, value) = alt((
parse_value,
parse_subquery_value,
nom::combinator::map(parse_expression, |expr| Value::Function(expr.to_string())),
))
.parse(input)?;
Ok((
input,
Condition {
left: Expr::Named(column.to_string()),
op: Operator::Eq,
value,
is_array_unnest: false,
},
))
}
fn parse_subquery_value(input: &str) -> IResult<&str, Value> {
let (input, _) = char('(').parse(input)?;
let (input, _) = multispace0(input)?;
let (input, subquery) = super::parse_root(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char(')').parse(input)?;
Ok((input, Value::Subquery(Box::new(subquery))))
}
pub fn parse_on_conflict(input: &str) -> IResult<&str, OnConflict> {
use nom::branch::alt;
let (input, _) = multispace0(input)?;
let (input, _) = tag_no_case("conflict").parse(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char('(').parse(input)?;
let (input, _) = multispace0(input)?;
let (input, columns) =
separated_list1((multispace0, char(','), multispace0), parse_identifier).parse(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char(')').parse(input)?;
let (input, _) = multispace0(input)?;
let (input, action) = alt((parse_conflict_nothing, parse_conflict_update)).parse(input)?;
Ok((
input,
OnConflict {
columns: columns.iter().map(|s| s.to_string()).collect(),
action,
},
))
}
fn parse_conflict_nothing(input: &str) -> IResult<&str, ConflictAction> {
use nom::combinator::value;
value(ConflictAction::DoNothing, tag_no_case("nothing")).parse(input)
}
fn parse_conflict_update(input: &str) -> IResult<&str, ConflictAction> {
let (input, _) = tag_no_case("update").parse(input)?;
let (input, _) = multispace1(input)?;
let (input, assignments) = parse_conflict_assignments(input)?;
Ok((input, ConflictAction::DoUpdate { assignments }))
}
fn parse_conflict_assignments(input: &str) -> IResult<&str, Vec<(String, Expr)>> {
separated_list1(
(multispace0, char(','), multispace0),
parse_conflict_assignment,
)
.parse(input)
}
fn parse_conflict_assignment(input: &str) -> IResult<&str, (String, Expr)> {
use super::expressions::parse_expression;
use nom::branch::alt;
let (input, column) = parse_identifier(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char('=').parse(input)?;
let (input, _) = multispace0(input)?;
let (input, expr) = alt((
nom::combinator::map(parse_value, super::expressions::value_to_expr),
parse_expression,
))
.parse(input)?;
Ok((input, (column.to_string(), expr)))
}
pub fn parse_source_query(input: &str) -> IResult<&str, Box<crate::ast::Qail>> {
let (input, _) = multispace0(input)?;
let (input, _) = tag_no_case("from").parse(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char('(').parse(input)?;
let (input, _) = multispace0(input)?;
let (input, subquery) = super::parse_root(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char(')').parse(input)?;
Ok((input, Box::new(subquery)))
}