mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-07 17:24:24 +01:00
adjustments to binary functions + backtrack return Option<T> + start of unary Predicate
This commit is contained in:
@@ -73,9 +73,9 @@ pub(crate) fn one_of<'a, T, R: Rng>(choices: Vec<Box<dyn Fn(&mut R) -> T + 'a>>,
|
||||
/// The function takes a list of functions that return an Option<T>, along with number of retries
|
||||
/// to make before giving up.
|
||||
pub(crate) fn backtrack<'a, T, R: Rng>(
|
||||
mut choices: Vec<(u32, Box<dyn Fn(&mut R) -> Option<T> + 'a>)>,
|
||||
mut choices: Vec<(usize, Box<dyn Fn(&mut R) -> Option<T> + 'a>)>,
|
||||
rng: &mut R,
|
||||
) -> T {
|
||||
) -> Option<T> {
|
||||
loop {
|
||||
// If there are no more choices left, we give up
|
||||
let choices_ = choices
|
||||
@@ -84,14 +84,15 @@ pub(crate) fn backtrack<'a, T, R: Rng>(
|
||||
.filter(|(_, (retries, _))| *retries > 0)
|
||||
.collect::<Vec<_>>();
|
||||
if choices_.is_empty() {
|
||||
panic!("backtrack: no more choices left");
|
||||
tracing::trace!("backtrack: no more choices left");
|
||||
return None;
|
||||
}
|
||||
// Run a one_of on the remaining choices
|
||||
let (choice_index, choice) = pick(&choices_, rng);
|
||||
let choice_index = *choice_index;
|
||||
// If the choice returns None, we decrement the number of retries and try again
|
||||
let result = choice.1(rng);
|
||||
if let Some(result) = result {
|
||||
if result.is_some() {
|
||||
return result;
|
||||
} else {
|
||||
choices[choice_index].0 -= 1;
|
||||
|
||||
@@ -54,24 +54,24 @@ impl Predicate {
|
||||
}
|
||||
|
||||
/// Produces a true [ast::Expr::Binary] [Predicate] that is true for the provided row in the given table
|
||||
pub fn true_binary<R: rand::Rng>(rng: &mut R, (t, row): (&Table, &Vec<Value>)) -> Predicate {
|
||||
pub fn true_binary<R: rand::Rng>(rng: &mut R, t: &Table, row: &Vec<Value>) -> Predicate {
|
||||
// Pick a column
|
||||
let column_index = rng.gen_range(0..t.columns.len());
|
||||
let column = &t.columns[column_index];
|
||||
let value = &row[column_index];
|
||||
let predicate = backtrack(
|
||||
let expr = backtrack(
|
||||
vec![
|
||||
(
|
||||
1,
|
||||
Box::new(|_| {
|
||||
Some(Predicate(Expr::Binary(
|
||||
Some(Expr::Binary(
|
||||
Box::new(ast::Expr::Qualified(
|
||||
ast::Name(t.name.clone()),
|
||||
ast::Name(column.name.clone()),
|
||||
)),
|
||||
ast::Operator::Equals,
|
||||
Box::new(Expr::Literal(value.into())),
|
||||
)))
|
||||
))
|
||||
}),
|
||||
),
|
||||
(
|
||||
@@ -81,14 +81,14 @@ impl Predicate {
|
||||
if &v == value {
|
||||
None
|
||||
} else {
|
||||
Some(Predicate(Expr::Binary(
|
||||
Some(Expr::Binary(
|
||||
Box::new(ast::Expr::Qualified(
|
||||
ast::Name(t.name.clone()),
|
||||
ast::Name(column.name.clone()),
|
||||
)),
|
||||
ast::Operator::NotEquals,
|
||||
Box::new(Expr::Literal(v.into())),
|
||||
)))
|
||||
))
|
||||
}
|
||||
}),
|
||||
),
|
||||
@@ -96,35 +96,35 @@ impl Predicate {
|
||||
1,
|
||||
Box::new(|rng| {
|
||||
let lt_value = LTValue::arbitrary_from(rng, value).0;
|
||||
Some(Predicate(Expr::Binary(
|
||||
Some(Expr::Binary(
|
||||
Box::new(ast::Expr::Qualified(
|
||||
ast::Name(t.name.clone()),
|
||||
ast::Name(column.name.clone()),
|
||||
)),
|
||||
ast::Operator::Greater,
|
||||
Box::new(Expr::Literal(lt_value.into())),
|
||||
)))
|
||||
))
|
||||
}),
|
||||
),
|
||||
(
|
||||
1,
|
||||
Box::new(|rng| {
|
||||
let gt_value = GTValue::arbitrary_from(rng, value).0;
|
||||
Some(Predicate(Expr::Binary(
|
||||
Some(Expr::Binary(
|
||||
Box::new(ast::Expr::Qualified(
|
||||
ast::Name(t.name.clone()),
|
||||
ast::Name(column.name.clone()),
|
||||
)),
|
||||
ast::Operator::Less,
|
||||
Box::new(Expr::Literal(gt_value.into())),
|
||||
)))
|
||||
))
|
||||
}),
|
||||
),
|
||||
(
|
||||
1,
|
||||
Box::new(|rng| {
|
||||
LikeValue::arbitrary_from_maybe(rng, value).map(|like| {
|
||||
Predicate(Expr::Like {
|
||||
Expr::Like {
|
||||
lhs: Box::new(ast::Expr::Qualified(
|
||||
ast::Name(t.name.clone()),
|
||||
ast::Name(column.name.clone()),
|
||||
@@ -133,33 +133,34 @@ impl Predicate {
|
||||
op: ast::LikeOperator::Like,
|
||||
rhs: Box::new(Expr::Literal(like.0.into())),
|
||||
escape: None, // TODO: implement
|
||||
})
|
||||
}
|
||||
})
|
||||
}),
|
||||
),
|
||||
],
|
||||
rng,
|
||||
);
|
||||
predicate
|
||||
// Backtrack will always return Some here
|
||||
Predicate(expr.unwrap())
|
||||
}
|
||||
|
||||
/// Produces an [ast::Expr::Binary] [Predicate] that is false for the provided row in the given table
|
||||
pub fn false_binary<R: rand::Rng>(rng: &mut R, (t, row): (&Table, &Vec<Value>)) -> Predicate {
|
||||
pub fn false_binary<R: rand::Rng>(rng: &mut R, t: &Table, row: &Vec<Value>) -> Predicate {
|
||||
// Pick a column
|
||||
let column_index = rng.gen_range(0..t.columns.len());
|
||||
let column = &t.columns[column_index];
|
||||
let value = &row[column_index];
|
||||
one_of(
|
||||
let expr = one_of(
|
||||
vec![
|
||||
Box::new(|_| {
|
||||
Predicate(Expr::Binary(
|
||||
Expr::Binary(
|
||||
Box::new(ast::Expr::Qualified(
|
||||
ast::Name(t.name.clone()),
|
||||
ast::Name(column.name.clone()),
|
||||
)),
|
||||
ast::Operator::NotEquals,
|
||||
Box::new(Expr::Literal(value.into())),
|
||||
))
|
||||
)
|
||||
}),
|
||||
Box::new(|rng| {
|
||||
let v = loop {
|
||||
@@ -168,40 +169,41 @@ impl Predicate {
|
||||
break v;
|
||||
}
|
||||
};
|
||||
Predicate(Expr::Binary(
|
||||
Expr::Binary(
|
||||
Box::new(ast::Expr::Qualified(
|
||||
ast::Name(t.name.clone()),
|
||||
ast::Name(column.name.clone()),
|
||||
)),
|
||||
ast::Operator::Equals,
|
||||
Box::new(Expr::Literal(v.into())),
|
||||
))
|
||||
)
|
||||
}),
|
||||
Box::new(|rng| {
|
||||
let gt_value = GTValue::arbitrary_from(rng, value).0;
|
||||
Predicate(Expr::Binary(
|
||||
Expr::Binary(
|
||||
Box::new(ast::Expr::Qualified(
|
||||
ast::Name(t.name.clone()),
|
||||
ast::Name(column.name.clone()),
|
||||
)),
|
||||
ast::Operator::Greater,
|
||||
Box::new(Expr::Literal(gt_value.into())),
|
||||
))
|
||||
)
|
||||
}),
|
||||
Box::new(|rng| {
|
||||
let lt_value = LTValue::arbitrary_from(rng, value).0;
|
||||
Predicate(Expr::Binary(
|
||||
Expr::Binary(
|
||||
Box::new(ast::Expr::Qualified(
|
||||
ast::Name(t.name.clone()),
|
||||
ast::Name(column.name.clone()),
|
||||
)),
|
||||
ast::Operator::Less,
|
||||
Box::new(Expr::Literal(lt_value.into())),
|
||||
))
|
||||
)
|
||||
}),
|
||||
],
|
||||
rng,
|
||||
)
|
||||
);
|
||||
Predicate(expr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::model::{
|
||||
use super::{one_of, ArbitraryFrom};
|
||||
|
||||
mod binary;
|
||||
mod unary;
|
||||
|
||||
struct CompoundPredicate(Predicate);
|
||||
struct SimplePredicate(Predicate);
|
||||
@@ -52,11 +53,11 @@ impl ArbitraryFrom<(&Table, &Vec<Value>)> for Predicate {
|
||||
|
||||
// Produce some true and false predicates
|
||||
let mut true_predicates = (1..=rng.gen_range(1..=4))
|
||||
.map(|_| Predicate::true_binary(rng, (t, row)))
|
||||
.map(|_| Predicate::true_binary(rng, t, row))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let false_predicates = (0..=rng.gen_range(0..=3))
|
||||
.map(|_| Predicate::false_binary(rng, (t, row)))
|
||||
.map(|_| Predicate::false_binary(rng, t, row))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Start building a top level predicate from a true predicate
|
||||
|
||||
101
simulator/generation/predicate/unary.rs
Normal file
101
simulator/generation/predicate/unary.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
//! Contains code for generation for [ast::Expr::Unary] Predicate
|
||||
|
||||
use limbo_sqlite3_parser::ast::{self, Expr};
|
||||
|
||||
use crate::{
|
||||
generation::{
|
||||
backtrack, pick, predicate::SimplePredicate, ArbitraryFrom as _, ArbitraryFromMaybe,
|
||||
},
|
||||
model::{
|
||||
query::predicate::Predicate,
|
||||
table::{Table, Value},
|
||||
},
|
||||
};
|
||||
|
||||
pub struct TrueValue(pub Value);
|
||||
|
||||
impl ArbitraryFromMaybe<&Value> for TrueValue {
|
||||
fn arbitrary_from_maybe<R: rand::Rng>(_rng: &mut R, value: &Value) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
// If the Value is a true value return it else you cannot return a true Value
|
||||
value.into_bool().then_some(Self(value.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ArbitraryFromMaybe<&Vec<&Value>> for TrueValue {
|
||||
fn arbitrary_from_maybe<R: rand::Rng>(rng: &mut R, values: &Vec<&Value>) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if values.is_empty() {
|
||||
return Some(Self(Value::TRUE));
|
||||
}
|
||||
|
||||
let value = pick(values, rng);
|
||||
Self::arbitrary_from_maybe(rng, *value)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FalseValue(pub Value);
|
||||
|
||||
impl ArbitraryFromMaybe<&Value> for FalseValue {
|
||||
fn arbitrary_from_maybe<R: rand::Rng>(_rng: &mut R, value: &Value) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
// If the Value is a false value return it else you cannot return a false Value
|
||||
(!value.into_bool()).then_some(Self(value.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ArbitraryFromMaybe<&Vec<&Value>> for FalseValue {
|
||||
fn arbitrary_from_maybe<R: rand::Rng>(rng: &mut R, values: &Vec<&Value>) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if values.is_empty() {
|
||||
return Some(Self(Value::TRUE));
|
||||
}
|
||||
|
||||
let value = pick(values, rng);
|
||||
Self::arbitrary_from_maybe(rng, *value)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
let column_values = table
|
||||
.rows
|
||||
.iter()
|
||||
.map(|r| &r[column_index])
|
||||
.collect::<Vec<_>>();
|
||||
let num_retries = column_values.len();
|
||||
let expr = backtrack(
|
||||
vec![
|
||||
(
|
||||
num_retries,
|
||||
Box::new(|rng| {
|
||||
TrueValue::arbitrary_from_maybe(rng, &column_values).map(|value| {
|
||||
// Positive is a no-op in Sqlite
|
||||
Expr::unary(ast::UnaryOperator::Positive, Expr::Literal(value.0.into()))
|
||||
})
|
||||
}),
|
||||
),
|
||||
(
|
||||
num_retries,
|
||||
Box::new(|rng| {
|
||||
FalseValue::arbitrary_from_maybe(rng, &column_values).map(|value| {
|
||||
Expr::unary(ast::UnaryOperator::Negative, Expr::Literal(value.0.into()))
|
||||
})
|
||||
}),
|
||||
),
|
||||
],
|
||||
rng,
|
||||
);
|
||||
// If cannot generate a value
|
||||
SimplePredicate(Predicate(expr.unwrap_or(Expr::Literal(Value::TRUE.into()))))
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,7 @@ impl ArbitraryFrom<&SimulatorEnv> for Insert {
|
||||
})
|
||||
};
|
||||
|
||||
// Backtrack here cannot return None
|
||||
backtrack(
|
||||
vec![
|
||||
(1, Box::new(|rng| gen_values(rng))),
|
||||
@@ -86,6 +87,7 @@ impl ArbitraryFrom<&SimulatorEnv> for Insert {
|
||||
],
|
||||
rng,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user