diff --git a/simulator/generation.rs b/simulator/generation.rs index 15b5845ec..d1006a953 100644 --- a/simulator/generation.rs +++ b/simulator/generation.rs @@ -30,7 +30,7 @@ pub(crate) fn frequency<'a, T, R: rand::Rng>( unreachable!() } -pub(crate) fn one_of(choices: Vec T>>, rng: &mut R) -> T { +pub(crate) fn one_of<'a, T, R: rand::Rng>(choices: Vec T + 'a>>, rng: &mut R) -> T { let index = rng.gen_range(0..choices.len()); choices[index](rng) } diff --git a/simulator/generation/query.rs b/simulator/generation/query.rs index 748409972..9589944ab 100644 --- a/simulator/generation/query.rs +++ b/simulator/generation/query.rs @@ -1,10 +1,12 @@ use crate::generation::table::{GTValue, LTValue}; -use crate::generation::{Arbitrary, ArbitraryFrom}; +use crate::generation::{one_of, Arbitrary, ArbitraryFrom}; use crate::model::query::{Create, Delete, Insert, Predicate, Query, Select}; use crate::model::table::{Table, Value}; use rand::Rng; +use super::{frequency, pick}; + impl Arbitrary for Create { fn arbitrary(rng: &mut R) -> Self { Create { @@ -15,20 +17,20 @@ impl Arbitrary for Create { impl ArbitraryFrom> for Select { fn arbitrary_from(rng: &mut R, tables: &Vec) -> Self { - let table = rng.gen_range(0..tables.len()); + let table = pick(tables, rng); Select { - table: tables[table].name.clone(), - predicate: Predicate::arbitrary_from(rng, &tables[table]), + table: table.name.clone(), + predicate: Predicate::arbitrary_from(rng, table), } } } impl ArbitraryFrom> for Select { fn arbitrary_from(rng: &mut R, tables: &Vec<&Table>) -> Self { - let table = rng.gen_range(0..tables.len()); + let table = pick(tables, rng); Select { - table: tables[table].name.clone(), - predicate: Predicate::arbitrary_from(rng, tables[table]), + table: table.name.clone(), + predicate: Predicate::arbitrary_from(rng, *table), } } } @@ -58,15 +60,24 @@ impl ArbitraryFrom
for Delete { impl ArbitraryFrom
for Query { fn arbitrary_from(rng: &mut R, table: &Table) -> Self { - match rng.gen_range(0..=200) { - 0 => Query::Create(Create::arbitrary(rng)), - 1..=100 => Query::Select(Select::arbitrary_from(rng, &vec![table])), - 101..=200 => Query::Insert(Insert::arbitrary_from(rng, table)), - // todo: This branch is currently never taken, as DELETE is not yet implemented. - // Change this when DELETE is implemented. - 201..=300 => Query::Delete(Delete::arbitrary_from(rng, table)), - _ => unreachable!(), - } + frequency( + vec![ + (1, Box::new(|rng| Query::Create(Create::arbitrary(rng)))), + ( + 100, + Box::new(|rng| Query::Select(Select::arbitrary_from(rng, &vec![table]))), + ), + ( + 100, + Box::new(|rng| Query::Insert(Insert::arbitrary_from(rng, table))), + ), + ( + 0, + Box::new(|rng| Query::Delete(Delete::arbitrary_from(rng, table))), + ), + ], + rng, + ) } } @@ -84,35 +95,53 @@ impl ArbitraryFrom<(&Table, bool)> for SimplePredicate { .map(|r| &r[column_index]) .collect::>(); // Pick an operator - let operator = match rng.gen_range(0..3) { - 0 => { - if *predicate_value { - Predicate::Eq( - column.name.clone(), - Value::arbitrary_from(rng, &column_values), - ) - } else { - Predicate::Eq( - column.name.clone(), - Value::arbitrary_from(rng, &column.column_type), - ) - } - } - 1 => Predicate::Gt( - column.name.clone(), - match predicate_value { - true => GTValue::arbitrary_from(rng, &column_values).0, - false => LTValue::arbitrary_from(rng, &column_values).0, - }, + 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, ), - 2 => Predicate::Lt( - column.name.clone(), - match predicate_value { - true => LTValue::arbitrary_from(rng, &column_values).0, - false => GTValue::arbitrary_from(rng, &column_values).0, - }, + 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, ), - _ => unreachable!(), }; SimplePredicate(operator) @@ -191,17 +220,10 @@ impl ArbitraryFrom
for Predicate { impl ArbitraryFrom<(&str, &Value)> for Predicate { fn arbitrary_from(rng: &mut R, (column_name, value): &(&str, &Value)) -> Self { - match rng.gen_range(0..3) { - 0 => Predicate::Eq(column_name.to_string(), (*value).clone()), - 1 => Predicate::Gt( - column_name.to_string(), - LTValue::arbitrary_from(rng, *value).0, - ), - 2 => Predicate::Lt( - column_name.to_string(), - LTValue::arbitrary_from(rng, *value).0, - ), - _ => unreachable!(), - } + one_of(vec![ + Box::new(|rng| Predicate::Eq(column_name.to_string(), (*value).clone())), + Box::new(|rng| Predicate::Gt(column_name.to_string(), GTValue::arbitrary_from(rng, *value).0)), + Box::new(|rng| Predicate::Lt(column_name.to_string(), LTValue::arbitrary_from(rng, *value).0)), + ], rng) } } diff --git a/simulator/generation/table.rs b/simulator/generation/table.rs index 685171207..46b6b0df6 100644 --- a/simulator/generation/table.rs +++ b/simulator/generation/table.rs @@ -1,6 +1,6 @@ use rand::Rng; -use crate::generation::{gen_random_text, readable_name_custom, Arbitrary, ArbitraryFrom}; +use crate::generation::{pick_index, gen_random_text, pick, readable_name_custom, Arbitrary, ArbitraryFrom}; use crate::model::table::{Column, ColumnType, Name, Table, Value}; impl Arbitrary for Name { @@ -13,7 +13,7 @@ impl Arbitrary for Name { impl Arbitrary for Table { fn arbitrary(rng: &mut R) -> Self { let name = Name::arbitrary(rng).0; - let columns = (1..=rng.gen_range(1..10)) + let columns = (1..=rng.gen_range(1..5)) .map(|_| Column::arbitrary(rng)) .collect(); Table { @@ -39,13 +39,16 @@ impl Arbitrary for Column { impl Arbitrary for ColumnType { fn arbitrary(rng: &mut R) -> Self { - match rng.gen_range(0..4) { - 0 => ColumnType::Integer, - 1 => ColumnType::Float, - 2 => ColumnType::Text, - 3 => ColumnType::Blob, - _ => unreachable!(), - } + pick( + &vec![ + ColumnType::Integer, + ColumnType::Float, + ColumnType::Text, + ColumnType::Blob, + ], + rng, + ) + .to_owned() } } @@ -55,8 +58,7 @@ impl ArbitraryFrom> for Value { return Value::Null; } - let index = rng.gen_range(0..values.len()); - values[index].clone() + pick(values, rng).to_owned().clone() } } @@ -78,8 +80,8 @@ impl ArbitraryFrom> for LTValue { if values.is_empty() { return LTValue(Value::Null); } - - let index = rng.gen_range(0..values.len()); + + let index = pick_index(values.len(), rng); LTValue::arbitrary_from(rng, values[index]) } } @@ -139,7 +141,7 @@ impl ArbitraryFrom> for GTValue { return GTValue(Value::Null); } - let index = rng.gen_range(0..values.len()); + let index = pick_index(values.len(), rng); GTValue::arbitrary_from(rng, values[index]) } } diff --git a/simulator/main.rs b/simulator/main.rs index 0f745e7e8..00b415ce9 100644 --- a/simulator/main.rs +++ b/simulator/main.rs @@ -1,7 +1,7 @@ use generation::plan::{Interaction, ResultSet}; use generation::{pick, pick_index, Arbitrary, ArbitraryFrom}; use limbo_core::{Connection, Database, File, OpenFlags, PlatformIO, Result, RowResult, IO}; -use model::query::{Insert, Predicate, Query, Select}; +use model::query::{Create, Insert, Predicate, Query, Select}; use model::table::{Column, Name, Table, Value}; use properties::{property_insert_select, property_select_all}; use rand::prelude::*; @@ -189,7 +189,12 @@ fn maybe_add_table(env: &mut SimulatorEnv, conn: &mut Rc) -> Result< .map(|_| Column::arbitrary(&mut env.rng)) .collect(), }; - let rows = get_all_rows(env, conn, table.to_create_str().as_str())?; + let query = Query::Create(Create { table: table.clone() }); + let rows = get_all_rows( + env, + conn, + query.to_string().as_str(), + )?; log::debug!("{:?}", rows); let rows = get_all_rows( env, @@ -207,7 +212,7 @@ fn maybe_add_table(env: &mut SimulatorEnv, conn: &mut Rc) -> Result< _ => unreachable!(), }; assert!( - *as_text != table.to_create_str(), + *as_text != query.to_string(), "table was not inserted correctly" ); env.tables.push(table); diff --git a/simulator/model/query.rs b/simulator/model/query.rs index 20058aead..ce227a252 100644 --- a/simulator/model/query.rs +++ b/simulator/model/query.rs @@ -7,6 +7,7 @@ pub(crate) enum Predicate { And(Vec), // p1 AND p2 AND p3... AND pn Or(Vec), // 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 } @@ -44,6 +45,7 @@ impl Display for Predicate { } } Predicate::Eq(name, value) => write!(f, "{} = {}", name, value), + Predicate::Neq(name, value) => write!(f, "{} != {}", name, value), Predicate::Gt(name, value) => write!(f, "{} > {}", name, value), Predicate::Lt(name, value) => write!(f, "{} < {}", name, value), } @@ -85,7 +87,18 @@ pub(crate) struct Delete { impl Display for Query { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Query::Create(Create { table }) => write!(f, "{}", table.to_create_str()), + Query::Create(Create { table }) => { + write!(f, "CREATE TABLE {} (", table.name)?; + + for (i, column) in table.columns.iter().enumerate() { + if i != 0 { + write!(f, ",")?; + } + write!(f, "{} {}", column.name, column.column_type)?; + } + + write!(f, ")") + }, Query::Select(Select { table, predicate: guard, diff --git a/simulator/model/table.rs b/simulator/model/table.rs index 93c3d6d74..ccc18f738 100644 --- a/simulator/model/table.rs +++ b/simulator/model/table.rs @@ -17,24 +17,6 @@ pub(crate) struct Table { pub(crate) columns: Vec, } -impl Table { - pub fn to_create_str(&self) -> String { - let mut out = String::new(); - - out.push_str(format!("CREATE TABLE {} (", self.name).as_str()); - - assert!(!self.columns.is_empty()); - for column in &self.columns { - out.push_str(format!("{} {},", column.name, column.column_type.as_str()).as_str()); - } - // remove last comma - out.pop(); - - out.push_str(");"); - out - } -} - #[derive(Debug, Clone)] pub(crate) struct Column { pub(crate) name: String, @@ -51,13 +33,13 @@ pub(crate) enum ColumnType { Blob, } -impl ColumnType { - pub fn as_str(&self) -> &str { +impl Display for ColumnType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ColumnType::Integer => "INTEGER", - ColumnType::Float => "FLOAT", - ColumnType::Text => "TEXT", - ColumnType::Blob => "BLOB", + ColumnType::Integer => write!(f, "INTEGER"), + ColumnType::Float => write!(f, "REAL"), + ColumnType::Text => write!(f, "TEXT"), + ColumnType::Blob => write!(f, "BLOB"), } } }