diff --git a/sql_generation/generation/expr.rs b/sql_generation/generation/expr.rs index c07d81414..244bf6469 100644 --- a/sql_generation/generation/expr.rs +++ b/sql_generation/generation/expr.rs @@ -5,7 +5,7 @@ use turso_parser::ast::{ use crate::{ generation::{ frequency, gen_random_text, one_of, pick, pick_index, Arbitrary, ArbitraryFrom, - ArbitrarySizedFrom, GenerationContext, + ArbitrarySized, ArbitrarySizedFrom, GenerationContext, }, model::table::SimValue, }; @@ -14,8 +14,21 @@ impl Arbitrary for Box where T: Arbitrary, { - fn arbitrary(rng: &mut R) -> Self { - Box::from(T::arbitrary(rng)) + fn arbitrary(rng: &mut R, context: &C) -> Self { + Box::from(T::arbitrary(rng, context)) + } +} + +impl ArbitrarySized for Box +where + T: ArbitrarySized, +{ + fn arbitrary_sized( + rng: &mut R, + context: &C, + size: usize, + ) -> Self { + Box::from(T::arbitrary_sized(rng, context, size)) } } @@ -23,8 +36,13 @@ impl ArbitrarySizedFrom for Box where T: ArbitrarySizedFrom, { - fn arbitrary_sized_from(rng: &mut R, t: A, size: usize) -> Self { - Box::from(T::arbitrary_sized_from(rng, t, size)) + fn arbitrary_sized_from( + rng: &mut R, + context: &C, + t: A, + size: usize, + ) -> Self { + Box::from(T::arbitrary_sized_from(rng, context, t, size)) } } @@ -32,8 +50,8 @@ impl Arbitrary for Option where T: Arbitrary, { - fn arbitrary(rng: &mut R) -> Self { - rng.random_bool(0.5).then_some(T::arbitrary(rng)) + fn arbitrary(rng: &mut R, context: &C) -> Self { + rng.random_bool(0.5).then_some(T::arbitrary(rng, context)) } } @@ -41,9 +59,14 @@ impl ArbitrarySizedFrom for Option where T: ArbitrarySizedFrom, { - fn arbitrary_sized_from(rng: &mut R, t: A, size: usize) -> Self { + fn arbitrary_sized_from( + rng: &mut R, + context: &C, + t: A, + size: usize, + ) -> Self { rng.random_bool(0.5) - .then_some(T::arbitrary_sized_from(rng, t, size)) + .then_some(T::arbitrary_sized_from(rng, context, t, size)) } } @@ -51,20 +74,26 @@ impl ArbitraryFrom for Vec where T: ArbitraryFrom, { - fn arbitrary_from(rng: &mut R, t: A) -> Self { + fn arbitrary_from(rng: &mut R, context: &C, t: A) -> Self { let size = rng.random_range(0..5); - (0..size).map(|_| T::arbitrary_from(rng, t)).collect() + (0..size) + .map(|_| T::arbitrary_from(rng, context, t)) + .collect() } } // Freestyling generation -impl ArbitrarySizedFrom<&C> for Expr { - fn arbitrary_sized_from(rng: &mut R, t: &C, size: usize) -> Self { +impl ArbitrarySized for Expr { + fn arbitrary_sized( + rng: &mut R, + context: &C, + size: usize, + ) -> Self { frequency( vec![ ( 1, - Box::new(|rng| Expr::Literal(ast::Literal::arbitrary_from(rng, t))), + Box::new(|rng| Expr::Literal(ast::Literal::arbitrary(rng, context))), ), ( size, @@ -79,9 +108,9 @@ impl ArbitrarySizedFrom<&C> for Expr { // }), Box::new(|rng: &mut R| { Expr::Binary( - Box::arbitrary_sized_from(rng, t, size - 1), - Operator::arbitrary(rng), - Box::arbitrary_sized_from(rng, t, size - 1), + Box::arbitrary_sized(rng, context, size - 1), + Operator::arbitrary(rng, context), + Box::arbitrary_sized(rng, context, size - 1), ) }), // Box::new(|rng| Expr::Case { @@ -133,8 +162,8 @@ impl ArbitrarySizedFrom<&C> for Expr { // }) Box::new(|rng| { Expr::Unary( - UnaryOperator::arbitrary_from(rng, t), - Box::arbitrary_sized_from(rng, t, size - 1), + UnaryOperator::arbitrary(rng, context), + Box::arbitrary_sized(rng, context, size - 1), ) }), // TODO: skip Exists for now @@ -159,7 +188,7 @@ impl ArbitrarySizedFrom<&C> for Expr { } impl Arbitrary for Operator { - fn arbitrary(rng: &mut R) -> Self { + fn arbitrary(rng: &mut R, _context: &C) -> Self { let choices = [ Operator::Add, Operator::And, @@ -190,7 +219,7 @@ impl Arbitrary for Operator { } impl Arbitrary for Type { - fn arbitrary(rng: &mut R) -> Self { + fn arbitrary(rng: &mut R, _context: &C) -> Self { let name = pick(&["INT", "INTEGER", "REAL", "TEXT", "BLOB", "ANY"], rng).to_string(); Self { name, @@ -199,11 +228,11 @@ impl Arbitrary for Type { } } -impl ArbitraryFrom<&C> for QualifiedName { - fn arbitrary_from(rng: &mut R, t: &C) -> Self { +impl Arbitrary for QualifiedName { + fn arbitrary(rng: &mut R, context: &C) -> Self { // TODO: for now just generate table name - let table_idx = pick_index(t.tables().len(), rng); - let table = &t.tables()[table_idx]; + let table_idx = pick_index(context.tables().len(), rng); + let table = &context.tables()[table_idx]; // TODO: for now forego alias Self { db_name: None, @@ -213,8 +242,8 @@ impl ArbitraryFrom<&C> for QualifiedName { } } -impl ArbitraryFrom<&C> for LikeOperator { - fn arbitrary_from(rng: &mut R, _t: &C) -> Self { +impl Arbitrary for LikeOperator { + fn arbitrary(rng: &mut R, _t: &C) -> Self { let choice = rng.random_range(0..4); match choice { 0 => LikeOperator::Glob, @@ -227,8 +256,8 @@ impl ArbitraryFrom<&C> for LikeOperator { } // Current implementation does not take into account the columns affinity nor if table is Strict -impl ArbitraryFrom<&C> for ast::Literal { - fn arbitrary_from(rng: &mut R, _t: &C) -> Self { +impl Arbitrary for ast::Literal { + fn arbitrary(rng: &mut R, _t: &C) -> Self { loop { let choice = rng.random_range(0..5); let lit = match choice { @@ -255,7 +284,11 @@ impl ArbitraryFrom<&C> for ast::Literal { // Creates a litreal value impl ArbitraryFrom<&Vec<&SimValue>> for ast::Expr { - fn arbitrary_from(rng: &mut R, values: &Vec<&SimValue>) -> Self { + fn arbitrary_from( + rng: &mut R, + _context: &C, + values: &Vec<&SimValue>, + ) -> Self { if values.is_empty() { return Self::Literal(ast::Literal::Null); } @@ -265,8 +298,8 @@ impl ArbitraryFrom<&Vec<&SimValue>> for ast::Expr { } } -impl ArbitraryFrom<&C> for UnaryOperator { - fn arbitrary_from(rng: &mut R, _t: &C) -> Self { +impl Arbitrary for UnaryOperator { + fn arbitrary(rng: &mut R, _t: &C) -> Self { let choice = rng.random_range(0..4); match choice { 0 => Self::BitwiseNot, diff --git a/sql_generation/generation/mod.rs b/sql_generation/generation/mod.rs index 331dc65f4..18fa78021 100644 --- a/sql_generation/generation/mod.rs +++ b/sql_generation/generation/mod.rs @@ -19,7 +19,7 @@ type Choice<'a, R, T> = (usize, Box Option + 'a>); /// the possible values of the type, with a bias towards smaller values for /// practicality. pub trait Arbitrary { - fn arbitrary(rng: &mut R) -> Self; + fn arbitrary(rng: &mut R, context: &C) -> Self; } /// ArbitrarySized trait for generating random values of a specific size @@ -29,7 +29,8 @@ pub trait Arbitrary { /// must fit in the given size. This is useful for generating values that are /// constrained by a specific size, such as integers or strings. pub trait ArbitrarySized { - fn arbitrary_sized(rng: &mut R, size: usize) -> Self; + fn arbitrary_sized(rng: &mut R, context: &C, size: usize) + -> Self; } /// ArbitraryFrom trait for generating random values from a given value @@ -38,19 +39,7 @@ pub trait ArbitrarySized { /// such as generating an integer within an interval, or a value that fits in a table, /// or a predicate satisfying a given table row. pub trait ArbitraryFrom { - fn arbitrary_from(rng: &mut R, t: T) -> Self; -} - -pub trait ArbitraryContext { - fn arbitrary_with_context(rng: &mut R, context: &C) -> Self; -} - -pub trait ArbitraryContextFrom { - fn arbitrary_with_context_from( - rng: &mut R, - context: &C, - t: T, - ) -> Self; + fn arbitrary_from(rng: &mut R, context: &C, t: T) -> Self; } /// ArbitrarySizedFrom trait for generating random values from a given value @@ -62,12 +51,21 @@ pub trait ArbitraryContextFrom { /// This is useful for generating values that are constrained by a specific size, /// such as integers or strings, while still being dependent on the given value. pub trait ArbitrarySizedFrom { - fn arbitrary_sized_from(rng: &mut R, t: T, size: usize) -> Self; + fn arbitrary_sized_from( + rng: &mut R, + context: &C, + t: T, + size: usize, + ) -> Self; } /// ArbitraryFromMaybe trait for fallibally generating random values from a given value pub trait ArbitraryFromMaybe { - fn arbitrary_from_maybe(rng: &mut R, t: T) -> Option + fn arbitrary_from_maybe( + rng: &mut R, + context: &C, + t: T, + ) -> Option where Self: Sized; } @@ -187,3 +185,27 @@ where } picked } + +#[cfg(test)] +mod tests { + use crate::{ + generation::{GenerationContext, Opts}, + model::table::Table, + }; + + #[derive(Debug, Default, Clone)] + pub struct TestContext { + pub opts: Opts, + pub tables: Vec, + } + + impl GenerationContext for TestContext { + fn tables(&self) -> &Vec
{ + &self.tables + } + + fn opts(&self) -> &Opts { + &self.opts + } + } +} diff --git a/sql_generation/generation/opts.rs b/sql_generation/generation/opts.rs index b61da6cdc..d978d54cd 100644 --- a/sql_generation/generation/opts.rs +++ b/sql_generation/generation/opts.rs @@ -4,6 +4,12 @@ use rand::distr::weighted::WeightedIndex; use crate::model::table::Table; +/// Trait used to provide context to generation functions +pub trait GenerationContext { + fn tables(&self) -> &Vec
; + fn opts(&self) -> &Opts; +} + #[derive(Debug, Clone)] pub struct Opts { /// Indexes enabled @@ -22,12 +28,6 @@ impl Default for Opts { } } -/// Trait used to provide context to generation functions -pub trait GenerationContext { - fn tables(&self) -> &Vec
; - fn opts(&self) -> &Opts; -} - #[derive(Debug, Clone)] pub struct TableOpts { pub large_table: LargeTableOpts, diff --git a/sql_generation/generation/predicate/binary.rs b/sql_generation/generation/predicate/binary.rs index 29c1727a9..a5901a9f8 100644 --- a/sql_generation/generation/predicate/binary.rs +++ b/sql_generation/generation/predicate/binary.rs @@ -7,7 +7,7 @@ use crate::{ backtrack, one_of, pick, predicate::{CompoundPredicate, SimplePredicate}, table::{GTValue, LTValue, LikeValue}, - ArbitraryFrom, ArbitraryFromMaybe as _, + ArbitraryFrom, ArbitraryFromMaybe as _, GenerationContext, }, model::{ query::predicate::Predicate, @@ -17,8 +17,9 @@ use crate::{ impl Predicate { /// Generate an [ast::Expr::Binary] [Predicate] from a column and [SimValue] - pub fn from_column_binary( + pub fn from_column_binary( rng: &mut R, + context: &C, column_name: &str, value: &SimValue, ) -> Predicate { @@ -32,7 +33,7 @@ impl Predicate { ) }), Box::new(|rng| { - let gt_value = GTValue::arbitrary_from(rng, value).0; + let gt_value = GTValue::arbitrary_from(rng, context, value).0; Expr::Binary( Box::new(Expr::Id(ast::Name::Ident(column_name.to_string()))), ast::Operator::Greater, @@ -40,7 +41,7 @@ impl Predicate { ) }), Box::new(|rng| { - let lt_value = LTValue::arbitrary_from(rng, value).0; + let lt_value = LTValue::arbitrary_from(rng, context, value).0; Expr::Binary( Box::new(Expr::Id(ast::Name::Ident(column_name.to_string()))), ast::Operator::Less, @@ -54,7 +55,12 @@ impl Predicate { } /// Produces a true [ast::Expr::Binary] [Predicate] that is true for the provided row in the given table - pub fn true_binary(rng: &mut R, t: &Table, row: &[SimValue]) -> Predicate { + pub fn true_binary( + rng: &mut R, + context: &C, + t: &Table, + row: &[SimValue], + ) -> Predicate { // Pick a column let column_index = rng.random_range(0..t.columns.len()); let mut column = t.columns[column_index].clone(); @@ -93,7 +99,7 @@ impl Predicate { ( 1, Box::new(|rng| { - let v = SimValue::arbitrary_from(rng, &column.column_type); + let v = SimValue::arbitrary_from(rng, context, &column.column_type); if &v == value { None } else { @@ -111,7 +117,7 @@ impl Predicate { ( 1, Box::new(|rng| { - let lt_value = LTValue::arbitrary_from(rng, value).0; + let lt_value = LTValue::arbitrary_from(rng, context, value).0; Some(Expr::Binary( Box::new(ast::Expr::Qualified( ast::Name::new(&table_name), @@ -125,7 +131,7 @@ impl Predicate { ( 1, Box::new(|rng| { - let gt_value = GTValue::arbitrary_from(rng, value).0; + let gt_value = GTValue::arbitrary_from(rng, context, value).0; Some(Expr::Binary( Box::new(ast::Expr::Qualified( ast::Name::new(&table_name), @@ -140,7 +146,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| { + LikeValue::arbitrary_from_maybe(rng, context, value).map(|like| { Expr::Like { lhs: Box::new(ast::Expr::Qualified( ast::Name::new(&table_name), @@ -162,7 +168,12 @@ impl Predicate { } /// Produces an [ast::Expr::Binary] [Predicate] that is false for the provided row in the given table - pub fn false_binary(rng: &mut R, t: &Table, row: &[SimValue]) -> Predicate { + pub fn false_binary( + rng: &mut R, + context: &C, + t: &Table, + row: &[SimValue], + ) -> Predicate { // Pick a column let column_index = rng.random_range(0..t.columns.len()); let mut column = t.columns[column_index].clone(); @@ -197,7 +208,7 @@ impl Predicate { }), Box::new(|rng| { let v = loop { - let v = SimValue::arbitrary_from(rng, &column.column_type); + let v = SimValue::arbitrary_from(rng, context, &column.column_type); if &v != value { break v; } @@ -212,7 +223,7 @@ impl Predicate { ) }), Box::new(|rng| { - let gt_value = GTValue::arbitrary_from(rng, value).0; + let gt_value = GTValue::arbitrary_from(rng, context, value).0; Expr::Binary( Box::new(ast::Expr::Qualified( ast::Name::new(&table_name), @@ -223,7 +234,7 @@ impl Predicate { ) }), Box::new(|rng| { - let lt_value = LTValue::arbitrary_from(rng, value).0; + let lt_value = LTValue::arbitrary_from(rng, context, value).0; Expr::Binary( Box::new(ast::Expr::Qualified( ast::Name::new(&table_name), @@ -242,8 +253,9 @@ impl Predicate { impl SimplePredicate { /// Generates a true [ast::Expr::Binary] [SimplePredicate] from a [TableContext] for a row in the table - pub fn true_binary( + pub fn true_binary( rng: &mut R, + context: &C, table: &T, row: &[SimValue], ) -> Self { @@ -271,7 +283,7 @@ impl SimplePredicate { ) }), Box::new(|rng| { - let lt_value = LTValue::arbitrary_from(rng, column_value).0; + let lt_value = LTValue::arbitrary_from(rng, context, column_value).0; Expr::Binary( Box::new(Expr::Qualified( ast::Name::new(table_name), @@ -282,7 +294,7 @@ impl SimplePredicate { ) }), Box::new(|rng| { - let gt_value = GTValue::arbitrary_from(rng, column_value).0; + let gt_value = GTValue::arbitrary_from(rng, context, column_value).0; Expr::Binary( Box::new(Expr::Qualified( ast::Name::new(table_name), @@ -299,8 +311,9 @@ impl SimplePredicate { } /// Generates a false [ast::Expr::Binary] [SimplePredicate] from a [TableContext] for a row in the table - pub fn false_binary( + pub fn false_binary( rng: &mut R, + context: &C, table: &T, row: &[SimValue], ) -> Self { @@ -328,7 +341,7 @@ impl SimplePredicate { ) }), Box::new(|rng| { - let gt_value = GTValue::arbitrary_from(rng, column_value).0; + let gt_value = GTValue::arbitrary_from(rng, context, column_value).0; Expr::Binary( Box::new(ast::Expr::Qualified( ast::Name::new(table_name), @@ -339,7 +352,7 @@ impl SimplePredicate { ) }), Box::new(|rng| { - let lt_value = LTValue::arbitrary_from(rng, column_value).0; + let lt_value = LTValue::arbitrary_from(rng, context, column_value).0; Expr::Binary( Box::new(ast::Expr::Qualified( ast::Name::new(table_name), @@ -360,8 +373,9 @@ impl CompoundPredicate { /// Decide if you want to create an AND or an OR /// /// Creates a Compound Predicate that is TRUE or FALSE for at least a single row - pub fn from_table_binary( + pub fn from_table_binary( rng: &mut R, + context: &C, table: &T, predicate_value: bool, ) -> Self { @@ -381,7 +395,7 @@ impl CompoundPredicate { // An AND for false requires at least one of its children to be false if predicate_value { (0..rng.random_range(1..=3)) - .map(|_| SimplePredicate::arbitrary_from(rng, (table, row, true)).0) + .map(|_| SimplePredicate::arbitrary_from(rng, context, (table, row, true)).0) .reduce(|accum, curr| { Predicate(Expr::Binary( Box::new(accum.0), @@ -405,7 +419,7 @@ impl CompoundPredicate { booleans .iter() - .map(|b| SimplePredicate::arbitrary_from(rng, (table, row, *b)).0) + .map(|b| SimplePredicate::arbitrary_from(rng, context, (table, row, *b)).0) .reduce(|accum, curr| { Predicate(Expr::Binary( Box::new(accum.0), @@ -431,7 +445,7 @@ impl CompoundPredicate { booleans .iter() - .map(|b| SimplePredicate::arbitrary_from(rng, (table, row, *b)).0) + .map(|b| SimplePredicate::arbitrary_from(rng, context, (table, row, *b)).0) .reduce(|accum, curr| { Predicate(Expr::Binary( Box::new(accum.0), @@ -442,7 +456,7 @@ impl CompoundPredicate { .unwrap_or(Predicate::true_()) } else { (0..rng.random_range(1..=3)) - .map(|_| SimplePredicate::arbitrary_from(rng, (table, row, false)).0) + .map(|_| SimplePredicate::arbitrary_from(rng, context, (table, row, false)).0) .reduce(|accum, curr| { Predicate(Expr::Binary( Box::new(accum.0), @@ -463,7 +477,9 @@ mod tests { use rand_chacha::ChaCha8Rng; use crate::{ - generation::{pick, predicate::SimplePredicate, Arbitrary, ArbitraryFrom as _}, + generation::{ + pick, predicate::SimplePredicate, tests::TestContext, Arbitrary, ArbitraryFrom as _, + }, model::{ query::predicate::{expr_to_value, Predicate}, table::{SimValue, Table}, @@ -481,20 +497,22 @@ mod tests { fn fuzz_true_binary_predicate() { let seed = get_seed(); let mut rng = ChaCha8Rng::seed_from_u64(seed); + let context = &TestContext::default(); + for _ in 0..10000 { - let table = Table::arbitrary(&mut rng); + let table = Table::arbitrary(&mut rng, context); let num_rows = rng.random_range(1..10); let values: Vec> = (0..num_rows) .map(|_| { table .columns .iter() - .map(|c| SimValue::arbitrary_from(&mut rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(&mut rng, context, &c.column_type)) .collect() }) .collect(); let row = pick(&values, &mut rng); - let predicate = Predicate::true_binary(&mut rng, &table, row); + let predicate = Predicate::true_binary(&mut rng, context, &table, row); let value = expr_to_value(&predicate.0, row, &table); assert!( value.as_ref().is_some_and(|value| value.as_bool()), @@ -507,20 +525,22 @@ mod tests { fn fuzz_false_binary_predicate() { let seed = get_seed(); let mut rng = ChaCha8Rng::seed_from_u64(seed); + let context = &TestContext::default(); + for _ in 0..10000 { - let table = Table::arbitrary(&mut rng); + let table = Table::arbitrary(&mut rng, context); let num_rows = rng.random_range(1..10); let values: Vec> = (0..num_rows) .map(|_| { table .columns .iter() - .map(|c| SimValue::arbitrary_from(&mut rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(&mut rng, context, &c.column_type)) .collect() }) .collect(); let row = pick(&values, &mut rng); - let predicate = Predicate::false_binary(&mut rng, &table, row); + let predicate = Predicate::false_binary(&mut rng, context, &table, row); let value = expr_to_value(&predicate.0, row, &table); assert!( !value.as_ref().is_some_and(|value| value.as_bool()), @@ -533,21 +553,23 @@ mod tests { fn fuzz_true_binary_simple_predicate() { let seed = get_seed(); let mut rng = ChaCha8Rng::seed_from_u64(seed); + let context = &TestContext::default(); + for _ in 0..10000 { - let mut table = Table::arbitrary(&mut rng); + let mut table = Table::arbitrary(&mut rng, context); let num_rows = rng.random_range(1..10); let values: Vec> = (0..num_rows) .map(|_| { table .columns .iter() - .map(|c| SimValue::arbitrary_from(&mut rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(&mut rng, context, &c.column_type)) .collect() }) .collect(); table.rows.extend(values.clone()); let row = pick(&table.rows, &mut rng); - let predicate = SimplePredicate::true_binary(&mut rng, &table, row); + let predicate = SimplePredicate::true_binary(&mut rng, context, &table, row); let result = values .iter() .map(|row| predicate.0.test(row, &table)) @@ -561,21 +583,23 @@ mod tests { fn fuzz_false_binary_simple_predicate() { let seed = get_seed(); let mut rng = ChaCha8Rng::seed_from_u64(seed); + let context = &TestContext::default(); + for _ in 0..10000 { - let mut table = Table::arbitrary(&mut rng); + let mut table = Table::arbitrary(&mut rng, context); let num_rows = rng.random_range(1..10); let values: Vec> = (0..num_rows) .map(|_| { table .columns .iter() - .map(|c| SimValue::arbitrary_from(&mut rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(&mut rng, context, &c.column_type)) .collect() }) .collect(); table.rows.extend(values.clone()); let row = pick(&table.rows, &mut rng); - let predicate = SimplePredicate::false_binary(&mut rng, &table, row); + let predicate = SimplePredicate::false_binary(&mut rng, context, &table, row); let result = values .iter() .map(|row| predicate.0.test(row, &table)) diff --git a/sql_generation/generation/predicate/mod.rs b/sql_generation/generation/predicate/mod.rs index b919ad0bd..78fa30ae4 100644 --- a/sql_generation/generation/predicate/mod.rs +++ b/sql_generation/generation/predicate/mod.rs @@ -1,9 +1,12 @@ use rand::{seq::SliceRandom as _, Rng}; use turso_parser::ast::{self, Expr}; -use crate::model::{ - query::predicate::Predicate, - table::{SimValue, Table, TableContext}, +use crate::{ + generation::GenerationContext, + model::{ + query::predicate::Predicate, + table::{SimValue, Table, TableContext}, + }, }; use super::{one_of, ArbitraryFrom}; @@ -18,20 +21,24 @@ struct CompoundPredicate(Predicate); struct SimplePredicate(Predicate); impl, T: TableContext> ArbitraryFrom<(&T, A, bool)> for SimplePredicate { - fn arbitrary_from(rng: &mut R, (table, row, predicate_value): (&T, A, bool)) -> Self { + fn arbitrary_from( + rng: &mut R, + context: &C, + (table, row, predicate_value): (&T, A, bool), + ) -> Self { let row = row.as_ref(); // Pick an operator let choice = rng.random_range(0..2); // Pick an operator match predicate_value { true => match choice { - 0 => SimplePredicate::true_binary(rng, table, row), - 1 => SimplePredicate::true_unary(rng, table, row), + 0 => SimplePredicate::true_binary(rng, context, table, row), + 1 => SimplePredicate::true_unary(rng, context, table, row), _ => unreachable!(), }, false => match choice { - 0 => SimplePredicate::false_binary(rng, table, row), - 1 => SimplePredicate::false_unary(rng, table, row), + 0 => SimplePredicate::false_binary(rng, context, table, row), + 1 => SimplePredicate::false_unary(rng, context, table, row), _ => unreachable!(), }, } @@ -39,43 +46,59 @@ impl, T: TableContext> ArbitraryFrom<(&T, A, bool)> for Sim } impl ArbitraryFrom<(&T, bool)> for CompoundPredicate { - fn arbitrary_from(rng: &mut R, (table, predicate_value): (&T, bool)) -> Self { - CompoundPredicate::from_table_binary(rng, table, predicate_value) + fn arbitrary_from( + rng: &mut R, + context: &C, + (table, predicate_value): (&T, bool), + ) -> Self { + CompoundPredicate::from_table_binary(rng, context, table, predicate_value) } } impl ArbitraryFrom<&T> for Predicate { - fn arbitrary_from(rng: &mut R, table: &T) -> Self { + fn arbitrary_from(rng: &mut R, context: &C, table: &T) -> Self { let predicate_value = rng.random_bool(0.5); - Predicate::arbitrary_from(rng, (table, predicate_value)).parens() + Predicate::arbitrary_from(rng, context, (table, predicate_value)).parens() } } impl ArbitraryFrom<(&T, bool)> for Predicate { - fn arbitrary_from(rng: &mut R, (table, predicate_value): (&T, bool)) -> Self { - CompoundPredicate::arbitrary_from(rng, (table, predicate_value)).0 + fn arbitrary_from( + rng: &mut R, + context: &C, + (table, predicate_value): (&T, bool), + ) -> Self { + CompoundPredicate::arbitrary_from(rng, context, (table, predicate_value)).0 } } impl ArbitraryFrom<(&str, &SimValue)> for Predicate { - fn arbitrary_from(rng: &mut R, (column_name, value): (&str, &SimValue)) -> Self { - Predicate::from_column_binary(rng, column_name, value) + fn arbitrary_from( + rng: &mut R, + context: &C, + (column_name, value): (&str, &SimValue), + ) -> Self { + Predicate::from_column_binary(rng, context, column_name, value) } } impl ArbitraryFrom<(&Table, &Vec)> for Predicate { - fn arbitrary_from(rng: &mut R, (t, row): (&Table, &Vec)) -> Self { + fn arbitrary_from( + rng: &mut R, + context: &C, + (t, row): (&Table, &Vec), + ) -> 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.random_range(1..=4)) - .map(|_| Predicate::true_binary(rng, t, row)) + .map(|_| Predicate::true_binary(rng, context, t, row)) .collect::>(); let false_predicates = (0..=rng.random_range(0..=3)) - .map(|_| Predicate::false_binary(rng, t, row)) + .map(|_| Predicate::false_binary(rng, context, t, row)) .collect::>(); // Start building a top level predicate from a true predicate @@ -231,7 +254,9 @@ mod tests { use rand_chacha::ChaCha8Rng; use crate::{ - generation::{pick, predicate::SimplePredicate, Arbitrary, ArbitraryFrom as _}, + generation::{ + pick, predicate::SimplePredicate, tests::TestContext, Arbitrary, ArbitraryFrom as _, + }, model::{ query::predicate::{expr_to_value, Predicate}, table::{SimValue, Table}, @@ -249,20 +274,23 @@ mod tests { fn fuzz_arbitrary_table_true_simple_predicate() { let seed = get_seed(); let mut rng = ChaCha8Rng::seed_from_u64(seed); + let context = &TestContext::default(); + for _ in 0..10000 { - let table = Table::arbitrary(&mut rng); + let table = Table::arbitrary(&mut rng, context); let num_rows = rng.random_range(1..10); let values: Vec> = (0..num_rows) .map(|_| { table .columns .iter() - .map(|c| SimValue::arbitrary_from(&mut rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(&mut rng, context, &c.column_type)) .collect() }) .collect(); let row = pick(&values, &mut rng); - let predicate = SimplePredicate::arbitrary_from(&mut rng, (&table, row, true)).0; + let predicate = + SimplePredicate::arbitrary_from(&mut rng, context, (&table, row, true)).0; let value = expr_to_value(&predicate.0, row, &table); assert!( value.as_ref().is_some_and(|value| value.as_bool()), @@ -275,20 +303,23 @@ mod tests { fn fuzz_arbitrary_table_false_simple_predicate() { let seed = get_seed(); let mut rng = ChaCha8Rng::seed_from_u64(seed); + let context = &TestContext::default(); + for _ in 0..10000 { - let table = Table::arbitrary(&mut rng); + let table = Table::arbitrary(&mut rng, context); let num_rows = rng.random_range(1..10); let values: Vec> = (0..num_rows) .map(|_| { table .columns .iter() - .map(|c| SimValue::arbitrary_from(&mut rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(&mut rng, context, &c.column_type)) .collect() }) .collect(); let row = pick(&values, &mut rng); - let predicate = SimplePredicate::arbitrary_from(&mut rng, (&table, row, false)).0; + let predicate = + SimplePredicate::arbitrary_from(&mut rng, context, (&table, row, false)).0; let value = expr_to_value(&predicate.0, row, &table); assert!( !value.as_ref().is_some_and(|value| value.as_bool()), @@ -301,20 +332,22 @@ mod tests { fn fuzz_arbitrary_row_table_predicate() { let seed = get_seed(); let mut rng = ChaCha8Rng::seed_from_u64(seed); + let context = &TestContext::default(); + for _ in 0..10000 { - let table = Table::arbitrary(&mut rng); + let table = Table::arbitrary(&mut rng, context); let num_rows = rng.random_range(1..10); let values: Vec> = (0..num_rows) .map(|_| { table .columns .iter() - .map(|c| SimValue::arbitrary_from(&mut rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(&mut rng, context, &c.column_type)) .collect() }) .collect(); let row = pick(&values, &mut rng); - let predicate = Predicate::arbitrary_from(&mut rng, (&table, row)); + let predicate = Predicate::arbitrary_from(&mut rng, context, (&table, row)); let value = expr_to_value(&predicate.0, row, &table); assert!( value.as_ref().is_some_and(|value| value.as_bool()), @@ -327,20 +360,22 @@ mod tests { fn fuzz_arbitrary_true_table_predicate() { let seed = get_seed(); let mut rng = ChaCha8Rng::seed_from_u64(seed); + let context = &TestContext::default(); + for _ in 0..10000 { - let mut table = Table::arbitrary(&mut rng); + let mut table = Table::arbitrary(&mut rng, context); let num_rows = rng.random_range(1..10); let values: Vec> = (0..num_rows) .map(|_| { table .columns .iter() - .map(|c| SimValue::arbitrary_from(&mut rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(&mut rng, context, &c.column_type)) .collect() }) .collect(); table.rows.extend(values.clone()); - let predicate = Predicate::arbitrary_from(&mut rng, (&table, true)); + let predicate = Predicate::arbitrary_from(&mut rng, context, (&table, true)); let result = values .iter() .map(|row| predicate.test(row, &table)) @@ -354,20 +389,22 @@ mod tests { fn fuzz_arbitrary_false_table_predicate() { let seed = get_seed(); let mut rng = ChaCha8Rng::seed_from_u64(seed); + let context = &TestContext::default(); + for _ in 0..10000 { - let mut table = Table::arbitrary(&mut rng); + let mut table = Table::arbitrary(&mut rng, context); let num_rows = rng.random_range(1..10); let values: Vec> = (0..num_rows) .map(|_| { table .columns .iter() - .map(|c| SimValue::arbitrary_from(&mut rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(&mut rng, context, &c.column_type)) .collect() }) .collect(); table.rows.extend(values.clone()); - let predicate = Predicate::arbitrary_from(&mut rng, (&table, false)); + let predicate = Predicate::arbitrary_from(&mut rng, context, (&table, false)); let result = values .iter() .map(|row| predicate.test(row, &table)) diff --git a/sql_generation/generation/predicate/unary.rs b/sql_generation/generation/predicate/unary.rs index 62c6d7d65..bfcd1cff0 100644 --- a/sql_generation/generation/predicate/unary.rs +++ b/sql_generation/generation/predicate/unary.rs @@ -5,7 +5,9 @@ use turso_parser::ast::{self, Expr}; use crate::{ - generation::{backtrack, pick, predicate::SimplePredicate, ArbitraryFromMaybe}, + generation::{ + backtrack, pick, predicate::SimplePredicate, ArbitraryFromMaybe, GenerationContext, + }, model::{ query::predicate::Predicate, table::{SimValue, TableContext}, @@ -15,7 +17,11 @@ use crate::{ pub struct TrueValue(pub SimValue); impl ArbitraryFromMaybe<&SimValue> for TrueValue { - fn arbitrary_from_maybe(_rng: &mut R, value: &SimValue) -> Option + fn arbitrary_from_maybe( + _rng: &mut R, + _context: &C, + value: &SimValue, + ) -> Option where Self: Sized, { @@ -25,7 +31,11 @@ impl ArbitraryFromMaybe<&SimValue> for TrueValue { } impl ArbitraryFromMaybe<&Vec<&SimValue>> for TrueValue { - fn arbitrary_from_maybe(rng: &mut R, values: &Vec<&SimValue>) -> Option + fn arbitrary_from_maybe( + rng: &mut R, + context: &C, + values: &Vec<&SimValue>, + ) -> Option where Self: Sized, { @@ -34,14 +44,18 @@ impl ArbitraryFromMaybe<&Vec<&SimValue>> for TrueValue { } let value = pick(values, rng); - Self::arbitrary_from_maybe(rng, *value) + Self::arbitrary_from_maybe(rng, context, *value) } } pub struct FalseValue(pub SimValue); impl ArbitraryFromMaybe<&SimValue> for FalseValue { - fn arbitrary_from_maybe(_rng: &mut R, value: &SimValue) -> Option + fn arbitrary_from_maybe( + _rng: &mut R, + _context: &C, + value: &SimValue, + ) -> Option where Self: Sized, { @@ -51,7 +65,11 @@ impl ArbitraryFromMaybe<&SimValue> for FalseValue { } impl ArbitraryFromMaybe<&Vec<&SimValue>> for FalseValue { - fn arbitrary_from_maybe(rng: &mut R, values: &Vec<&SimValue>) -> Option + fn arbitrary_from_maybe( + rng: &mut R, + context: &C, + values: &Vec<&SimValue>, + ) -> Option where Self: Sized, { @@ -60,7 +78,7 @@ impl ArbitraryFromMaybe<&Vec<&SimValue>> for FalseValue { } let value = pick(values, rng); - Self::arbitrary_from_maybe(rng, *value) + Self::arbitrary_from_maybe(rng, context, *value) } } @@ -68,8 +86,9 @@ impl ArbitraryFromMaybe<&Vec<&SimValue>> for FalseValue { pub struct BitNotValue(pub SimValue); impl ArbitraryFromMaybe<(&SimValue, bool)> for BitNotValue { - fn arbitrary_from_maybe( + fn arbitrary_from_maybe( _rng: &mut R, + _context: &C, (value, predicate): (&SimValue, bool), ) -> Option where @@ -82,8 +101,9 @@ impl ArbitraryFromMaybe<(&SimValue, bool)> for BitNotValue { } impl ArbitraryFromMaybe<(&Vec<&SimValue>, bool)> for BitNotValue { - fn arbitrary_from_maybe( + fn arbitrary_from_maybe( rng: &mut R, + context: &C, (values, predicate): (&Vec<&SimValue>, bool), ) -> Option where @@ -94,15 +114,16 @@ impl ArbitraryFromMaybe<(&Vec<&SimValue>, bool)> for BitNotValue { } let value = pick(values, rng); - Self::arbitrary_from_maybe(rng, (*value, predicate)) + Self::arbitrary_from_maybe(rng, context, (*value, predicate)) } } // TODO: have some more complex generation with columns names here as well impl SimplePredicate { /// Generates a true [ast::Expr::Unary] [SimplePredicate] from a [TableContext] for some values in the table - pub fn true_unary( + pub fn true_unary( rng: &mut R, + context: &C, table: &T, row: &[SimValue], ) -> Self { @@ -120,7 +141,7 @@ impl SimplePredicate { ( num_retries, Box::new(|rng| { - TrueValue::arbitrary_from_maybe(rng, column_value).map(|value| { + TrueValue::arbitrary_from_maybe(rng, context, column_value).map(|value| { assert!(value.0.as_bool()); // Positive is a no-op in Sqlite Expr::unary(ast::UnaryOperator::Positive, Expr::Literal(value.0.into())) @@ -151,7 +172,7 @@ impl SimplePredicate { ( num_retries, Box::new(|rng| { - FalseValue::arbitrary_from_maybe(rng, column_value).map(|value| { + FalseValue::arbitrary_from_maybe(rng, context, column_value).map(|value| { assert!(!value.0.as_bool()); Expr::unary(ast::UnaryOperator::Not, Expr::Literal(value.0.into())) }) @@ -167,8 +188,9 @@ impl SimplePredicate { } /// Generates a false [ast::Expr::Unary] [SimplePredicate] from a [TableContext] for a row in the table - pub fn false_unary( + pub fn false_unary( rng: &mut R, + context: &C, table: &T, row: &[SimValue], ) -> Self { @@ -217,7 +239,7 @@ impl SimplePredicate { ( num_retries, Box::new(|rng| { - TrueValue::arbitrary_from_maybe(rng, column_value).map(|value| { + TrueValue::arbitrary_from_maybe(rng, context, column_value).map(|value| { assert!(value.0.as_bool()); Expr::unary(ast::UnaryOperator::Not, Expr::Literal(value.0.into())) }) @@ -239,7 +261,9 @@ mod tests { use rand_chacha::ChaCha8Rng; use crate::{ - generation::{pick, predicate::SimplePredicate, Arbitrary, ArbitraryFrom as _}, + generation::{ + pick, predicate::SimplePredicate, tests::TestContext, Arbitrary, ArbitraryFrom as _, + }, model::table::{SimValue, Table}, }; @@ -254,21 +278,23 @@ mod tests { fn fuzz_true_unary_simple_predicate() { let seed = get_seed(); let mut rng = ChaCha8Rng::seed_from_u64(seed); + let context = &TestContext::default(); + for _ in 0..10000 { - let mut table = Table::arbitrary(&mut rng); + let mut table = Table::arbitrary(&mut rng, context); let num_rows = rng.random_range(1..10); let values: Vec> = (0..num_rows) .map(|_| { table .columns .iter() - .map(|c| SimValue::arbitrary_from(&mut rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(&mut rng, context, &c.column_type)) .collect() }) .collect(); table.rows.extend(values.clone()); let row = pick(&table.rows, &mut rng); - let predicate = SimplePredicate::true_unary(&mut rng, &table, row); + let predicate = SimplePredicate::true_unary(&mut rng, context, &table, row); let result = values .iter() .map(|row| predicate.0.test(row, &table)) @@ -282,21 +308,23 @@ mod tests { fn fuzz_false_unary_simple_predicate() { let seed = get_seed(); let mut rng = ChaCha8Rng::seed_from_u64(seed); + let context = &TestContext::default(); + for _ in 0..10000 { - let mut table = Table::arbitrary(&mut rng); + let mut table = Table::arbitrary(&mut rng, context); let num_rows = rng.random_range(1..10); let values: Vec> = (0..num_rows) .map(|_| { table .columns .iter() - .map(|c| SimValue::arbitrary_from(&mut rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(&mut rng, context, &c.column_type)) .collect() }) .collect(); table.rows.extend(values.clone()); let row = pick(&table.rows, &mut rng); - let predicate = SimplePredicate::false_unary(&mut rng, &table, row); + let predicate = SimplePredicate::false_unary(&mut rng, context, &table, row); let result = values .iter() .map(|row| predicate.0.test(row, &table)) diff --git a/sql_generation/generation/query.rs b/sql_generation/generation/query.rs index f3729bf9e..ba0f9aeec 100644 --- a/sql_generation/generation/query.rs +++ b/sql_generation/generation/query.rs @@ -1,6 +1,6 @@ use crate::generation::{ - gen_random_text, pick_n_unique, pick_unique, Arbitrary, ArbitraryContext, ArbitraryContextFrom, - ArbitraryFrom, ArbitrarySizedFrom, GenerationContext, + gen_random_text, pick_n_unique, pick_unique, Arbitrary, ArbitraryFrom, ArbitrarySized, + GenerationContext, }; use crate::model::query::predicate::Predicate; use crate::model::query::select::{ @@ -17,32 +17,20 @@ use turso_parser::ast::{Expr, SortOrder}; use super::{backtrack, pick}; impl Arbitrary for Create { - fn arbitrary(rng: &mut R) -> Self { + fn arbitrary(rng: &mut R, context: &C) -> Self { Create { - table: Table::arbitrary(rng), + table: Table::arbitrary(rng, context), } } } -impl ArbitraryContext for Create { - fn arbitrary_with_context(rng: &mut R, context: &C) -> Self { - Create { - table: Table::arbitrary_with_context(rng, context), - } - } -} - -impl ArbitraryContextFrom<&Vec
> for FromClause { - fn arbitrary_with_context_from( - rng: &mut R, - context: &C, - tables: &Vec
, - ) -> Self { +impl Arbitrary for FromClause { + fn arbitrary(rng: &mut R, context: &C) -> Self { let opts = &context.opts().query.from_clause; let weights = opts.as_weighted_index(); let num_joins = opts.joins[rng.sample(weights)].num_joins; - let mut tables = tables.clone(); + let mut tables = context.tables().clone(); let mut table = pick(&tables, rng).clone(); tables.retain(|t| t.name != table.name); @@ -83,7 +71,7 @@ impl ArbitraryContextFrom<&Vec
> for FromClause { ); } - let predicate = Predicate::arbitrary_from(rng, &table); + let predicate = Predicate::arbitrary_from(rng, context, &table); Some(JoinedTable { table: joined_table_name, join_type: JoinType::Inner, @@ -95,71 +83,9 @@ impl ArbitraryContextFrom<&Vec
> for FromClause { } } -impl ArbitraryFrom<&Vec
> for FromClause { - fn arbitrary_from(rng: &mut R, tables: &Vec
) -> Self { - let num_joins = match rng.random_range(0..=100) { - 0..=90 => 0, - 91..=97 => 1, - 98..=100 => 2, - _ => unreachable!(), - }; - - let mut tables = tables.clone(); - let mut table = pick(&tables, rng).clone(); - - tables.retain(|t| t.name != table.name); - - let name = table.name.clone(); - - let mut table_context = JoinTable { - tables: Vec::new(), - rows: Vec::new(), - }; - - let joins: Vec<_> = (0..num_joins) - .filter_map(|_| { - if tables.is_empty() { - return None; - } - let join_table = pick(&tables, rng).clone(); - let joined_table_name = join_table.name.clone(); - - tables.retain(|t| t.name != join_table.name); - table_context.rows = table_context - .rows - .iter() - .cartesian_product(join_table.rows.iter()) - .map(|(t_row, j_row)| { - let mut row = t_row.clone(); - row.extend(j_row.clone()); - row - }) - .collect(); - // TODO: inneficient. use a Deque to push_front? - table_context.tables.insert(0, join_table); - for row in &mut table.rows { - assert_eq!( - row.len(), - table.columns.len(), - "Row length does not match column length after join" - ); - } - - let predicate = Predicate::arbitrary_from(rng, &table); - Some(JoinedTable { - table: joined_table_name, - join_type: JoinType::Inner, - on: predicate, - }) - }) - .collect(); - FromClause { table: name, joins } - } -} - -impl ArbitraryFrom<&C> for SelectInner { - fn arbitrary_from(rng: &mut R, env: &C) -> Self { - let from = FromClause::arbitrary_from(rng, env.tables()); +impl Arbitrary for SelectInner { + fn arbitrary(rng: &mut R, env: &C) -> Self { + let from = FromClause::arbitrary(rng, env); let tables = env.tables().clone(); let join_table = from.into_join_table(&tables); let cuml_col_count = join_table.columns().count(); @@ -205,21 +131,25 @@ impl ArbitraryFrom<&C> for SelectInner { SelectInner { distinctness: if env.opts().indexes { - Distinctness::arbitrary(rng) + Distinctness::arbitrary(rng, env) } else { Distinctness::All }, columns: vec![ResultColumn::Star], from: Some(from), - where_clause: Predicate::arbitrary_from(rng, &join_table), + where_clause: Predicate::arbitrary_from(rng, env, &join_table), order_by, } } } -impl ArbitrarySizedFrom<&C> for SelectInner { - fn arbitrary_sized_from(rng: &mut R, env: &C, num_result_columns: usize) -> Self { - let mut select_inner = SelectInner::arbitrary_from(rng, env); +impl ArbitrarySized for SelectInner { + fn arbitrary_sized( + rng: &mut R, + env: &C, + num_result_columns: usize, + ) -> Self { + let mut select_inner = SelectInner::arbitrary(rng, env); let select_from = &select_inner.from.as_ref().unwrap(); let table_names = select_from .joins @@ -251,7 +181,7 @@ impl ArbitrarySizedFrom<&C> for SelectInner { } impl Arbitrary for Distinctness { - fn arbitrary(rng: &mut R) -> Self { + fn arbitrary(rng: &mut R, _context: &C) -> Self { match rng.random_range(0..=5) { 0..4 => Distinctness::All, _ => Distinctness::Distinct, @@ -259,7 +189,7 @@ impl Arbitrary for Distinctness { } } impl Arbitrary for CompoundOperator { - fn arbitrary(rng: &mut R) -> Self { + fn arbitrary(rng: &mut R, _context: &C) -> Self { match rng.random_range(0..=1) { 0 => CompoundOperator::Union, 1 => CompoundOperator::UnionAll, @@ -273,16 +203,16 @@ impl Arbitrary for CompoundOperator { /// arbitrary expressions without referring to the tables. pub struct SelectFree(pub Select); -impl ArbitraryFrom<&C> for SelectFree { - fn arbitrary_from(rng: &mut R, env: &C) -> Self { - let expr = Predicate(Expr::arbitrary_sized_from(rng, env, 8)); +impl Arbitrary for SelectFree { + fn arbitrary(rng: &mut R, env: &C) -> Self { + let expr = Predicate(Expr::arbitrary_sized(rng, env, 8)); let select = Select::expr(expr); Self(select) } } -impl ArbitraryFrom<&C> for Select { - fn arbitrary_from(rng: &mut R, env: &C) -> Self { +impl Arbitrary for Select { + fn arbitrary(rng: &mut R, env: &C) -> Self { // Generate a number of selects based on the query size // If experimental indexes are enabled, we can have selects with compounds // Otherwise, we just have a single select with no compounds @@ -302,10 +232,10 @@ impl ArbitraryFrom<&C> for Select { let num_result_columns = rng.random_range(1..=min_column_count_across_tables); - let mut first = SelectInner::arbitrary_sized_from(rng, env, num_result_columns); + let mut first = SelectInner::arbitrary_sized(rng, env, num_result_columns); let mut rest: Vec = (0..num_compound_selects) - .map(|_| SelectInner::arbitrary_sized_from(rng, env, num_result_columns)) + .map(|_| SelectInner::arbitrary_sized(rng, env, num_result_columns)) .collect(); if !rest.is_empty() { @@ -322,7 +252,7 @@ impl ArbitraryFrom<&C> for Select { compounds: rest .into_iter() .map(|s| CompoundSelect { - operator: CompoundOperator::arbitrary(rng), + operator: CompoundOperator::arbitrary(rng, env), select: Box::new(s), }) .collect(), @@ -332,8 +262,8 @@ impl ArbitraryFrom<&C> for Select { } } -impl ArbitraryFrom<&C> for Insert { - fn arbitrary_from(rng: &mut R, env: &C) -> Self { +impl Arbitrary for Insert { + fn arbitrary(rng: &mut R, env: &C) -> Self { let gen_values = |rng: &mut R| { let table = pick(env.tables(), rng); let num_rows = rng.random_range(1..10); @@ -342,7 +272,7 @@ impl ArbitraryFrom<&C> for Insert { table .columns .iter() - .map(|c| SimValue::arbitrary_from(rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(rng, env, &c.column_type)) .collect() }) .collect(); @@ -356,7 +286,7 @@ impl ArbitraryFrom<&C> for Insert { // Find a non-empty table let select_table = env.tables().iter().find(|t| !t.rows.is_empty())?; let row = pick(&select_table.rows, rng); - let predicate = Predicate::arbitrary_from(rng, (select_table, row)); + let predicate = Predicate::arbitrary_from(rng, env, (select_table, row)); // Pick another table to insert into let select = Select::simple(select_table.name.clone(), predicate); let table = pick(env.tables(), rng); @@ -372,18 +302,18 @@ impl ArbitraryFrom<&C> for Insert { } } -impl ArbitraryFrom<&C> for Delete { - fn arbitrary_from(rng: &mut R, env: &C) -> Self { +impl Arbitrary for Delete { + fn arbitrary(rng: &mut R, env: &C) -> Self { let table = pick(env.tables(), rng); Self { table: table.name.clone(), - predicate: Predicate::arbitrary_from(rng, table), + predicate: Predicate::arbitrary_from(rng, env, table), } } } -impl ArbitraryFrom<&C> for Drop { - fn arbitrary_from(rng: &mut R, env: &C) -> Self { +impl Arbitrary for Drop { + fn arbitrary(rng: &mut R, env: &C) -> Self { let table = pick(env.tables(), rng); Self { table: table.name.clone(), @@ -391,8 +321,8 @@ impl ArbitraryFrom<&C> for Drop { } } -impl ArbitraryFrom<&C> for CreateIndex { - fn arbitrary_from(rng: &mut R, env: &C) -> Self { +impl Arbitrary for CreateIndex { + fn arbitrary(rng: &mut R, env: &C) -> Self { assert!( !env.tables().is_empty(), "Cannot create an index when no tables exist in the environment." @@ -439,8 +369,8 @@ impl ArbitraryFrom<&C> for CreateIndex { } } -impl ArbitraryFrom<&C> for Update { - fn arbitrary_from(rng: &mut R, env: &C) -> Self { +impl Arbitrary for Update { + fn arbitrary(rng: &mut R, env: &C) -> Self { let table = pick(env.tables(), rng); let num_cols = rng.random_range(1..=table.columns.len()); let columns = pick_unique(&table.columns, num_cols, rng); @@ -449,14 +379,14 @@ impl ArbitraryFrom<&C> for Update { .map(|column| { ( column.name.clone(), - SimValue::arbitrary_from(rng, &column.column_type), + SimValue::arbitrary_from(rng, env, &column.column_type), ) }) .collect(); Update { table: table.name.clone(), set_values, - predicate: Predicate::arbitrary_from(rng, table), + predicate: Predicate::arbitrary_from(rng, env, table), } } } diff --git a/sql_generation/generation/table.rs b/sql_generation/generation/table.rs index 32bcb8117..66f02250c 100644 --- a/sql_generation/generation/table.rs +++ b/sql_generation/generation/table.rs @@ -4,24 +4,23 @@ use rand::Rng; use turso_core::Value; use crate::generation::{ - gen_random_text, pick, readable_name_custom, Arbitrary, ArbitraryContext, ArbitraryFrom, - GenerationContext, Opts, + gen_random_text, pick, readable_name_custom, Arbitrary, ArbitraryFrom, GenerationContext, }; use crate::model::table::{Column, ColumnType, Name, SimValue, Table}; use super::ArbitraryFromMaybe; impl Arbitrary for Name { - fn arbitrary(rng: &mut R) -> Self { + fn arbitrary(rng: &mut R, _c: &C) -> Self { let name = readable_name_custom("_", rng); Name(name.replace("-", "_")) } } -impl Table { - fn gen_table(rng: &mut R, opts: &Opts) -> Self { - let opts = opts.table.clone(); - let name = Name::arbitrary(rng).0; +impl Arbitrary for Table { + fn arbitrary(rng: &mut R, context: &C) -> Self { + let opts = context.opts().table.clone(); + let name = Name::arbitrary(rng, context).0; let large_table = opts.large_table.enable && rng.random_bool(opts.large_table.large_table_prob); let column_size = if large_table { @@ -30,7 +29,7 @@ impl Table { rng.random_range(opts.column_range) } as usize; let mut column_set = HashSet::with_capacity(column_size); - for col in std::iter::repeat_with(|| Column::arbitrary(rng)) { + for col in std::iter::repeat_with(|| Column::arbitrary(rng, context)) { column_set.insert(col); if column_set.len() == column_size { break; @@ -46,22 +45,10 @@ impl Table { } } -impl Arbitrary for Table { - fn arbitrary(rng: &mut R) -> Self { - Table::gen_table(rng, &Opts::default()) - } -} - -impl ArbitraryContext for Table { - fn arbitrary_with_context(rng: &mut R, context: &C) -> Self { - Table::gen_table(rng, context.opts()) - } -} - impl Arbitrary for Column { - fn arbitrary(rng: &mut R) -> Self { - let name = Name::arbitrary(rng).0; - let column_type = ColumnType::arbitrary(rng); + fn arbitrary(rng: &mut R, context: &C) -> Self { + let name = Name::arbitrary(rng, context).0; + let column_type = ColumnType::arbitrary(rng, context); Self { name, column_type, @@ -72,16 +59,20 @@ impl Arbitrary for Column { } impl Arbitrary for ColumnType { - fn arbitrary(rng: &mut R) -> Self { + fn arbitrary(rng: &mut R, _context: &C) -> Self { pick(&[Self::Integer, Self::Float, Self::Text, Self::Blob], rng).to_owned() } } impl ArbitraryFrom<&Table> for Vec { - fn arbitrary_from(rng: &mut R, table: &Table) -> Self { + fn arbitrary_from( + rng: &mut R, + context: &C, + table: &Table, + ) -> Self { let mut row = Vec::new(); for column in table.columns.iter() { - let value = SimValue::arbitrary_from(rng, &column.column_type); + let value = SimValue::arbitrary_from(rng, context, &column.column_type); row.push(value); } row @@ -89,7 +80,11 @@ impl ArbitraryFrom<&Table> for Vec { } impl ArbitraryFrom<&Vec<&SimValue>> for SimValue { - fn arbitrary_from(rng: &mut R, values: &Vec<&Self>) -> Self { + fn arbitrary_from( + rng: &mut R, + _context: &C, + values: &Vec<&Self>, + ) -> Self { if values.is_empty() { return Self(Value::Null); } @@ -99,7 +94,11 @@ impl ArbitraryFrom<&Vec<&SimValue>> for SimValue { } impl ArbitraryFrom<&ColumnType> for SimValue { - fn arbitrary_from(rng: &mut R, column_type: &ColumnType) -> Self { + fn arbitrary_from( + rng: &mut R, + _context: &C, + column_type: &ColumnType, + ) -> Self { let value = match column_type { ColumnType::Integer => Value::Integer(rng.random_range(i64::MIN..i64::MAX)), ColumnType::Float => Value::Float(rng.random_range(-1e10..1e10)), @@ -113,19 +112,27 @@ impl ArbitraryFrom<&ColumnType> for SimValue { pub struct LTValue(pub SimValue); impl ArbitraryFrom<&Vec<&SimValue>> for LTValue { - fn arbitrary_from(rng: &mut R, values: &Vec<&SimValue>) -> Self { + fn arbitrary_from( + rng: &mut R, + context: &C, + values: &Vec<&SimValue>, + ) -> Self { if values.is_empty() { return Self(SimValue(Value::Null)); } // Get value less than all values let value = Value::exec_min(values.iter().map(|value| &value.0)); - Self::arbitrary_from(rng, &SimValue(value)) + Self::arbitrary_from(rng, context, &SimValue(value)) } } impl ArbitraryFrom<&SimValue> for LTValue { - fn arbitrary_from(rng: &mut R, value: &SimValue) -> Self { + fn arbitrary_from( + rng: &mut R, + _context: &C, + value: &SimValue, + ) -> Self { let new_value = match &value.0 { Value::Integer(i) => Value::Integer(rng.random_range(i64::MIN..*i - 1)), Value::Float(f) => Value::Float(f - rng.random_range(0.0..1e10)), @@ -175,19 +182,27 @@ impl ArbitraryFrom<&SimValue> for LTValue { pub struct GTValue(pub SimValue); impl ArbitraryFrom<&Vec<&SimValue>> for GTValue { - fn arbitrary_from(rng: &mut R, values: &Vec<&SimValue>) -> Self { + fn arbitrary_from( + rng: &mut R, + context: &C, + values: &Vec<&SimValue>, + ) -> Self { if values.is_empty() { return Self(SimValue(Value::Null)); } // Get value greater than all values let value = Value::exec_max(values.iter().map(|value| &value.0)); - Self::arbitrary_from(rng, &SimValue(value)) + Self::arbitrary_from(rng, context, &SimValue(value)) } } impl ArbitraryFrom<&SimValue> for GTValue { - fn arbitrary_from(rng: &mut R, value: &SimValue) -> Self { + fn arbitrary_from( + rng: &mut R, + _context: &C, + value: &SimValue, + ) -> Self { let new_value = match &value.0 { Value::Integer(i) => Value::Integer(rng.random_range(*i..i64::MAX)), Value::Float(f) => Value::Float(rng.random_range(*f..1e10)), @@ -237,7 +252,11 @@ impl ArbitraryFrom<&SimValue> for GTValue { pub struct LikeValue(pub SimValue); impl ArbitraryFromMaybe<&SimValue> for LikeValue { - fn arbitrary_from_maybe(rng: &mut R, value: &SimValue) -> Option { + fn arbitrary_from_maybe( + rng: &mut R, + _context: &C, + value: &SimValue, + ) -> Option { match &value.0 { value @ Value::Text(..) => { let t = value.to_string();