remove previous predicate struct and rewrite generation with the new the struct

This commit is contained in:
pedrocarlo
2025-06-03 02:48:27 -03:00
parent 3e369b9dde
commit 9f2608bded
12 changed files with 614 additions and 476 deletions

View File

@@ -4,6 +4,7 @@ use limbo_sqlite3_parser::ast::{
use crate::{
generation::{gen_random_text, pick, pick_index, Arbitrary, ArbitraryFrom},
model::table::Value,
SimulatorEnv,
};
@@ -56,6 +57,7 @@ where
}
}
// Freestyling generation
impl ArbitraryFrom<&SimulatorEnv> for Expr {
fn arbitrary_from<R: rand::Rng>(rng: &mut R, t: &SimulatorEnv) -> Self {
// Loop until we get an implmeneted expression
@@ -267,6 +269,22 @@ impl ArbitraryFrom<&SimulatorEnv> for ast::Literal {
}
}
// Creates a litreal value
impl ArbitraryFrom<&Vec<&Value>> for ast::Expr {
fn arbitrary_from<R: rand::Rng>(rng: &mut R, values: &Vec<&Value>) -> Self {
if values.is_empty() {
return Self::Literal(ast::Literal::Null);
}
// TODO: for now just convert the value to an ast::Literal
let values = values
.iter()
.map(|value| ast::Expr::Literal((*value).into()))
.collect::<Vec<_>>();
pick(&values, rng).to_owned().clone()
}
}
impl ArbitraryFrom<&SimulatorEnv> for UnaryOperator {
fn arbitrary_from<R: rand::Rng>(rng: &mut R, _t: &SimulatorEnv) -> Self {
let choice = rng.gen_range(0..4);

View File

@@ -5,6 +5,7 @@ use rand::{distributions::uniform::SampleUniform, Rng};
mod expr;
pub mod plan;
mod predicate;
pub mod property;
pub mod query;
pub mod table;

View File

@@ -6,7 +6,8 @@ use serde::{Deserialize, Serialize};
use crate::{
model::{
query::{
select::{Distinctness, Predicate, ResultColumn},
predicate::Predicate,
select::{Distinctness, ResultColumn},
update::Update,
Create, CreateIndex, Delete, Drop, Insert, Query, Select,
},

View File

@@ -0,0 +1,553 @@
use limbo_sqlite3_parser::ast::{self, Expr};
use rand::{seq::SliceRandom as _, Rng};
use crate::model::{
query::predicate::Predicate,
table::{Table, Value},
};
use super::{
backtrack, one_of,
table::{GTValue, LTValue, LikeValue},
ArbitraryFrom, ArbitraryFromMaybe as _,
};
struct CompoundPredicate(Predicate);
struct SimplePredicate(Predicate);
impl ArbitraryFrom<(&Table, bool)> for SimplePredicate {
fn arbitrary_from<R: Rng>(rng: &mut R, (table, predicate_value): (&Table, bool)) -> Self {
// Pick a random column
let column_index = rng.gen_range(0..table.columns.len());
let column = &table.columns[column_index];
let column_values = table
.rows
.iter()
.map(|r| &r[column_index])
.collect::<Vec<_>>();
// Pick an operator
let operator = match predicate_value {
true => one_of(
vec![
Box::new(|rng| {
Expr::Binary(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column.name.clone()),
)),
ast::Operator::Equals,
Box::new(Expr::arbitrary_from(rng, &column_values)),
)
}),
Box::new(|rng| {
let gt_value = GTValue::arbitrary_from(rng, &column_values).0;
Expr::Binary(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
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, &column_values).0;
Expr::Binary(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column.name.clone()),
)),
ast::Operator::Less,
Box::new(Expr::Literal(lt_value.into())),
)
}),
],
rng,
),
false => one_of(
vec![
Box::new(|rng| {
Expr::Binary(
Box::new(Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column.name.clone()),
)),
ast::Operator::NotEquals,
Box::new(Expr::arbitrary_from(rng, &column_values)),
)
}),
Box::new(|rng| {
let lt_value = LTValue::arbitrary_from(rng, &column_values).0;
Expr::Binary(
Box::new(Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column.name.clone()),
)),
ast::Operator::Greater,
Box::new(Expr::Literal(lt_value.into())),
)
}),
Box::new(|rng| {
let gt_value = GTValue::arbitrary_from(rng, &column_values).0;
Expr::Binary(
Box::new(Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column.name.clone()),
)),
ast::Operator::Less,
Box::new(Expr::Literal(gt_value.into())),
)
}),
],
rng,
),
};
Self(Predicate(operator))
}
}
impl ArbitraryFrom<(&Table, bool)> for CompoundPredicate {
fn arbitrary_from<R: Rng>(rng: &mut R, (table, predicate_value): (&Table, bool)) -> Self {
// Decide if you want to create an AND or an OR
Self(if rng.gen_bool(0.7) {
// An AND for true requires each of its children to be true
// An AND for false requires at least one of its children to be false
if predicate_value {
(0..rng.gen_range(0..=3))
.map(|_| SimplePredicate::arbitrary_from(rng, (table, true)).0)
.reduce(|accum, curr| {
Predicate(Expr::Binary(
Box::new(accum.0),
ast::Operator::And,
Box::new(curr.0),
))
})
.unwrap_or(Predicate::true_()) // Empty And is True
} else {
// Create a vector of random booleans
let mut booleans = (0..rng.gen_range(0..=3))
.map(|_| rng.gen_bool(0.5))
.collect::<Vec<_>>();
let len = booleans.len();
// Make sure at least one of them is false
if !booleans.is_empty() && booleans.iter().all(|b| *b) {
booleans[rng.gen_range(0..len)] = false;
}
booleans
.iter()
.map(|b| SimplePredicate::arbitrary_from(rng, (table, *b)).0)
.reduce(|accum, curr| {
Predicate(Expr::Binary(
Box::new(accum.0),
ast::Operator::And,
Box::new(curr.0),
))
})
.unwrap_or(Predicate::true_()) // Empty And is True
}
} else {
// An OR for true requires at least one of its children to be true
// An OR for false requires each of its children to be false
if predicate_value {
// Create a vector of random booleans
let mut booleans = (0..rng.gen_range(0..=3))
.map(|_| rng.gen_bool(0.5))
.collect::<Vec<_>>();
let len = booleans.len();
// Make sure at least one of them is true
if !booleans.is_empty() && booleans.iter().all(|b| !*b) {
booleans[rng.gen_range(0..len)] = true;
}
booleans
.iter()
.map(|b| SimplePredicate::arbitrary_from(rng, (table, *b)).0)
.reduce(|accum, curr| {
Predicate(Expr::Binary(
Box::new(accum.0),
ast::Operator::Or,
Box::new(curr.0),
))
})
.unwrap_or(Predicate::false_()) // Empty Or is False
} else {
(0..rng.gen_range(0..=3))
.map(|_| SimplePredicate::arbitrary_from(rng, (table, false)).0)
.reduce(|accum, curr| {
Predicate(Expr::Binary(
Box::new(accum.0),
ast::Operator::Or,
Box::new(curr.0),
))
})
.unwrap_or(Predicate::false_()) // Empty Or is False
}
})
}
}
impl ArbitraryFrom<&Table> for Predicate {
fn arbitrary_from<R: Rng>(rng: &mut R, table: &Table) -> Self {
let predicate_value = rng.gen_bool(0.5);
CompoundPredicate::arbitrary_from(rng, (table, predicate_value)).0
}
}
impl ArbitraryFrom<(&str, &Value)> for Predicate {
fn arbitrary_from<R: Rng>(rng: &mut R, (column_name, value): (&str, &Value)) -> Self {
one_of(
vec![
Box::new(|_| {
Predicate(Expr::Binary(
Box::new(Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column_name.to_string()),
)),
ast::Operator::Equals,
Box::new(Expr::Literal(value.into())),
))
}),
Box::new(|rng| {
let gt_value = GTValue::arbitrary_from(rng, value).0;
Predicate(Expr::Binary(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column_name.to_string()),
)),
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(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column_name.to_string()),
)),
ast::Operator::Less,
Box::new(Expr::Literal(lt_value.into())),
))
}),
],
rng,
)
}
}
/// Produces a predicate that is true for the provided row in the given table
fn produce_true_predicate<R: Rng>(rng: &mut R, (t, row): (&Table, &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];
backtrack(
vec![
(
1,
Box::new(|_| {
Some(Predicate(Expr::Binary(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column.name.clone()),
)),
ast::Operator::Equals,
Box::new(Expr::Literal(value.into())),
)))
}),
),
(
1,
Box::new(|rng| {
let v = Value::arbitrary_from(rng, &column.column_type);
if &v == value {
None
} else {
Some(Predicate(Expr::Binary(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column.name.clone()),
)),
ast::Operator::NotEquals,
Box::new(Expr::Literal(v.into())),
)))
}
}),
),
(
1,
Box::new(|rng| {
let lt_value = LTValue::arbitrary_from(rng, value).0;
Some(Predicate(Expr::Binary(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
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(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
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 {
lhs: Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column.name.clone()),
)),
not: false, // TODO: also generate this value eventually
op: ast::LikeOperator::Like,
rhs: Box::new(Expr::Literal(like.0.into())),
escape: None, // TODO: implement
})
})
}),
),
],
rng,
)
}
/// Produces a predicate that is false for the provided row in the given table
fn produce_false_predicate<R: Rng>(rng: &mut R, (t, row): (&Table, &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(
vec![
Box::new(|_| {
Predicate(Expr::Binary(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column.name.clone()),
)),
ast::Operator::NotEquals,
Box::new(Expr::Literal(value.into())),
))
}),
Box::new(|rng| {
let v = loop {
let v = Value::arbitrary_from(rng, &column.column_type);
if &v != value {
break v;
}
};
Predicate(Expr::Binary(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
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(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
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(
Box::new(ast::Expr::Qualified(
ast::Name("".to_string()),
ast::Name(column.name.clone()),
)),
ast::Operator::Less,
Box::new(Expr::Literal(lt_value.into())),
))
}),
],
rng,
)
}
impl ArbitraryFrom<(&Table, &Vec<Value>)> for Predicate {
fn arbitrary_from<R: Rng>(rng: &mut R, (t, row): (&Table, &Vec<Value>)) -> Self {
// We want to produce a predicate that is true for the row
// We can do this by creating several predicates that
// are true, some that are false, combiend them in ways that correspond to the creation of a true predicate
// Produce some true and false predicates
let mut true_predicates = (1..=rng.gen_range(1..=4))
.map(|_| produce_true_predicate(rng, (t, row)))
.collect::<Vec<_>>();
let false_predicates = (0..=rng.gen_range(0..=3))
.map(|_| produce_false_predicate(rng, (t, row)))
.collect::<Vec<_>>();
// Start building a top level predicate from a true predicate
let mut result = true_predicates.pop().unwrap();
let mut predicates = true_predicates
.iter()
.map(|p| (true, p.clone()))
.chain(false_predicates.iter().map(|p| (false, p.clone())))
.collect::<Vec<_>>();
predicates.shuffle(rng);
while !predicates.is_empty() {
// Create a new predicate from at least 1 and at most 3 predicates
let context =
predicates[0..rng.gen_range(0..=usize::min(3, predicates.len()))].to_vec();
// Shift `predicates` to remove the predicates in the context
predicates = predicates[context.len()..].to_vec();
// `result` is true, so we have the following three options to make a true predicate:
// T or F
// T or T
// T and T
result = one_of(
vec![
// T or (X1 or X2 or ... or Xn)
Box::new(|_| {
Predicate(Expr::Binary(
Box::new(result.0.clone()),
ast::Operator::Or,
Box::new(
context
.iter()
.map(|(_, p)| p.clone())
.reduce(|accum, curr| {
Predicate(Expr::Binary(
Box::new(accum.0),
ast::Operator::Or,
Box::new(curr.0),
))
})
.unwrap_or(Predicate::false_())
.0,
),
))
}),
// T or (T1 and T2 and ... and Tn)
Box::new(|_| {
Predicate(Expr::Binary(
Box::new(result.0.clone()),
ast::Operator::Or,
Box::new(
context
.iter()
.map(|(_, p)| p.clone())
.reduce(|accum, curr| {
Predicate(Expr::Binary(
Box::new(accum.0),
ast::Operator::And,
Box::new(curr.0),
))
})
.unwrap_or(Predicate::true_())
.0,
),
))
}),
// T and T
Box::new(|_| {
// Check if all the predicates in the context are true
if context.iter().all(|(b, _)| *b) {
// T and (X1 or X2 or ... or Xn)
Predicate(Expr::Binary(
Box::new(result.0.clone()),
ast::Operator::And,
Box::new(
context
.iter()
.map(|(_, p)| p.clone())
.reduce(|accum, curr| {
Predicate(Expr::Binary(
Box::new(accum.0),
ast::Operator::And,
Box::new(curr.0),
))
})
.unwrap_or(Predicate::true_())
.0,
),
))
}
// Check if there is at least one true predicate
else if context.iter().any(|(b, _)| *b) {
// T and (X1 or X2 or ... or Xn)
Predicate(Expr::Binary(
Box::new(result.0.clone()),
ast::Operator::And,
Box::new(
context
.iter()
.map(|(_, p)| p.clone())
.reduce(|accum, curr| {
Predicate(Expr::Binary(
Box::new(accum.0),
ast::Operator::Or,
Box::new(curr.0),
))
})
.unwrap_or(Predicate::false_())
.0,
),
))
// Predicate::And(vec![
// result.clone(),
// Predicate::Or(context.iter().map(|(_, p)| p.clone()).collect()),
// ])
} else {
// T and (X1 or X2 or ... or Xn or TRUE)
Predicate(Expr::Binary(
Box::new(result.0.clone()),
ast::Operator::And,
Box::new(
context
.iter()
.map(|(_, p)| p.clone())
.chain(std::iter::once(Predicate::true_()))
.reduce(|accum, curr| {
Predicate(Expr::Binary(
Box::new(accum.0),
ast::Operator::Or,
Box::new(curr.0),
))
})
.unwrap() // Chain guarantees at least one value
.0,
),
))
}
}),
],
rng,
);
}
result
}
}

View File

@@ -4,7 +4,8 @@ use serde::{Deserialize, Serialize};
use crate::{
model::{
query::{
select::{Distinctness, Predicate, ResultColumn},
predicate::Predicate,
select::{Distinctness, ResultColumn},
Create, Delete, Drop, Insert, Query, Select,
},
table::Value,

View File

@@ -1,19 +1,16 @@
use std::collections::HashSet;
use crate::generation::table::{GTValue, LTValue};
use crate::generation::{one_of, Arbitrary, ArbitraryFrom};
use crate::model::query::select::{Distinctness, Predicate, ResultColumn};
use crate::generation::{Arbitrary, ArbitraryFrom};
use crate::model::query::predicate::Predicate;
use crate::model::query::select::{Distinctness, ResultColumn};
use crate::model::query::update::Update;
use crate::model::query::{Create, Delete, Drop, Insert, Query, Select};
use crate::model::table::{Table, Value};
use crate::SimulatorEnv;
use rand::seq::SliceRandom as _;
use rand::Rng;
use super::property::Remaining;
use super::table::LikeValue;
use super::{backtrack, frequency, pick, ArbitraryFromMaybe};
use super::{backtrack, frequency, pick};
impl Arbitrary for Create {
fn arbitrary<R: Rng>(rng: &mut R) -> Self {
@@ -137,342 +134,6 @@ impl ArbitraryFrom<(&SimulatorEnv, &Remaining)> for Query {
}
}
struct CompoundPredicate(Predicate);
struct SimplePredicate(Predicate);
impl ArbitraryFrom<(&Table, bool)> for SimplePredicate {
fn arbitrary_from<R: Rng>(rng: &mut R, (table, predicate_value): (&Table, bool)) -> Self {
// Pick a random column
let column_index = rng.gen_range(0..table.columns.len());
let column = &table.columns[column_index];
let column_values = table
.rows
.iter()
.map(|r| &r[column_index])
.collect::<Vec<_>>();
// Pick an operator
let operator = match predicate_value {
true => one_of(
vec![
Box::new(|rng| {
Predicate::Eq(
column.name.clone(),
Value::arbitrary_from(rng, &column_values),
)
}),
Box::new(|rng| {
Predicate::Gt(
column.name.clone(),
GTValue::arbitrary_from(rng, &column_values).0,
)
}),
Box::new(|rng| {
Predicate::Lt(
column.name.clone(),
LTValue::arbitrary_from(rng, &column_values).0,
)
}),
],
rng,
),
false => one_of(
vec![
Box::new(|rng| {
Predicate::Neq(
column.name.clone(),
Value::arbitrary_from(rng, &column.column_type),
)
}),
Box::new(|rng| {
Predicate::Gt(
column.name.clone(),
LTValue::arbitrary_from(rng, &column_values).0,
)
}),
Box::new(|rng| {
Predicate::Lt(
column.name.clone(),
GTValue::arbitrary_from(rng, &column_values).0,
)
}),
],
rng,
),
};
Self(operator)
}
}
impl ArbitraryFrom<(&Table, bool)> for CompoundPredicate {
fn arbitrary_from<R: Rng>(rng: &mut R, (table, predicate_value): (&Table, bool)) -> Self {
// Decide if you want to create an AND or an OR
Self(if rng.gen_bool(0.7) {
// An AND for true requires each of its children to be true
// An AND for false requires at least one of its children to be false
if predicate_value {
Predicate::And(
(0..rng.gen_range(0..=3))
.map(|_| SimplePredicate::arbitrary_from(rng, (table, true)).0)
.collect(),
)
} else {
// Create a vector of random booleans
let mut booleans = (0..rng.gen_range(0..=3))
.map(|_| rng.gen_bool(0.5))
.collect::<Vec<_>>();
let len = booleans.len();
// Make sure at least one of them is false
if !booleans.is_empty() && booleans.iter().all(|b| *b) {
booleans[rng.gen_range(0..len)] = false;
}
Predicate::And(
booleans
.iter()
.map(|b| SimplePredicate::arbitrary_from(rng, (table, *b)).0)
.collect(),
)
}
} else {
// An OR for true requires at least one of its children to be true
// An OR for false requires each of its children to be false
if predicate_value {
// Create a vector of random booleans
let mut booleans = (0..rng.gen_range(0..=3))
.map(|_| rng.gen_bool(0.5))
.collect::<Vec<_>>();
let len = booleans.len();
// Make sure at least one of them is true
if !booleans.is_empty() && booleans.iter().all(|b| !*b) {
booleans[rng.gen_range(0..len)] = true;
}
Predicate::Or(
booleans
.iter()
.map(|b| SimplePredicate::arbitrary_from(rng, (table, *b)).0)
.collect(),
)
} else {
Predicate::Or(
(0..rng.gen_range(0..=3))
.map(|_| SimplePredicate::arbitrary_from(rng, (table, false)).0)
.collect(),
)
}
})
}
}
impl ArbitraryFrom<&Table> for Predicate {
fn arbitrary_from<R: Rng>(rng: &mut R, table: &Table) -> Self {
let predicate_value = rng.gen_bool(0.5);
CompoundPredicate::arbitrary_from(rng, (table, predicate_value)).0
}
}
impl ArbitraryFrom<(&str, &Value)> for Predicate {
fn arbitrary_from<R: Rng>(rng: &mut R, (column_name, value): (&str, &Value)) -> Self {
one_of(
vec![
Box::new(|_| Predicate::Eq(column_name.to_string(), (*value).clone())),
Box::new(|rng| {
Self::Gt(
column_name.to_string(),
GTValue::arbitrary_from(rng, value).0,
)
}),
Box::new(|rng| {
Self::Lt(
column_name.to_string(),
LTValue::arbitrary_from(rng, value).0,
)
}),
],
rng,
)
}
}
/// Produces a predicate that is true for the provided row in the given table
fn produce_true_predicate<R: Rng>(rng: &mut R, (t, row): (&Table, &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];
backtrack(
vec![
(
1,
Box::new(|_| Some(Predicate::Eq(column.name.clone(), value.clone()))),
),
(
1,
Box::new(|rng| {
let v = Value::arbitrary_from(rng, &column.column_type);
if &v == value {
None
} else {
Some(Predicate::Neq(column.name.clone(), v))
}
}),
),
(
1,
Box::new(|rng| {
Some(Predicate::Gt(
column.name.clone(),
LTValue::arbitrary_from(rng, value).0,
))
}),
),
(
1,
Box::new(|rng| {
Some(Predicate::Lt(
column.name.clone(),
GTValue::arbitrary_from(rng, value).0,
))
}),
),
(
1,
Box::new(|rng| {
LikeValue::arbitrary_from_maybe(rng, value)
.map(|like| Predicate::Like(column.name.clone(), like.0))
}),
),
],
rng,
)
}
/// Produces a predicate that is false for the provided row in the given table
fn produce_false_predicate<R: Rng>(rng: &mut R, (t, row): (&Table, &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(
vec![
Box::new(|_| Predicate::Neq(column.name.clone(), value.clone())),
Box::new(|rng| {
let v = loop {
let v = Value::arbitrary_from(rng, &column.column_type);
if &v != value {
break v;
}
};
Predicate::Eq(column.name.clone(), v)
}),
Box::new(|rng| {
Predicate::Gt(column.name.clone(), GTValue::arbitrary_from(rng, value).0)
}),
Box::new(|rng| {
Predicate::Lt(column.name.clone(), LTValue::arbitrary_from(rng, value).0)
}),
],
rng,
)
}
impl ArbitraryFrom<(&Table, &Vec<Value>)> for Predicate {
fn arbitrary_from<R: Rng>(rng: &mut R, (t, row): (&Table, &Vec<Value>)) -> Self {
// We want to produce a predicate that is true for the row
// We can do this by creating several predicates that
// are true, some that are false, combiend them in ways that correspond to the creation of a true predicate
// Produce some true and false predicates
let mut true_predicates = (1..=rng.gen_range(1..=4))
.map(|_| produce_true_predicate(rng, (t, row)))
.collect::<Vec<_>>();
let false_predicates = (0..=rng.gen_range(0..=3))
.map(|_| produce_false_predicate(rng, (t, row)))
.collect::<Vec<_>>();
// Start building a top level predicate from a true predicate
let mut result = true_predicates.pop().unwrap();
let mut predicates = true_predicates
.iter()
.map(|p| (true, p.clone()))
.chain(false_predicates.iter().map(|p| (false, p.clone())))
.collect::<Vec<_>>();
predicates.shuffle(rng);
while !predicates.is_empty() {
// Create a new predicate from at least 1 and at most 3 predicates
let context =
predicates[0..rng.gen_range(0..=usize::min(3, predicates.len()))].to_vec();
// Shift `predicates` to remove the predicates in the context
predicates = predicates[context.len()..].to_vec();
// `result` is true, so we have the following three options to make a true predicate:
// T or F
// T or T
// T and T
result = one_of(
vec![
// T or (X1 or X2 or ... or Xn)
Box::new(|_| {
Predicate::Or(vec![
result.clone(),
Predicate::Or(context.iter().map(|(_, p)| p.clone()).collect()),
])
}),
// T or (T1 and T2 and ... and Tn)
Box::new(|_| {
Predicate::Or(vec![
result.clone(),
Predicate::And(context.iter().map(|(_, p)| p.clone()).collect()),
])
}),
// T and T
Box::new(|_| {
// Check if all the predicates in the context are true
if context.iter().all(|(b, _)| *b) {
// T and (X1 or X2 or ... or Xn)
Predicate::And(vec![
result.clone(),
Predicate::And(context.iter().map(|(_, p)| p.clone()).collect()),
])
}
// Check if there is at least one true predicate
else if context.iter().any(|(b, _)| *b) {
// T and (X1 or X2 or ... or Xn)
Predicate::And(vec![
result.clone(),
Predicate::Or(context.iter().map(|(_, p)| p.clone()).collect()),
])
} else {
// T and (X1 or X2 or ... or Xn or TRUE)
Predicate::And(vec![
result.clone(),
Predicate::Or(
context
.iter()
.map(|(_, p)| p.clone())
.chain(std::iter::once(Predicate::true_()))
.collect(),
),
])
}
}),
],
rng,
);
}
result
}
}
impl ArbitraryFrom<&SimulatorEnv> for Update {
fn arbitrary_from<R: Rng>(rng: &mut R, env: &SimulatorEnv) -> Self {
let table = pick(&env.tables, rng);

View File

@@ -197,7 +197,7 @@ impl ArbitraryFrom<&Value> for GTValue {
}
}
pub(crate) struct LikeValue(pub(crate) String);
pub(crate) struct LikeValue(pub(crate) Value);
impl ArbitraryFromMaybe<&Value> for LikeValue {
fn arbitrary_from_maybe<R: Rng>(rng: &mut R, value: &Value) -> Option<Self> {
@@ -221,7 +221,7 @@ impl ArbitraryFromMaybe<&Value> for LikeValue {
}
let index = rng.gen_range(0..t.len());
t.insert(index, '%');
Some(Self(t.into_iter().collect()))
Some(Self(Value::Text(t.into_iter().collect())))
}
_ => None,
}

View File

@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use crate::{model::table::Value, SimulatorEnv};
use super::select::Predicate;
use super::predicate::Predicate;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub(crate) struct Delete {

View File

@@ -18,7 +18,7 @@ macro_rules! assert_implemented_predicate_expr {
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Predicate(ast::Expr);
pub struct Predicate(pub ast::Expr);
impl Predicate {
pub(crate) fn true_() -> Self {
@@ -59,7 +59,8 @@ impl Predicate {
}
}
ast::Expr::Literal(literal) => Value::from(literal).into_bool(),
ast::Expr::Unary(unary_operator, expr) => todo!(),
// TODO: next implement unary operator
ast::Expr::Unary(..) => todo!(),
expr => unimplemented!("{:?}", expr),
}
}

View File

@@ -1,12 +1,10 @@
use std::fmt::Display;
use regex::{Regex, RegexBuilder};
use serde::{Deserialize, Serialize};
use crate::{
model::table::{Table, Value},
SimulatorEnv,
};
use crate::{model::table::Value, SimulatorEnv};
use super::predicate::Predicate;
/// `SELECT` distinctness
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@@ -80,123 +78,3 @@ impl Display for Select {
)
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub(crate) enum Predicate {
And(Vec<Predicate>), // p1 AND p2 AND p3... AND pn
Or(Vec<Predicate>), // p1 OR p2 OR p3... OR pn
Eq(String, Value), // column = Value
Neq(String, Value), // column != Value
Gt(String, Value), // column > Value
Lt(String, Value), // column < Value
Like(String, String), // column LIKE Value
}
/// This function is a duplication of the exec_like function in core/vdbe/mod.rs at commit 9b9d5f9b4c9920e066ef1237c80878f4c3968524
/// Any updates to the original function should be reflected here, otherwise the test will be incorrect.
fn construct_like_regex(pattern: &str) -> Regex {
let mut regex_pattern = String::with_capacity(pattern.len() * 2);
regex_pattern.push('^');
for c in pattern.chars() {
match c {
'\\' => regex_pattern.push_str("\\\\"),
'%' => regex_pattern.push_str(".*"),
'_' => regex_pattern.push('.'),
ch => {
if regex_syntax::is_meta_character(c) {
regex_pattern.push('\\');
}
regex_pattern.push(ch);
}
}
}
regex_pattern.push('$');
RegexBuilder::new(&regex_pattern)
.case_insensitive(true)
.dot_matches_new_line(true)
.build()
.unwrap()
}
fn exec_like(pattern: &str, text: &str) -> bool {
let re = construct_like_regex(pattern);
re.is_match(text)
}
impl Predicate {
pub(crate) fn true_() -> Self {
Self::And(vec![])
}
pub(crate) fn false_() -> Self {
Self::Or(vec![])
}
pub(crate) fn test(&self, row: &[Value], table: &Table) -> bool {
let get_value = |name: &str| {
table
.columns
.iter()
.zip(row.iter())
.find(|(column, _)| column.name == name)
.map(|(_, value)| value)
};
match self {
Predicate::And(vec) => vec.iter().all(|p| p.test(row, table)),
Predicate::Or(vec) => vec.iter().any(|p| p.test(row, table)),
Predicate::Eq(column, value) => get_value(column) == Some(value),
Predicate::Neq(column, value) => get_value(column) != Some(value),
Predicate::Gt(column, value) => get_value(column).map(|v| v > value).unwrap_or(false),
Predicate::Lt(column, value) => get_value(column).map(|v| v < value).unwrap_or(false),
Predicate::Like(column, value) => get_value(column)
.map(|v| exec_like(v.to_string().as_str(), value.as_str()))
.unwrap_or(false),
}
}
}
impl Display for Predicate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::And(predicates) => {
if predicates.is_empty() {
// todo: Make this TRUE when the bug is fixed
write!(f, "TRUE")
} else {
write!(f, "(")?;
for (i, p) in predicates.iter().enumerate() {
if i != 0 {
write!(f, " AND ")?;
}
write!(f, "{}", p)?;
}
write!(f, ")")
}
}
Self::Or(predicates) => {
if predicates.is_empty() {
write!(f, "FALSE")
} else {
write!(f, "(")?;
for (i, p) in predicates.iter().enumerate() {
if i != 0 {
write!(f, " OR ")?;
}
write!(f, "{}", p)?;
}
write!(f, ")")
}
}
Self::Eq(name, value) => write!(f, "{} = {}", name, value),
Self::Neq(name, value) => write!(f, "{} != {}", name, value),
Self::Gt(name, value) => write!(f, "{} > {}", name, value),
Self::Lt(name, value) => write!(f, "{} < {}", name, value),
Self::Like(name, value) => write!(f, "{} LIKE '{}'", name, value),
}
}
}

View File

@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use crate::{model::table::Value, SimulatorEnv};
use super::select::Predicate;
use super::predicate::Predicate;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub(crate) struct Update {

View File

@@ -248,6 +248,30 @@ impl From<ast::Literal> for Value {
}
}
impl From<Value> for ast::Literal {
fn from(value: Value) -> Self {
match value {
Value::Null => Self::Null,
Value::Integer(i) => Self::Numeric(i.to_string()),
Value::Float(f) => Self::Numeric(f.to_string()),
Value::Text(string) => Self::String(string),
Value::Blob(blob) => Self::Blob(hex::encode(blob)),
}
}
}
impl From<&Value> for ast::Literal {
fn from(value: &Value) -> Self {
match value {
Value::Null => Self::Null,
Value::Integer(i) => Self::Numeric(i.to_string()),
Value::Float(f) => Self::Numeric(f.to_string()),
Value::Text(string) => Self::String(string.clone()),
Value::Blob(blob) => Self::Blob(hex::encode(blob)),
}
}
}
impl From<Numeric> for Value {
fn from(value: Numeric) -> Self {
match value {