use crate::parser::{
ParseError, QueryParser,
ast::{ArgsExpr, ComparatorOp, ScalarExpr, Truth},
};
#[derive(Clone, PartialEq)]
pub enum Predicate {
And(Vec<Predicate>),
Or(Vec<Predicate>),
Compare {
left: ScalarExpr,
op: ComparatorOp,
right: ScalarExpr,
}, IsNull {
expr: ScalarExpr,
negated: bool,
},
InList {
expr: ScalarExpr,
list: Vec<ScalarExpr>,
negated: bool,
},
Like {
expr: ScalarExpr,
pattern: ScalarExpr,
negated: bool,
},
Const3(Truth),
}
impl Predicate {
pub fn is_start(parser: &QueryParser) -> bool {
parser.comparers.on.compare(parser)
}
pub fn parse_single(
parser: &mut QueryParser,
allow_wildcard: bool,
) -> Result<Self, ParseError> {
let left = ScalarExpr::parse(parser, allow_wildcard)?;
parser.next_non_whitespace();
let pivot = parser.position;
let comparator = ComparatorOp::check(parser);
if let Some(op) = comparator {
parser.next_non_whitespace();
let right = ScalarExpr::parse(parser, false)?;
return Ok(Self::Compare { left, op, right });
}
if parser.comparers.is_null.compare(parser) {
parser.jump(parser.comparers.is_null.length);
return Ok(Self::IsNull {
expr: left,
negated: false,
});
}
if parser.comparers.is_not_null.compare(parser) {
parser.jump(parser.comparers.is_not_null.length);
return Ok(Self::IsNull {
expr: left,
negated: true,
});
}
if parser.comparers.r#in.compare(parser) {
parser.jump(parser.comparers.r#in.length);
parser.next_non_whitespace();
let args_expr = ArgsExpr::parse(parser, allow_wildcard)?;
return Ok(Self::InList {
expr: left,
list: args_expr.args,
negated: false,
});
}
if parser.comparers.not_in.compare(parser) {
parser.jump(parser.comparers.not_in.length);
parser.next_non_whitespace();
let args_expr = ArgsExpr::parse(parser, allow_wildcard)?;
return Ok(Self::InList {
expr: left,
list: args_expr.args,
negated: true,
});
}
if parser.comparers.like.compare(parser) {
parser.jump(parser.comparers.like.length);
parser.next_non_whitespace();
let pattern = ScalarExpr::parse(parser, false)?;
return Ok(Self::Like {
expr: left,
pattern,
negated: false,
});
}
if parser.comparers.not_like.compare(parser) {
parser.jump(parser.comparers.not_like.length);
parser.next_non_whitespace();
let pattern = ScalarExpr::parse(parser, true)?;
return Ok(Self::Like {
expr: left,
pattern,
negated: false,
});
}
ParseError::new("Invalid predicate", pivot, parser).err()
}
pub fn parse_all(
parser: &mut QueryParser,
allow_wildcard: bool,
depth: i8,
) -> Result<Self, ParseError> {
let mut pivot = parser.position;
let mut predicates: Vec<Predicate> = vec![];
let mut and = false;
let mut or = false;
while (!parser.check_next_phase()) && (depth == 0 || parser.current() != ')') {
predicates.push(Self::parse_single(parser, allow_wildcard)?);
parser.next_non_whitespace();
if parser.comparers.and.compare(parser) {
parser.jump(parser.comparers.and.length);
parser.next_non_whitespace();
and = true;
}
if parser.comparers.or.compare(parser) {
parser.jump(parser.comparers.or.length);
parser.next_non_whitespace();
or = true;
}
if parser.current() == '(' {
parser.next();
predicates.push(Self::parse_all(parser, allow_wildcard, depth + 1)?);
parser.next();
}
pivot = parser.position;
}
if depth > 0 && parser.current() != ')' {
return ParseError::new("Invalid predicate", pivot, parser).err();
}
if and {
if predicates.len() == 1 {
return ParseError::new("Invalid predicate", pivot, parser).err();
}
return Ok(Self::And(predicates));
}
if or {
if predicates.len() == 1 {
return ParseError::new("Invalid predicate", pivot, parser).err();
}
return Ok(Self::Or(predicates));
}
if predicates.len() == 1 {
return Ok(predicates.pop().unwrap());
}
ParseError::new("Invalid predicate", pivot, parser).err()
}
pub fn parse(parser: &mut QueryParser, allow_wildcard: bool) -> Result<Self, ParseError> {
Self::parse_all(parser, allow_wildcard, 0)
}
}
use std::fmt;
impl fmt::Display for Predicate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Predicate::And(items) => {
let s = items
.iter()
.map(|p| format!("{}", p))
.collect::<Vec<_>>()
.join(" AND ");
write!(f, "({})", s)
}
Predicate::Or(items) => {
let s = items
.iter()
.map(|p| format!("{}", p))
.collect::<Vec<_>>()
.join(" OR ");
write!(f, "({})", s)
}
Predicate::Compare { left, op, right } => write!(f, "{} {} {}", left, op, right),
Predicate::IsNull { expr, negated } => {
if *negated {
write!(f, "{} IS NOT NULL", expr)
} else {
write!(f, "{} IS NULL", expr)
}
}
Predicate::InList {
expr,
list,
negated,
} => {
let items = list
.iter()
.map(|s| format!("{}", s))
.collect::<Vec<_>>()
.join(", ");
if *negated {
write!(f, "{} NOT IN [{}]", expr, items)
} else {
write!(f, "{} IN [{}]", expr, items)
}
}
Predicate::Like {
expr,
pattern,
negated,
} => {
if *negated {
write!(f, "{} NOT LIKE {}", expr, pattern)
} else {
write!(f, "{} LIKE {}", expr, pattern)
}
}
Predicate::Const3(t) => write!(f, "{}", t),
}
}
}
impl fmt::Debug for Predicate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Predicate({})", self)
}
}
#[cfg(test)]
mod tests {
use crate::parser::{
QueryParser,
ast::{ComparatorOp, Literal, Predicate, ScalarExpr, Truth},
};
#[test]
pub fn test_predicate_single_equal() {
let text = "columnA = columnB";
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::Compare {
left: _,
op,
right: _,
} => assert_eq!(op, ComparatorOp::Eq),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_not_equal_b() {
let text = r#"columnA <> "columnB""#;
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::Compare {
left: _,
op,
right: _,
} => assert_eq!(op, ComparatorOp::NotEq),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_not_equal_c() {
let text = "columnA != 'columnB'";
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::Compare {
left: _,
op,
right: _,
} => assert_eq!(op, ComparatorOp::NotEq),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_not_less_than() {
let text = "columnA < 10";
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::Compare {
left: _,
op,
right: _,
} => assert_eq!(op, ComparatorOp::Lt),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_not_less_than_or_equal() {
let text = "columnA <= 10";
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::Compare {
left: _,
op,
right: _,
} => assert_eq!(op, ComparatorOp::LtEq),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_not_greater_than() {
let text = "columnA > 10";
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::Compare {
left: _,
op,
right: _,
} => assert_eq!(op, ComparatorOp::Gt),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_not_greater_than_or_equal() {
let text = "columnA >= 10";
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::Compare {
left: _,
op,
right: _,
} => assert_eq!(op, ComparatorOp::GtEq),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_is_null() {
let text = "columnA IS NULL";
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::IsNull { expr: _, negated } => assert!(!negated),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_is_not_null() {
let text = "columnA IS NOT NULL";
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::IsNull { expr: _, negated } => assert!(negated),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_in() {
let text = "columnA IN(1, 2, 3)";
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::InList {
expr: _,
list,
negated,
} => {
assert_eq!(list.len(), 3);
assert!(!negated);
}
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_not_in() {
let text = "columnA NOT IN(1, 2, 3)";
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::InList {
expr: _,
list,
negated,
} => {
assert_eq!(list.len(), 3);
assert!(negated);
}
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_like() {
let text = "columnA LIKE '%pattern%'";
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::Like {
expr: _,
pattern: _,
negated,
} => assert!(!negated),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_not_like() {
let text = r#"columnA Not Like "%pattern%""#;
let mut parser = QueryParser::new(text);
let result =
Predicate::parse_single(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::Like {
expr: _,
pattern: _,
negated,
} => assert!(!negated),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_single_wrong_comparer() {
let text = "columnA =! 'columnB'";
let mut parser = QueryParser::new(text);
let result = Predicate::parse_single(&mut parser, false);
match result {
Ok(_) => panic!(),
Err(err) => {
assert_eq!(err.text, "=");
assert_eq!(err.start, 8);
assert_eq!(err.end, 8);
}
};
}
#[test]
pub fn test_predicate_single_without_right_side() {
let text = "columnA != ";
let mut parser = QueryParser::new(text);
let result = Predicate::parse_single(&mut parser, false);
match result {
Ok(_) => panic!(),
Err(err) => {
assert_eq!(err.text, "");
assert_eq!(err.start, 11);
assert_eq!(err.end, 11);
}
};
}
#[test]
pub fn test_predicate_and() {
let text = "columnA = columnB AND columnC = columnD";
let mut parser = QueryParser::new(text);
let result = Predicate::parse(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::And(predicates) => assert_eq!(predicates.len(), 2),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_and_p_or() {
let text = "columnA = columnB AND (columnC = columnD OR columnE = columnF)";
let mut parser = QueryParser::new(text);
let result = Predicate::parse(&mut parser, false).expect("Failed to parse predicate");
match result {
Predicate::And(predicates) => assert_eq!(predicates.len(), 2),
_ => panic!(),
};
}
#[test]
pub fn test_predicate_and_p_or_without_end() {
let text = "columnA = columnB AND (columnC = columnD OR columnE = columnF FROM";
let mut parser = QueryParser::new(text);
let result = Predicate::parse(&mut parser, false);
match result {
Ok(_) => panic!(),
Err(err) => {
assert_eq!(err.text, "");
assert_eq!(err.start, 66);
assert_eq!(err.end, 66);
}
};
}
#[test]
pub fn test_predicate_and_p_or_without_end_case2() {
let text = "columnA = columnB AND (columnC = columnD OR columnE = columnF FROM ";
let mut parser = QueryParser::new(text);
let result = Predicate::parse(&mut parser, false);
match result {
Ok(_) => panic!(),
Err(err) => {
assert_eq!(err.text, "F");
assert_eq!(err.start, 62);
assert_eq!(err.end, 62);
}
};
}
#[test]
pub fn test_predicate_and_p_or_without_start() {
let text = "columnA = columnB AND columnC = columnD OR columnE = columnF) FROM ";
let mut parser = QueryParser::new(text);
let result = Predicate::parse(&mut parser, false);
match result {
Ok(_) => panic!(),
Err(err) => {
assert_eq!(err.text, ")");
assert_eq!(err.start, 60);
assert_eq!(err.end, 60);
}
};
}
#[test]
pub fn test_predicate_double_and_p_or_without_start() {
let text = "columnA = columnB AND AND columnC = columnD OR columnE = columnF FROM ";
let mut parser = QueryParser::new(text);
let result = Predicate::parse(&mut parser, false);
match result {
Ok(_) => panic!(),
Err(err) => {
assert_eq!(err.text, "c");
assert_eq!(err.start, 26);
assert_eq!(err.end, 26);
}
};
}
fn int(value: i64) -> ScalarExpr {
ScalarExpr::Literal(Literal::Int(value))
}
#[test]
fn is_start_detects_on_keyword() {
assert!(Predicate::is_start(&QueryParser::new(
"ON people.id = orders.person_id"
)));
assert!(!Predicate::is_start(&QueryParser::new("WHERE id = 1")));
}
#[test]
fn display_and_debug_cover_predicate_variants() {
let compare = Predicate::Compare {
left: int(1),
op: ComparatorOp::Eq,
right: int(2),
};
let is_null = Predicate::IsNull {
expr: int(1),
negated: false,
};
let is_not_null = Predicate::IsNull {
expr: int(1),
negated: true,
};
let in_list = Predicate::InList {
expr: int(1),
list: vec![int(1), int(2)],
negated: false,
};
let not_in = Predicate::InList {
expr: int(1),
list: vec![int(1), int(2)],
negated: true,
};
let like = Predicate::Like {
expr: int(1),
pattern: ScalarExpr::Literal(Literal::String("%a%".to_string())),
negated: false,
};
let not_like = Predicate::Like {
expr: int(1),
pattern: ScalarExpr::Literal(Literal::String("%a%".to_string())),
negated: true,
};
let and = Predicate::And(vec![compare.clone(), is_null.clone()]);
let or = Predicate::Or(vec![compare.clone(), is_not_null.clone()]);
let truth = Predicate::Const3(Truth::Unknown);
assert_eq!(compare.to_string(), "lit: i: 1 = lit: i: 2");
assert_eq!(is_null.to_string(), "lit: i: 1 IS NULL");
assert_eq!(is_not_null.to_string(), "lit: i: 1 IS NOT NULL");
assert_eq!(in_list.to_string(), "lit: i: 1 IN [lit: i: 1, lit: i: 2]");
assert_eq!(
not_in.to_string(),
"lit: i: 1 NOT IN [lit: i: 1, lit: i: 2]"
);
assert_eq!(like.to_string(), "lit: i: 1 LIKE lit: s: \"%a%\"");
assert_eq!(not_like.to_string(), "lit: i: 1 NOT LIKE lit: s: \"%a%\"");
assert_eq!(
and.to_string(),
"(lit: i: 1 = lit: i: 2 AND lit: i: 1 IS NULL)"
);
assert_eq!(
or.to_string(),
"(lit: i: 1 = lit: i: 2 OR lit: i: 1 IS NOT NULL)"
);
assert_eq!(truth.to_string(), "Unknown");
assert_eq!(format!("{:?}", truth), "Predicate(Unknown)");
}
}