small fix to binary true predicate + fuzz test for true_binary_predicate. More tests to come

This commit is contained in:
pedrocarlo
2025-06-06 02:58:00 -03:00
parent 3068c3398e
commit b60037255b
4 changed files with 66 additions and 4 deletions

View File

@@ -123,6 +123,7 @@ impl Predicate {
(
1,
Box::new(|rng| {
// TODO: generation for Like and Glob expressions should be extracted to different module
LikeValue::arbitrary_from_maybe(rng, value).map(|like| {
Expr::Like {
lhs: Box::new(ast::Expr::Qualified(
@@ -392,3 +393,52 @@ impl CompoundPredicate {
Self(predicate)
}
}
#[cfg(test)]
mod tests {
use rand::{Rng as _, SeedableRng as _};
use rand_chacha::ChaCha8Rng;
use crate::{
generation::{pick, Arbitrary, ArbitraryFrom as _},
model::{
query::predicate::{expr_to_value, Predicate},
table::{SimValue, Table},
},
};
fn get_rng() -> rand_chacha::ChaCha8Rng {
let seed = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
ChaCha8Rng::seed_from_u64(seed)
}
#[test]
fn fuzz_true_binary_predicate() {
let mut rng = get_rng();
for _ in 0..1000 {
let table = Table::arbitrary(&mut rng);
let num_rows = rng.gen_range(1..10);
let values: Vec<Vec<SimValue>> = (0..num_rows)
.map(|_| {
table
.columns
.iter()
.map(|c| SimValue::arbitrary_from(&mut rng, &c.column_type))
.collect()
})
.collect();
let row = pick(&values, &mut rng);
let predicate = Predicate::true_binary(&mut rng, &table, row);
let value = expr_to_value(&predicate.0, row, &table);
assert!(
value.as_ref().map_or(false, |value| value.into_bool()),
"Predicate: {:#?}\nValue: {:#?}",
predicate,
value
)
}
}
}

View File

@@ -97,6 +97,7 @@ impl ArbitraryFromMaybe<(&Vec<&SimValue>, bool)> for BitNotValue {
}
}
// TODO: have some more complex generation with columns names here as well
impl SimplePredicate {
/// Generates a true [ast::Expr::Unary] [SimplePredicate] from a [Table]
pub fn true_unary<R: rand::Rng>(rng: &mut R, table: &Table, column_index: usize) -> Self {

View File

@@ -30,7 +30,7 @@ impl Predicate {
// This function attempts to convert an simpler easily computable expression into values
// TODO: In the future, we can try to expand this computation if we want to support harder properties that require us
// to already know more values before hand
fn expr_to_value(expr: &ast::Expr, row: &[SimValue], table: &Table) -> Option<SimValue> {
pub fn expr_to_value(expr: &ast::Expr, row: &[SimValue], table: &Table) -> Option<SimValue> {
match expr {
ast::Expr::DoublyQualified(_, _, ast::Name(col_name))
| ast::Expr::Qualified(_, ast::Name(col_name))

View File

@@ -81,7 +81,7 @@ impl Display for SimValue {
types::Value::Null => write!(f, "NULL"),
types::Value::Integer(i) => write!(f, "{}", i),
types::Value::Float(fl) => write!(f, "{}", fl),
value @ types::Value::Text(..) => write!(f, "'{}'", value.to_string()),
value @ types::Value::Text(..) => write!(f, "'{}'", value),
types::Value::Blob(b) => write!(f, "{}", to_sqlite_blob(b)),
}
}
@@ -138,7 +138,11 @@ impl SimValue {
ast::LikeOperator::Like => {
// TODO: support ESCAPE `expr` option in AST
// TODO: regex cache
types::Value::exec_like(None, self.to_string().as_str(), other.to_string().as_str())
types::Value::exec_like(
None,
other.0.to_string().as_str(),
self.0.to_string().as_str(),
)
}
ast::LikeOperator::Match => todo!(),
ast::LikeOperator::Regexp => todo!(),
@@ -166,12 +170,19 @@ impl From<ast::Literal> for SimValue {
}
}
/// Sanitaizes a string literal by removing single quote at front and back
/// and escaping double single quotes
fn sanitize_string(input: &str) -> String {
input[1..input.len() - 1].replace("''", "'").to_string()
}
impl From<&ast::Literal> for SimValue {
fn from(value: &ast::Literal) -> Self {
let new_value = match value {
ast::Literal::Null => types::Value::Null,
ast::Literal::Numeric(number) => Numeric::from(number).into(),
ast::Literal::String(string) => types::Value::build_text(string),
// TODO: see how to avoid sanitizing here
ast::Literal::String(string) => types::Value::build_text(sanitize_string(&string)),
ast::Literal::Blob(blob) => types::Value::Blob(
blob.as_bytes()
.chunks_exact(2)