Skip to content

Commit 82fcc27

Browse files
committed
this commit fixes query generation;
- previous query generation method was faulty, producing wrong assertions - this commit adds a new arbitrary_from implementation for predicates - new implementation takes a table and a row, and produces a predicate that would evaluate to true for the row this commit makes small changes to the main for increasing readability
1 parent 7b2f65f commit 82fcc27

File tree

7 files changed

+245
-71
lines changed

7 files changed

+245
-71
lines changed

simulator/generation/property.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,7 @@ fn property_insert_select<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Prop
184184
// Select the row
185185
let select_query = Select {
186186
table: table.name.clone(),
187-
predicate: Predicate::arbitrary_from(
188-
rng,
189-
&(table, &Predicate::Eq(column.name.clone(), value.clone())),
190-
),
187+
predicate: Predicate::arbitrary_from(rng, &(table, &row)),
191188
};
192189

193190
Property::InsertSelect {

simulator/generation/query.rs

Lines changed: 152 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::generation::{one_of, Arbitrary, ArbitraryFrom};
33

44
use crate::model::query::{Create, Delete, Insert, Predicate, Query, Select};
55
use crate::model::table::{Table, Value};
6+
use rand::seq::SliceRandom as _;
67
use rand::Rng;
78

89
use super::{frequency, pick};
@@ -174,7 +175,7 @@ impl ArbitraryFrom<(&Table, bool)> for CompoundPredicate {
174175
let len = booleans.len();
175176

176177
// Make sure at least one of them is false
177-
if booleans.iter().all(|b| *b) {
178+
if !booleans.is_empty() && booleans.iter().all(|b| *b) {
178179
booleans[rng.gen_range(0..len)] = false;
179180
}
180181

@@ -195,7 +196,7 @@ impl ArbitraryFrom<(&Table, bool)> for CompoundPredicate {
195196
.collect::<Vec<_>>();
196197
let len = booleans.len();
197198
// Make sure at least one of them is true
198-
if booleans.iter().all(|b| !*b) {
199+
if !booleans.is_empty() && booleans.iter().all(|b| !*b) {
199200
booleans[rng.gen_range(0..len)] = true;
200201
}
201202

@@ -246,16 +247,155 @@ impl ArbitraryFrom<(&str, &Value)> for Predicate {
246247
}
247248
}
248249

249-
impl ArbitraryFrom<(&Table, &Predicate)> for Predicate {
250-
fn arbitrary_from<R: Rng>(rng: &mut R, (t, p): &(&Table, &Predicate)) -> Self {
251-
if rng.gen_bool(0.5) {
252-
// produce a true predicate
253-
let p_t = CompoundPredicate::arbitrary_from(rng, &(*t, true)).0;
254-
Predicate::And(vec![p_t, (*p).clone()])
255-
} else {
256-
// produce a false predicate
257-
let p_f = CompoundPredicate::arbitrary_from(rng, &(*t, false)).0;
258-
Predicate::Or(vec![p_f, (*p).clone()])
250+
/// Produces a predicate that is true for the provided row in the given table
251+
fn produce_true_predicate<R: Rng>(rng: &mut R, (t, row): &(&Table, &Vec<Value>)) -> Predicate {
252+
// Pick a column
253+
let column_index = rng.gen_range(0..t.columns.len());
254+
let column = &t.columns[column_index];
255+
let value = &row[column_index];
256+
one_of(
257+
vec![
258+
Box::new(|_| Predicate::Eq(column.name.clone(), value.clone())),
259+
Box::new(|rng| {
260+
let v = loop {
261+
let v = Value::arbitrary_from(rng, &column.column_type);
262+
if &v != value {
263+
break v;
264+
}
265+
};
266+
Predicate::Neq(column.name.clone(), v)
267+
}),
268+
Box::new(|rng| {
269+
Predicate::Gt(column.name.clone(), LTValue::arbitrary_from(rng, value).0)
270+
}),
271+
Box::new(|rng| {
272+
Predicate::Lt(column.name.clone(), GTValue::arbitrary_from(rng, value).0)
273+
}),
274+
],
275+
rng,
276+
)
277+
}
278+
279+
/// Produces a predicate that is false for the provided row in the given table
280+
fn produce_false_predicate<R: Rng>(rng: &mut R, (t, row): &(&Table, &Vec<Value>)) -> Predicate {
281+
// Pick a column
282+
let column_index = rng.gen_range(0..t.columns.len());
283+
let column = &t.columns[column_index];
284+
let value = &row[column_index];
285+
one_of(
286+
vec![
287+
Box::new(|_| Predicate::Neq(column.name.clone(), value.clone())),
288+
Box::new(|rng| {
289+
let v = loop {
290+
let v = Value::arbitrary_from(rng, &column.column_type);
291+
if &v != value {
292+
break v;
293+
}
294+
};
295+
Predicate::Eq(column.name.clone(), v)
296+
}),
297+
Box::new(|rng| {
298+
Predicate::Gt(column.name.clone(), GTValue::arbitrary_from(rng, value).0)
299+
}),
300+
Box::new(|rng| {
301+
Predicate::Lt(column.name.clone(), LTValue::arbitrary_from(rng, value).0)
302+
}),
303+
],
304+
rng,
305+
)
306+
}
307+
308+
impl ArbitraryFrom<(&Table, &Vec<Value>)> for Predicate {
309+
fn arbitrary_from<R: Rng>(rng: &mut R, (t, row): &(&Table, &Vec<Value>)) -> Self {
310+
// We want to produce a predicate that is true for the row
311+
// We can do this by creating several predicates that
312+
// are true, some that are false, combiend them in ways that correspond to the creation of a true predicate
313+
314+
// Produce some true and false predicates
315+
let mut true_predicates = (1..=rng.gen_range(1..=4))
316+
.map(|_| produce_true_predicate(rng, &(*t, row)))
317+
.collect::<Vec<_>>();
318+
319+
let false_predicates = (0..=rng.gen_range(0..=3))
320+
.map(|_| produce_false_predicate(rng, &(*t, row)))
321+
.collect::<Vec<_>>();
322+
323+
// Start building a top level predicate from a true predicate
324+
let mut result = true_predicates.pop().unwrap();
325+
println!("True predicate: {:?}", result);
326+
327+
let mut predicates = true_predicates
328+
.iter()
329+
.map(|p| (true, p.clone()))
330+
.chain(false_predicates.iter().map(|p| (false, p.clone())))
331+
.collect::<Vec<_>>();
332+
333+
predicates.shuffle(rng);
334+
335+
while !predicates.is_empty() {
336+
// Create a new predicate from at least 1 and at most 3 predicates
337+
let context =
338+
predicates[0..rng.gen_range(0..=usize::min(3, predicates.len()))].to_vec();
339+
// Shift `predicates` to remove the predicates in the context
340+
predicates = predicates[context.len()..].to_vec();
341+
342+
// `result` is true, so we have the following three options to make a true predicate:
343+
// T or F
344+
// T or T
345+
// T and T
346+
347+
result = one_of(
348+
vec![
349+
// T or (X1 or X2 or ... or Xn)
350+
Box::new(|_| {
351+
Predicate::Or(vec![
352+
result.clone(),
353+
Predicate::Or(context.iter().map(|(_, p)| p.clone()).collect()),
354+
])
355+
}),
356+
// T or (T1 and T2 and ... and Tn)
357+
Box::new(|_| {
358+
Predicate::Or(vec![
359+
result.clone(),
360+
Predicate::And(context.iter().map(|(_, p)| p.clone()).collect()),
361+
])
362+
}),
363+
// T and T
364+
Box::new(|_| {
365+
// Check if all the predicates in the context are true
366+
if context.iter().all(|(b, _)| *b) {
367+
// T and (X1 or X2 or ... or Xn)
368+
Predicate::And(vec![
369+
result.clone(),
370+
Predicate::And(context.iter().map(|(_, p)| p.clone()).collect()),
371+
])
372+
}
373+
// Check if there is at least one true predicate
374+
else if context.iter().any(|(b, _)| *b) {
375+
// T and (X1 or X2 or ... or Xn)
376+
Predicate::And(vec![
377+
result.clone(),
378+
Predicate::Or(context.iter().map(|(_, p)| p.clone()).collect()),
379+
])
380+
} else {
381+
// T and (X1 or X2 or ... or Xn or TRUE)
382+
Predicate::And(vec![
383+
result.clone(),
384+
Predicate::Or(
385+
context
386+
.iter()
387+
.map(|(_, p)| p.clone())
388+
.chain(std::iter::once(Predicate::true_()))
389+
.collect(),
390+
),
391+
])
392+
}
393+
}),
394+
],
395+
rng,
396+
);
259397
}
398+
399+
result
260400
}
261401
}

simulator/generation/table.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ impl Arbitrary for Name {
1515
impl Arbitrary for Table {
1616
fn arbitrary<R: Rng>(rng: &mut R) -> Self {
1717
let name = Name::arbitrary(rng).0;
18-
let columns = (1..=rng.gen_range(1..5))
18+
let columns = (1..=rng.gen_range(1..10))
1919
.map(|_| Column::arbitrary(rng))
2020
.collect();
2121
Table {
@@ -83,7 +83,7 @@ impl ArbitraryFrom<Value> for LTValue {
8383
fn arbitrary_from<R: Rng>(rng: &mut R, value: &Value) -> Self {
8484
match value {
8585
Value::Integer(i) => Self(Value::Integer(rng.gen_range(i64::MIN..*i - 1))),
86-
Value::Float(f) => Self(Value::Float(rng.gen_range(-1e10..*f - 1.0))),
86+
Value::Float(f) => Self(Value::Float(f - rng.gen_range(0.0..1e10))),
8787
Value::Text(t) => {
8888
// Either shorten the string, or make at least one character smaller and mutate the rest
8989
let mut t = t.clone();

0 commit comments

Comments
 (0)