use crate::{
db::{
predicate::{CoercionId, CompareFieldsPredicate, CompareOp, ComparePredicate, Predicate},
sql_shared::{Keyword, SqlParseError, SqlTokenCursor, TokenKind},
},
value::Value,
};
enum BetweenBound {
Literal(Value),
Field(String),
}
pub(in crate::db::predicate::parser) fn parse_in_predicate(
cursor: &mut SqlTokenCursor,
field: String,
negated: bool,
) -> Result<Predicate, SqlParseError> {
cursor.expect_lparen()?;
let mut values = Vec::new();
loop {
values.push(cursor.parse_literal()?);
if !cursor.eat_comma() {
break;
}
if matches!(cursor.peek_kind(), Some(TokenKind::RParen)) {
break;
}
}
cursor.expect_rparen()?;
let op = if negated {
CompareOp::NotIn
} else {
CompareOp::In
};
Ok(Predicate::Compare(ComparePredicate::with_coercion(
field,
op,
Value::List(values),
CoercionId::Strict,
)))
}
pub(in crate::db::predicate::parser) fn parse_between_predicate(
cursor: &mut SqlTokenCursor,
field: String,
negated: bool,
) -> Result<Predicate, SqlParseError> {
let lower = parse_between_bound(cursor)?;
cursor.expect_keyword(Keyword::And)?;
let upper = parse_between_bound(cursor)?;
Ok(if negated {
Predicate::Or(vec![
predicate_between_bound(field.clone(), CompareOp::Lt, lower),
predicate_between_bound(field, CompareOp::Gt, upper),
])
} else {
Predicate::And(vec![
predicate_between_bound(field.clone(), CompareOp::Gte, lower),
predicate_between_bound(field, CompareOp::Lte, upper),
])
})
}
fn parse_between_bound(cursor: &mut SqlTokenCursor) -> Result<BetweenBound, SqlParseError> {
if matches!(cursor.peek_kind(), Some(TokenKind::Identifier(_))) {
return cursor.expect_identifier().map(BetweenBound::Field);
}
cursor.parse_literal().map(BetweenBound::Literal)
}
pub(in crate::db::predicate::parser) fn predicate_compare(
field: String,
op: CompareOp,
value: Value,
) -> Predicate {
let coercion = match op {
CompareOp::Lt | CompareOp::Lte | CompareOp::Gt | CompareOp::Gte => {
if matches!(value, Value::Text(_)) {
CoercionId::Strict
} else {
CoercionId::NumericWiden
}
}
_ => CoercionId::Strict,
};
predicate_compare_with_coercion(field, op, value, coercion)
}
pub(in crate::db::predicate::parser) fn predicate_compare_with_coercion(
field: String,
op: CompareOp,
value: Value,
coercion: CoercionId,
) -> Predicate {
Predicate::Compare(ComparePredicate::with_coercion(field, op, value, coercion))
}
pub(in crate::db::predicate::parser) fn predicate_compare_fields(
left_field: String,
op: CompareOp,
right_field: String,
) -> Predicate {
let coercion = match op {
CompareOp::Lt | CompareOp::Lte | CompareOp::Gt | CompareOp::Gte => CoercionId::NumericWiden,
_ => CoercionId::Strict,
};
Predicate::CompareFields(CompareFieldsPredicate::with_coercion(
left_field,
op,
right_field,
coercion,
))
}
fn predicate_between_bound(field: String, op: CompareOp, bound: BetweenBound) -> Predicate {
match bound {
BetweenBound::Literal(value) => predicate_compare(field, op, value),
BetweenBound::Field(other_field) => predicate_compare_fields(field, op, other_field),
}
}