diff --git a/Cargo.lock b/Cargo.lock index ed065f587..2ddd72015 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2346,6 +2346,7 @@ name = "limbo_sim" version = "0.2.0" dependencies = [ "anyhow", + "bitflags 2.9.4", "bitmaps", "chrono", "clap", diff --git a/simulator/Cargo.toml b/simulator/Cargo.toml index 09401f2cc..7fd5dbeff 100644 --- a/simulator/Cargo.toml +++ b/simulator/Cargo.toml @@ -46,3 +46,4 @@ either = "1.15.0" similar = { workspace = true } similar-asserts = { workspace = true } bitmaps = { workspace = true } +bitflags.workspace = true diff --git a/simulator/generation/mod.rs b/simulator/generation/mod.rs index 80a2d0cff..6206c9a62 100644 --- a/simulator/generation/mod.rs +++ b/simulator/generation/mod.rs @@ -1,3 +1,6 @@ +use rand::distr::weighted::WeightedIndex; +use sql_generation::generation::GenerationContext; + use crate::runner::env::ShadowTablesMut; pub mod plan; @@ -17,3 +20,15 @@ pub(crate) trait Shadow { type Result; fn shadow(&self, tables: &mut ShadowTablesMut<'_>) -> Self::Result; } + +pub(super) trait WeightedDistribution { + type Item; + type GenItem; + fn items(&self) -> &[Self::Item]; + fn weights(&self) -> &WeightedIndex; + fn sample( + &self, + rng: &mut R, + context: &C, + ) -> Self::GenItem; +} diff --git a/simulator/generation/plan.rs b/simulator/generation/plan.rs index 3c07d73e3..05373312f 100644 --- a/simulator/generation/plan.rs +++ b/simulator/generation/plan.rs @@ -11,12 +11,11 @@ use indexmap::IndexSet; use serde::{Deserialize, Serialize}; use sql_generation::{ - generation::{Arbitrary, ArbitraryFrom, GenerationContext, frequency, query::SelectFree}, + generation::{Arbitrary, ArbitraryFrom, GenerationContext, frequency}, model::{ query::{ - Create, CreateIndex, Delete, Drop, Insert, Select, + Create, transaction::{Begin, Commit}, - update::Update, }, table::SimValue, }, @@ -26,7 +25,11 @@ use turso_core::{Connection, Result, StepResult}; use crate::{ SimulatorEnv, - generation::Shadow, + generation::{ + Shadow, WeightedDistribution, + property::PropertyDistribution, + query::{QueryDistribution, possible_queries}, + }, model::Query, runner::env::{ShadowTablesMut, SimConnection, SimulationType}, }; @@ -1064,118 +1067,22 @@ fn reopen_database(env: &mut SimulatorEnv) { }; } -fn random_create(rng: &mut R, env: &SimulatorEnv, conn_index: usize) -> Interactions { - let conn_ctx = env.connection_context(conn_index); - let mut create = Create::arbitrary(rng, &conn_ctx); - while conn_ctx - .tables() - .iter() - .any(|t| t.name == create.table.name) - { - create = Create::arbitrary(rng, &conn_ctx); - } - Interactions::new(conn_index, InteractionsType::Query(Query::Create(create))) -} - -fn random_read(rng: &mut R, env: &SimulatorEnv, conn_index: usize) -> Interactions { - Interactions::new( - conn_index, - InteractionsType::Query(Query::Select(Select::arbitrary( - rng, - &env.connection_context(conn_index), - ))), - ) -} - -fn random_expr(rng: &mut R, env: &SimulatorEnv, conn_index: usize) -> Interactions { - Interactions::new( - conn_index, - InteractionsType::Query(Query::Select( - SelectFree::arbitrary(rng, &env.connection_context(conn_index)).0, - )), - ) -} - -fn random_write(rng: &mut R, env: &SimulatorEnv, conn_index: usize) -> Interactions { - Interactions::new( - conn_index, - InteractionsType::Query(Query::Insert(Insert::arbitrary( - rng, - &env.connection_context(conn_index), - ))), - ) -} - -fn random_delete(rng: &mut R, env: &SimulatorEnv, conn_index: usize) -> Interactions { - Interactions::new( - conn_index, - InteractionsType::Query(Query::Delete(Delete::arbitrary( - rng, - &env.connection_context(conn_index), - ))), - ) -} - -fn random_update(rng: &mut R, env: &SimulatorEnv, conn_index: usize) -> Interactions { - Interactions::new( - conn_index, - InteractionsType::Query(Query::Update(Update::arbitrary( - rng, - &env.connection_context(conn_index), - ))), - ) -} - -fn random_drop(rng: &mut R, env: &SimulatorEnv, conn_index: usize) -> Interactions { - Interactions::new( - conn_index, - InteractionsType::Query(Query::Drop(Drop::arbitrary( - rng, - &env.connection_context(conn_index), - ))), - ) -} - -fn random_create_index( +fn random_fault( rng: &mut R, env: &SimulatorEnv, conn_index: usize, -) -> Option { - let conn_ctx = env.connection_context(conn_index); - if conn_ctx.tables().is_empty() { - return None; - } - let mut create_index = CreateIndex::arbitrary(rng, &conn_ctx); - while conn_ctx - .tables() - .iter() - .find(|t| t.name == create_index.table_name) - .expect("table should exist") - .indexes - .iter() - .any(|i| i == &create_index.index_name) - { - create_index = CreateIndex::arbitrary(rng, &conn_ctx); - } - - Some(Interactions::new( - conn_index, - InteractionsType::Query(Query::CreateIndex(create_index)), - )) -} - -fn random_fault(rng: &mut R, env: &SimulatorEnv) -> Interactions { +) -> Interactions { let faults = if env.opts.disable_reopen_database { vec![Fault::Disconnect] } else { vec![Fault::Disconnect, Fault::ReopenDatabase] }; let fault = faults[rng.random_range(0..faults.len())]; - Interactions::new(env.choose_conn(rng), InteractionsType::Fault(fault)) + Interactions::new(conn_index, InteractionsType::Fault(fault)) } impl ArbitraryFrom<(&SimulatorEnv, InteractionStats, usize)> for Interactions { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, conn_ctx: &C, (env, stats, conn_index): (&SimulatorEnv, InteractionStats, usize), @@ -1186,68 +1093,48 @@ impl ArbitraryFrom<(&SimulatorEnv, InteractionStats, usize)> for Interactions { &stats, env.profile.experimental_mvcc, ); + + let queries = possible_queries(conn_ctx.tables()); + let query_distr = QueryDistribution::new(queries, &remaining_); + + let property_distr = + PropertyDistribution::new(env, &remaining_, &query_distr, conn_ctx.opts()); + frequency( vec![ ( - u32::min(remaining_.select, remaining_.insert) + remaining_.create, + property_distr.weights().total_weight(), Box::new(|rng: &mut R| { Interactions::new( conn_index, InteractionsType::Property(Property::arbitrary_from( rng, conn_ctx, - (env, &stats), + &property_distr, )), ) }), ), ( - remaining_.select, - Box::new(|rng: &mut R| random_read(rng, env, conn_index)), - ), - ( - remaining_.select / 3, - Box::new(|rng: &mut R| random_expr(rng, env, conn_index)), - ), - ( - remaining_.insert, - Box::new(|rng: &mut R| random_write(rng, env, conn_index)), - ), - ( - remaining_.create, - Box::new(|rng: &mut R| random_create(rng, env, conn_index)), - ), - ( - remaining_.create_index, + query_distr.weights().total_weight(), Box::new(|rng: &mut R| { - if let Some(interaction) = random_create_index(rng, env, conn_index) { - interaction - } else { - // if no tables exist, we can't create an index, so fallback to creating a table - random_create(rng, env, conn_index) - } + Interactions::new( + conn_index, + InteractionsType::Query(Query::arbitrary_from( + rng, + conn_ctx, + &query_distr, + )), + ) }), ), - ( - remaining_.delete, - Box::new(|rng: &mut R| random_delete(rng, env, conn_index)), - ), - ( - remaining_.update, - Box::new(|rng: &mut R| random_update(rng, env, conn_index)), - ), - ( - // remaining_.drop, - 0, - Box::new(|rng: &mut R| random_drop(rng, env, conn_index)), - ), ( remaining_ .select .min(remaining_.insert) .min(remaining_.create) .max(1), - Box::new(|rng: &mut R| random_fault(rng, env)), + Box::new(|rng: &mut R| random_fault(rng, env, conn_index)), ), ], rng, diff --git a/simulator/generation/property.rs b/simulator/generation/property.rs index a113dc939..b00a1114d 100644 --- a/simulator/generation/property.rs +++ b/simulator/generation/property.rs @@ -1,6 +1,7 @@ +use rand::distr::{Distribution, weighted::WeightedIndex}; use serde::{Deserialize, Serialize}; use sql_generation::{ - generation::{Arbitrary, ArbitraryFrom, GenerationContext, frequency, pick, pick_index}, + generation::{Arbitrary, ArbitraryFrom, GenerationContext, Opts, pick, pick_index}, model::{ query::{ Create, Delete, Drop, Insert, Select, @@ -9,16 +10,21 @@ use sql_generation::{ transaction::{Begin, Commit, Rollback}, update::Update, }, - table::SimValue, + table::{SimValue, Table}, }, }; +use strum::IntoEnumIterator; use turso_core::{LimboError, types}; use turso_parser::ast::{self, Distinctness}; use crate::{ common::print_diff, - generation::{Shadow as _, plan::InteractionType}, - model::Query, + generation::{ + Shadow as _, WeightedDistribution, + plan::InteractionType, + query::{QueryDistribution, possible_queries}, + }, + model::{Query, QueryCapabilities, QueryDiscriminants}, profiles::query::QueryProfile, runner::env::SimulatorEnv, }; @@ -27,7 +33,8 @@ use super::plan::{Assertion, Interaction, InteractionStats, ResultSet}; /// Properties are representations of executable specifications /// about the database behavior. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, strum::EnumDiscriminants)] +#[strum_discriminants(derive(strum::EnumIter))] pub enum Property { /// Insert-Select is a property in which the inserted row /// must be in the resulting rows of a select query that has a @@ -1205,9 +1212,9 @@ pub(crate) fn remaining( } } -fn property_insert_values_select( +fn property_insert_values_select( rng: &mut R, - remaining: &Remaining, + query_distr: &QueryDistribution, ctx: &impl GenerationContext, mvcc: bool, ) -> Property { @@ -1251,7 +1258,7 @@ fn property_insert_values_select( })); } for _ in 0..rng.random_range(0..3) { - let query = Query::arbitrary_from(rng, ctx, remaining); + let query = Query::arbitrary_from(rng, ctx, query_distr); match &query { Query::Delete(Delete { table: t, @@ -1306,9 +1313,11 @@ fn property_insert_values_select( } } -fn property_read_your_updates_back( +fn property_read_your_updates_back( rng: &mut R, + _query_distr: &QueryDistribution, ctx: &impl GenerationContext, + _mvcc: bool, ) -> Property { // e.g. UPDATE t SET a=1, b=2 WHERE c=1; let update = Update::arbitrary(rng, ctx); @@ -1328,9 +1337,11 @@ fn property_read_your_updates_back( Property::ReadYourUpdatesBack { update, select } } -fn property_table_has_expected_content( +fn property_table_has_expected_content( rng: &mut R, + _query_distr: &QueryDistribution, ctx: &impl GenerationContext, + _mvcc: bool, ) -> Property { // Get a random table let table = pick(ctx.tables(), rng); @@ -1339,7 +1350,12 @@ fn property_table_has_expected_content( } } -fn property_select_limit(rng: &mut R, ctx: &impl GenerationContext) -> Property { +fn property_select_limit( + rng: &mut R, + _query_distr: &QueryDistribution, + ctx: &impl GenerationContext, + _mvcc: bool, +) -> Property { // Get a random table let table = pick(ctx.tables(), rng); // Select the table @@ -1353,10 +1369,11 @@ fn property_select_limit(rng: &mut R, ctx: &impl GenerationContext Property::SelectLimit { select } } -fn property_double_create_failure( +fn property_double_create_failure( rng: &mut R, - remaining: &Remaining, + query_distr: &QueryDistribution, ctx: &impl GenerationContext, + _mvcc: bool, ) -> Property { // Create the table let create_query = Create::arbitrary(rng, ctx); @@ -1368,7 +1385,7 @@ fn property_double_create_failure( // - [x] There will be no errors in the middle interactions.(best effort) // - [ ] Table `t` will not be renamed or dropped.(todo: add this constraint once ALTER or DROP is implemented) for _ in 0..rng.random_range(0..3) { - let query = Query::arbitrary_from(rng, ctx, remaining); + let query = Query::arbitrary_from(rng, ctx, query_distr); if let Query::Create(Create { table: t }) = &query { // There will be no errors in the middle interactions. // - Creating the same table is an error @@ -1385,10 +1402,11 @@ fn property_double_create_failure( } } -fn property_delete_select( +fn property_delete_select( rng: &mut R, - remaining: &Remaining, + query_distr: &QueryDistribution, ctx: &impl GenerationContext, + _mvcc: bool, ) -> Property { // Get a random table let table = pick(ctx.tables(), rng); @@ -1401,7 +1419,7 @@ fn property_delete_select( // - [x] A row that holds for the predicate will not be inserted. // - [ ] The table `t` will not be renamed, dropped, or altered. (todo: add this constraint once ALTER or DROP is implemented) for _ in 0..rng.random_range(0..3) { - let query = Query::arbitrary_from(rng, ctx, remaining); + let query = Query::arbitrary_from(rng, ctx, query_distr); match &query { Query::Insert(Insert::Values { table: t, values }) => { // A row that holds for the predicate will not be inserted. @@ -1443,10 +1461,11 @@ fn property_delete_select( } } -fn property_drop_select( +fn property_drop_select( rng: &mut R, - remaining: &Remaining, + query_distr: &QueryDistribution, ctx: &impl GenerationContext, + _mvcc: bool, ) -> Property { // Get a random table let table = pick(ctx.tables(), rng); @@ -1456,7 +1475,7 @@ fn property_drop_select( // - [x] There will be no errors in the middle interactions. (this constraint is impossible to check, so this is just best effort) // - [-] The table `t` will not be created, no table will be renamed to `t`. (todo: update this constraint once ALTER is implemented) for _ in 0..rng.random_range(0..3) { - let query = Query::arbitrary_from(rng, ctx, remaining); + let query = Query::arbitrary_from(rng, ctx, query_distr); if let Query::Create(Create { table: t }) = &query { // - The table `t` will not be created if t.name == table.name { @@ -1478,9 +1497,11 @@ fn property_drop_select( } } -fn property_select_select_optimizer( +fn property_select_select_optimizer( rng: &mut R, + _query_distr: &QueryDistribution, ctx: &impl GenerationContext, + _mvcc: bool, ) -> Property { // Get a random table let table = pick(ctx.tables(), rng); @@ -1499,9 +1520,11 @@ fn property_select_select_optimizer( } } -fn property_where_true_false_null( +fn property_where_true_false_null( rng: &mut R, + _query_distr: &QueryDistribution, ctx: &impl GenerationContext, + _mvcc: bool, ) -> Property { // Get a random table let table = pick(ctx.tables(), rng); @@ -1518,9 +1541,11 @@ fn property_where_true_false_null( } } -fn property_union_all_preserves_cardinality( +fn property_union_all_preserves_cardinality( rng: &mut R, + _query_distr: &QueryDistribution, ctx: &impl GenerationContext, + _mvcc: bool, ) -> Property { // Get a random table let table = pick(ctx.tables(), rng); @@ -1543,133 +1568,130 @@ fn property_union_all_preserves_cardinality( } } -fn property_fsync_no_wait( +fn property_fsync_no_wait( rng: &mut R, - remaining: &Remaining, + query_distr: &QueryDistribution, ctx: &impl GenerationContext, + _mvcc: bool, ) -> Property { Property::FsyncNoWait { - query: Query::arbitrary_from(rng, ctx, remaining), + query: Query::arbitrary_from(rng, ctx, query_distr), tables: ctx.tables().iter().map(|t| t.name.clone()).collect(), } } -fn property_faulty_query( +fn property_faulty_query( rng: &mut R, - remaining: &Remaining, + query_distr: &QueryDistribution, ctx: &impl GenerationContext, + _mvcc: bool, ) -> Property { Property::FaultyQuery { - query: Query::arbitrary_from(rng, ctx, remaining), + query: Query::arbitrary_from(rng, ctx, query_distr), tables: ctx.tables().iter().map(|t| t.name.clone()).collect(), } } -impl ArbitraryFrom<(&SimulatorEnv, &InteractionStats)> for Property { - fn arbitrary_from( - rng: &mut R, - conn_ctx: &C, - (env, stats): (&SimulatorEnv, &InteractionStats), - ) -> Self { - let opts = conn_ctx.opts(); - let remaining_ = remaining( - env.opts.max_interactions, - &env.profile.query, - stats, - env.profile.experimental_mvcc, - ); +type PropertyGenFunc = fn(&mut R, &QueryDistribution, &G, bool) -> Property; - #[allow(clippy::type_complexity)] - let choices: Vec<(_, Box Property>)> = vec![ - ( +impl PropertyDiscriminants { + pub(super) fn gen_function(&self) -> PropertyGenFunc + where + R: rand::Rng + ?Sized, + G: GenerationContext, + { + match self { + PropertyDiscriminants::InsertValuesSelect => property_insert_values_select, + PropertyDiscriminants::ReadYourUpdatesBack => property_read_your_updates_back, + PropertyDiscriminants::TableHasExpectedContent => property_table_has_expected_content, + PropertyDiscriminants::DoubleCreateFailure => property_double_create_failure, + PropertyDiscriminants::SelectLimit => property_select_limit, + PropertyDiscriminants::DeleteSelect => property_delete_select, + PropertyDiscriminants::DropSelect => property_drop_select, + PropertyDiscriminants::SelectSelectOptimizer => property_select_select_optimizer, + PropertyDiscriminants::WhereTrueFalseNull => property_where_true_false_null, + PropertyDiscriminants::UNIONAllPreservesCardinality => { + property_union_all_preserves_cardinality + } + PropertyDiscriminants::FsyncNoWait => property_fsync_no_wait, + PropertyDiscriminants::FaultyQuery => property_faulty_query, + PropertyDiscriminants::Queries => { + unreachable!("should not try to generate queries property") + } + } + } + + pub fn weight(&self, env: &SimulatorEnv, remaining: &Remaining, opts: &Opts) -> u32 { + match self { + PropertyDiscriminants::InsertValuesSelect => { if !env.opts.disable_insert_values_select { - u32::min(remaining_.select, remaining_.insert).max(1) + u32::min(remaining.select, remaining.insert).max(1) } else { 0 - }, - Box::new(|rng: &mut R| { - property_insert_values_select( - rng, - &remaining_, - conn_ctx, - env.profile.experimental_mvcc, - ) - }), - ), - ( - remaining_.select.max(1), - Box::new(|rng: &mut R| property_table_has_expected_content(rng, conn_ctx)), - ), - ( - u32::min(remaining_.select, remaining_.insert).max(1), - Box::new(|rng: &mut R| property_read_your_updates_back(rng, conn_ctx)), - ), - ( + } + } + PropertyDiscriminants::ReadYourUpdatesBack => { + u32::min(remaining.select, remaining.insert).max(1) + } + PropertyDiscriminants::TableHasExpectedContent => remaining.select.max(1), + PropertyDiscriminants::DoubleCreateFailure => { if !env.opts.disable_double_create_failure { - remaining_.create / 2 + remaining.create / 2 } else { 0 - }, - Box::new(|rng: &mut R| property_double_create_failure(rng, &remaining_, conn_ctx)), - ), - ( + } + } + PropertyDiscriminants::SelectLimit => { if !env.opts.disable_select_limit { - remaining_.select + remaining.select } else { 0 - }, - Box::new(|rng: &mut R| property_select_limit(rng, conn_ctx)), - ), - ( + } + } + PropertyDiscriminants::DeleteSelect => { if !env.opts.disable_delete_select { - u32::min(remaining_.select, remaining_.insert).min(remaining_.delete) + u32::min(remaining.select, remaining.insert).min(remaining.delete) } else { 0 - }, - Box::new(|rng: &mut R| property_delete_select(rng, &remaining_, conn_ctx)), - ), - ( + } + } + PropertyDiscriminants::DropSelect => { if !env.opts.disable_drop_select { - // remaining_.drop + // remaining.drop 0 } else { 0 - }, - Box::new(|rng: &mut R| property_drop_select(rng, &remaining_, conn_ctx)), - ), - ( + } + } + PropertyDiscriminants::SelectSelectOptimizer => { if !env.opts.disable_select_optimizer { - remaining_.select / 2 + remaining.select / 2 } else { 0 - }, - Box::new(|rng: &mut R| property_select_select_optimizer(rng, conn_ctx)), - ), - ( + } + } + PropertyDiscriminants::WhereTrueFalseNull => { if opts.indexes && !env.opts.disable_where_true_false_null { - remaining_.select / 2 + remaining.select / 2 } else { 0 - }, - Box::new(|rng: &mut R| property_where_true_false_null(rng, conn_ctx)), - ), - ( + } + } + PropertyDiscriminants::UNIONAllPreservesCardinality => { if opts.indexes && !env.opts.disable_union_all_preserves_cardinality { - remaining_.select / 3 + remaining.select / 3 } else { 0 - }, - Box::new(|rng: &mut R| property_union_all_preserves_cardinality(rng, conn_ctx)), - ), - ( + } + } + PropertyDiscriminants::FsyncNoWait => { if env.profile.io.enable && !env.opts.disable_fsync_no_wait { 50 // Freestyle number } else { 0 - }, - Box::new(|rng: &mut R| property_fsync_no_wait(rng, &remaining_, conn_ctx)), - ), - ( + } + } + PropertyDiscriminants::FaultyQuery => { if env.profile.io.enable && env.profile.io.fault.enable && !env.opts.disable_faulty_query @@ -1677,12 +1699,120 @@ impl ArbitraryFrom<(&SimulatorEnv, &InteractionStats)> for Property { 20 } else { 0 - }, - Box::new(|rng: &mut R| property_faulty_query(rng, &remaining_, conn_ctx)), - ), - ]; + } + } + PropertyDiscriminants::Queries => { + unreachable!("queries property should not be generated") + } + } + } - frequency(choices, rng) + fn can_generate(queries: &[QueryDiscriminants]) -> Vec { + let queries_capabilities = QueryCapabilities::from_list_queries(queries); + + PropertyDiscriminants::iter() + .filter(|property| { + !matches!(property, PropertyDiscriminants::Queries) + && queries_capabilities.contains(property.requirements()) + }) + .collect() + } + + pub const fn requirements(&self) -> QueryCapabilities { + match self { + PropertyDiscriminants::InsertValuesSelect => { + QueryCapabilities::SELECT.union(QueryCapabilities::INSERT) + } + PropertyDiscriminants::ReadYourUpdatesBack => { + QueryCapabilities::SELECT.union(QueryCapabilities::UPDATE) + } + PropertyDiscriminants::TableHasExpectedContent => QueryCapabilities::SELECT, + PropertyDiscriminants::DoubleCreateFailure => QueryCapabilities::CREATE, + PropertyDiscriminants::SelectLimit => QueryCapabilities::SELECT, + PropertyDiscriminants::DeleteSelect => { + QueryCapabilities::SELECT.union(QueryCapabilities::DELETE) + } + PropertyDiscriminants::DropSelect => { + QueryCapabilities::SELECT.union(QueryCapabilities::DROP) + } + PropertyDiscriminants::SelectSelectOptimizer => QueryCapabilities::SELECT, + PropertyDiscriminants::WhereTrueFalseNull => QueryCapabilities::SELECT, + PropertyDiscriminants::UNIONAllPreservesCardinality => QueryCapabilities::SELECT, + PropertyDiscriminants::FsyncNoWait => QueryCapabilities::all(), + PropertyDiscriminants::FaultyQuery => QueryCapabilities::all(), + PropertyDiscriminants::Queries => panic!("queries property should not be generated"), + } + } +} + +pub fn possiple_properties(tables: &[Table]) -> Vec { + let queries = possible_queries(tables); + PropertyDiscriminants::can_generate(queries) +} + +pub(super) struct PropertyDistribution<'a> { + properties: Vec, + weights: WeightedIndex, + query_distr: &'a QueryDistribution, + mvcc: bool, +} + +impl<'a> PropertyDistribution<'a> { + pub fn new( + env: &SimulatorEnv, + remaining: &Remaining, + query_distr: &'a QueryDistribution, + opts: &Opts, + ) -> Self { + let properties = PropertyDiscriminants::can_generate(query_distr.items()); + let weights = WeightedIndex::new( + properties + .iter() + .map(|property| property.weight(env, remaining, opts)), + ) + .unwrap(); + + Self { + properties, + weights, + query_distr, + mvcc: env.profile.experimental_mvcc, + } + } +} + +impl<'a> WeightedDistribution for PropertyDistribution<'a> { + type Item = PropertyDiscriminants; + + type GenItem = Property; + + fn items(&self) -> &[Self::Item] { + &self.properties + } + + fn weights(&self) -> &WeightedIndex { + &self.weights + } + + fn sample( + &self, + rng: &mut R, + conn_ctx: &C, + ) -> Self::GenItem { + let properties = &self.properties; + let idx = self.weights.sample(rng); + let property_fn = properties[idx].gen_function(); + (property_fn)(rng, self.query_distr, conn_ctx, self.mvcc) + } +} + +impl<'a> ArbitraryFrom<&PropertyDistribution<'a>> for Property { + fn arbitrary_from( + rng: &mut R, + conn_ctx: &C, + property_distr: &PropertyDistribution<'a>, + ) -> Self { + property_distr.sample(rng, conn_ctx) } } diff --git a/simulator/generation/query.rs b/simulator/generation/query.rs index 72541c4d7..88cd95126 100644 --- a/simulator/generation/query.rs +++ b/simulator/generation/query.rs @@ -1,42 +1,184 @@ -use crate::model::Query; -use rand::Rng; +use crate::{ + generation::WeightedDistribution, + model::{Query, QueryDiscriminants}, +}; +use rand::{ + Rng, + distr::{Distribution, weighted::WeightedIndex}, +}; use sql_generation::{ - generation::{Arbitrary, ArbitraryFrom, GenerationContext, frequency}, - model::query::{Create, Delete, Insert, Select, update::Update}, + generation::{Arbitrary, ArbitraryFrom, GenerationContext, query::SelectFree}, + model::{ + query::{Create, CreateIndex, Delete, Insert, Select, update::Update}, + table::Table, + }, }; use super::property::Remaining; -impl ArbitraryFrom<&Remaining> for Query { - fn arbitrary_from( - rng: &mut R, - context: &C, - remaining: &Remaining, - ) -> Self { - frequency( - vec![ - ( - remaining.create, - Box::new(|rng| Self::Create(Create::arbitrary(rng, context))), - ), - ( - remaining.select, - Box::new(|rng| Self::Select(Select::arbitrary(rng, context))), - ), - ( - remaining.insert, - Box::new(|rng| Self::Insert(Insert::arbitrary(rng, context))), - ), - ( - remaining.update, - Box::new(|rng| Self::Update(Update::arbitrary(rng, context))), - ), - ( - remaining.insert.min(remaining.delete), - Box::new(|rng| Self::Delete(Delete::arbitrary(rng, context))), - ), - ], - rng, - ) +fn random_create(rng: &mut R, conn_ctx: &impl GenerationContext) -> Query { + let mut create = Create::arbitrary(rng, conn_ctx); + while conn_ctx + .tables() + .iter() + .any(|t| t.name == create.table.name) + { + create = Create::arbitrary(rng, conn_ctx); + } + Query::Create(create) +} + +fn random_select(rng: &mut R, conn_ctx: &impl GenerationContext) -> Query { + if rng.random_bool(0.7) { + Query::Select(Select::arbitrary(rng, conn_ctx)) + } else { + // Random expression + Query::Select(SelectFree::arbitrary(rng, conn_ctx).0) + } +} + +fn random_insert(rng: &mut R, conn_ctx: &impl GenerationContext) -> Query { + assert!(!conn_ctx.tables().is_empty()); + Query::Insert(Insert::arbitrary(rng, conn_ctx)) +} + +fn random_delete(rng: &mut R, conn_ctx: &impl GenerationContext) -> Query { + assert!(!conn_ctx.tables().is_empty()); + Query::Delete(Delete::arbitrary(rng, conn_ctx)) +} + +fn random_update(rng: &mut R, conn_ctx: &impl GenerationContext) -> Query { + assert!(!conn_ctx.tables().is_empty()); + Query::Update(Update::arbitrary(rng, conn_ctx)) +} + +fn random_drop(rng: &mut R, conn_ctx: &impl GenerationContext) -> Query { + assert!(!conn_ctx.tables().is_empty()); + Query::Drop(sql_generation::model::query::Drop::arbitrary(rng, conn_ctx)) +} + +fn random_create_index( + rng: &mut R, + conn_ctx: &impl GenerationContext, +) -> Query { + assert!(!conn_ctx.tables().is_empty()); + + let mut create_index = CreateIndex::arbitrary(rng, conn_ctx); + while conn_ctx + .tables() + .iter() + .find(|t| t.name == create_index.table_name) + .expect("table should exist") + .indexes + .iter() + .any(|i| i == &create_index.index_name) + { + create_index = CreateIndex::arbitrary(rng, conn_ctx); + } + + Query::CreateIndex(create_index) +} + +/// Possible queries that can be generated given the table state +/// +/// Does not take into account transactional statements +pub const fn possible_queries(tables: &[Table]) -> &'static [QueryDiscriminants] { + if tables.is_empty() { + &[QueryDiscriminants::Select, QueryDiscriminants::Create] + } else { + QueryDiscriminants::ALL_NO_TRANSACTION + } +} + +type QueryGenFunc = fn(&mut R, &G) -> Query; + +impl QueryDiscriminants { + pub fn gen_function(&self) -> QueryGenFunc + where + R: rand::Rng + ?Sized, + G: GenerationContext, + { + match self { + QueryDiscriminants::Create => random_create, + QueryDiscriminants::Select => random_select, + QueryDiscriminants::Insert => random_insert, + QueryDiscriminants::Delete => random_delete, + QueryDiscriminants::Update => random_update, + QueryDiscriminants::Drop => random_drop, + QueryDiscriminants::CreateIndex => random_create_index, + QueryDiscriminants::Begin + | QueryDiscriminants::Commit + | QueryDiscriminants::Rollback => { + unreachable!("transactional queries should not be generated") + } + } + } + + pub fn weight(&self, remaining: &Remaining) -> u32 { + match self { + QueryDiscriminants::Create => remaining.create, + QueryDiscriminants::Select => remaining.select + remaining.select / 3, // remaining.select / 3 is for the random_expr generation + QueryDiscriminants::Insert => remaining.insert, + QueryDiscriminants::Delete => remaining.delete, + QueryDiscriminants::Update => remaining.update, + QueryDiscriminants::Drop => 0, + QueryDiscriminants::CreateIndex => remaining.create_index, + QueryDiscriminants::Begin + | QueryDiscriminants::Commit + | QueryDiscriminants::Rollback => { + unreachable!("transactional queries should not be generated") + } + } + } +} + +pub(super) struct QueryDistribution { + queries: &'static [QueryDiscriminants], + weights: WeightedIndex, +} + +impl QueryDistribution { + pub fn new(queries: &'static [QueryDiscriminants], remaining: &Remaining) -> Self { + let query_weights = + WeightedIndex::new(queries.iter().map(|query| query.weight(remaining))).unwrap(); + Self { + queries, + weights: query_weights, + } + } +} + +impl WeightedDistribution for QueryDistribution { + type Item = QueryDiscriminants; + type GenItem = Query; + + fn items(&self) -> &[Self::Item] { + self.queries + } + + fn weights(&self) -> &WeightedIndex { + &self.weights + } + + fn sample( + &self, + rng: &mut R, + ctx: &C, + ) -> Self::GenItem { + let weights = &self.weights; + + let idx = weights.sample(rng); + let query_fn = self.queries[idx].gen_function(); + (query_fn)(rng, ctx) + } +} + +impl ArbitraryFrom<&QueryDistribution> for Query { + fn arbitrary_from( + rng: &mut R, + context: &C, + query_distr: &QueryDistribution, + ) -> Self { + query_distr.sample(rng, context) } } diff --git a/simulator/model/mod.rs b/simulator/model/mod.rs index 551c08b1d..510922f6b 100644 --- a/simulator/model/mod.rs +++ b/simulator/model/mod.rs @@ -1,6 +1,7 @@ use std::fmt::Display; use anyhow::Context; +use bitflags::bitflags; use indexmap::IndexSet; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -18,7 +19,8 @@ use turso_parser::ast::Distinctness; use crate::{generation::Shadow, runner::env::ShadowTablesMut}; // This type represents the potential queries on the database. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, strum::EnumDiscriminants)] +#[strum_discriminants(derive(strum::VariantArray, strum::EnumIter))] pub enum Query { Create(Create), Select(Select), @@ -115,6 +117,74 @@ impl Shadow for Query { } } +bitflags! { + pub struct QueryCapabilities: u32 { + const CREATE = 1 << 0; + const SELECT = 1 << 1; + const INSERT = 1 << 2; + const DELETE = 1 << 3; + const UPDATE = 1 << 4; + const DROP = 1 << 5; + const CREATE_INDEX = 1 << 6; + } +} + +impl QueryCapabilities { + // TODO: can be const fn in the future + pub fn from_list_queries(queries: &[QueryDiscriminants]) -> Self { + queries + .iter() + .fold(Self::empty(), |accum, q| accum.union(q.into())) + } +} + +impl From<&QueryDiscriminants> for QueryCapabilities { + fn from(value: &QueryDiscriminants) -> Self { + (*value).into() + } +} + +impl From for QueryCapabilities { + fn from(value: QueryDiscriminants) -> Self { + match value { + QueryDiscriminants::Create => Self::CREATE, + QueryDiscriminants::Select => Self::SELECT, + QueryDiscriminants::Insert => Self::INSERT, + QueryDiscriminants::Delete => Self::DELETE, + QueryDiscriminants::Update => Self::UPDATE, + QueryDiscriminants::Drop => Self::DROP, + QueryDiscriminants::CreateIndex => Self::CREATE_INDEX, + QueryDiscriminants::Begin + | QueryDiscriminants::Commit + | QueryDiscriminants::Rollback => { + unreachable!("QueryCapabilities do not apply to transaction queries") + } + } + } +} + +impl QueryDiscriminants { + pub const ALL_NO_TRANSACTION: &[QueryDiscriminants] = &[ + QueryDiscriminants::Select, + QueryDiscriminants::Create, + QueryDiscriminants::Insert, + QueryDiscriminants::Update, + QueryDiscriminants::Delete, + QueryDiscriminants::Drop, + QueryDiscriminants::CreateIndex, + ]; + + #[inline] + pub fn is_transaction(&self) -> bool { + matches!(self, Self::Begin | Self::Commit | Self::Rollback) + } + + #[inline] + pub fn is_ddl(&self) -> bool { + matches!(self, Self::Create | Self::CreateIndex | Self::Drop) + } +} + impl Shadow for Create { type Result = anyhow::Result>>; diff --git a/sql_generation/generation/expr.rs b/sql_generation/generation/expr.rs index 25bdc0d97..e60baf78f 100644 --- a/sql_generation/generation/expr.rs +++ b/sql_generation/generation/expr.rs @@ -14,7 +14,7 @@ impl Arbitrary for Box where T: Arbitrary, { - fn arbitrary(rng: &mut R, context: &C) -> Self { + fn arbitrary(rng: &mut R, context: &C) -> Self { Box::from(T::arbitrary(rng, context)) } } @@ -23,7 +23,7 @@ impl ArbitrarySized for Box where T: ArbitrarySized, { - fn arbitrary_sized( + fn arbitrary_sized( rng: &mut R, context: &C, size: usize, @@ -36,7 +36,7 @@ impl ArbitrarySizedFrom for Box where T: ArbitrarySizedFrom, { - fn arbitrary_sized_from( + fn arbitrary_sized_from( rng: &mut R, context: &C, t: A, @@ -50,7 +50,7 @@ impl Arbitrary for Option where T: Arbitrary, { - fn arbitrary(rng: &mut R, context: &C) -> Self { + fn arbitrary(rng: &mut R, context: &C) -> Self { rng.random_bool(0.5).then_some(T::arbitrary(rng, context)) } } @@ -59,7 +59,7 @@ impl ArbitrarySizedFrom for Option where T: ArbitrarySizedFrom, { - fn arbitrary_sized_from( + fn arbitrary_sized_from( rng: &mut R, context: &C, t: A, @@ -74,7 +74,11 @@ impl ArbitraryFrom for Vec where T: ArbitraryFrom, { - fn arbitrary_from(rng: &mut R, context: &C, 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, context, t)) @@ -84,7 +88,7 @@ where // Freestyling generation impl ArbitrarySized for Expr { - fn arbitrary_sized( + fn arbitrary_sized( rng: &mut R, context: &C, size: usize, @@ -188,7 +192,7 @@ impl ArbitrarySized for Expr { } impl Arbitrary for Operator { - fn arbitrary(rng: &mut R, _context: &C) -> Self { + fn arbitrary(rng: &mut R, _context: &C) -> Self { let choices = [ Operator::Add, Operator::And, @@ -219,7 +223,7 @@ impl Arbitrary for Operator { } impl Arbitrary for Type { - fn arbitrary(rng: &mut R, _context: &C) -> Self { + fn arbitrary(rng: &mut R, _context: &C) -> Self { let name = pick(&["INT", "INTEGER", "REAL", "TEXT", "BLOB", "ANY"], rng).to_string(); Self { name, @@ -229,7 +233,7 @@ impl Arbitrary for Type { } impl Arbitrary for QualifiedName { - fn arbitrary(rng: &mut R, context: &C) -> Self { + fn arbitrary(rng: &mut R, context: &C) -> Self { // TODO: for now just generate table name let table_idx = pick_index(context.tables().len(), rng); let table = &context.tables()[table_idx]; @@ -243,7 +247,7 @@ impl Arbitrary for QualifiedName { } impl Arbitrary for LikeOperator { - fn arbitrary(rng: &mut R, _t: &C) -> Self { + fn arbitrary(rng: &mut R, _t: &C) -> Self { let choice = rng.random_range(0..4); match choice { 0 => LikeOperator::Glob, @@ -257,7 +261,7 @@ impl Arbitrary for LikeOperator { // Current implementation does not take into account the columns affinity nor if table is Strict impl Arbitrary for ast::Literal { - fn arbitrary(rng: &mut R, _t: &C) -> Self { + fn arbitrary(rng: &mut R, _t: &C) -> Self { loop { let choice = rng.random_range(0..5); let lit = match choice { @@ -284,7 +288,7 @@ impl Arbitrary for ast::Literal { // Creates a litreal value impl ArbitraryFrom<&Vec<&SimValue>> for ast::Expr { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, _context: &C, values: &Vec<&SimValue>, @@ -299,7 +303,7 @@ impl ArbitraryFrom<&Vec<&SimValue>> for ast::Expr { } impl Arbitrary for UnaryOperator { - fn arbitrary(rng: &mut R, _t: &C) -> Self { + 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 25f353673..1292b3448 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, context: &C) -> Self; + fn arbitrary(rng: &mut R, context: &C) -> Self; } /// ArbitrarySized trait for generating random values of a specific size @@ -29,8 +29,11 @@ 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, context: &C, size: usize) - -> Self; + fn arbitrary_sized( + rng: &mut R, + context: &C, + size: usize, + ) -> Self; } /// ArbitraryFrom trait for generating random values from a given value @@ -39,7 +42,11 @@ 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, 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 @@ -51,7 +58,7 @@ pub trait ArbitraryFrom { /// 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( + fn arbitrary_sized_from( rng: &mut R, context: &C, t: T, @@ -61,7 +68,7 @@ pub trait ArbitrarySizedFrom { /// ArbitraryFromMaybe trait for fallibally generating random values from a given value pub trait ArbitraryFromMaybe { - fn arbitrary_from_maybe( + fn arbitrary_from_maybe( rng: &mut R, context: &C, t: T, @@ -77,7 +84,11 @@ pub trait ArbitraryFromMaybe { /// the operations we require for the implementation. // todo: switch to a simpler type signature that can accommodate all integer and float types, which // should be enough for our purposes. -pub fn frequency( +pub fn frequency< + T, + R: Rng + ?Sized, + N: Sum + PartialOrd + Copy + Default + SampleUniform + SubAssign, +>( choices: Vec<(N, ArbitraryFromFunc)>, rng: &mut R, ) -> T { @@ -95,7 +106,7 @@ pub fn frequency(choices: Vec>, rng: &mut R) -> T { +pub fn one_of(choices: Vec>, rng: &mut R) -> T { let index = rng.random_range(0..choices.len()); choices[index](rng) } @@ -103,7 +114,7 @@ pub fn one_of(choices: Vec>, rng: &mut R) -> /// backtrack is a helper function for composing different "failable" generators. /// The function takes a list of functions that return an Option, along with number of retries /// to make before giving up. -pub fn backtrack(mut choices: Vec>, rng: &mut R) -> Option { +pub fn backtrack(mut choices: Vec>, rng: &mut R) -> Option { loop { // If there are no more choices left, we give up let choices_ = choices @@ -129,20 +140,20 @@ pub fn backtrack(mut choices: Vec>, rng: &mut R) -> Opti } /// pick is a helper function for uniformly picking a random element from a slice -pub fn pick<'a, T, R: Rng>(choices: &'a [T], rng: &mut R) -> &'a T { +pub fn pick<'a, T, R: Rng + ?Sized>(choices: &'a [T], rng: &mut R) -> &'a T { let index = rng.random_range(0..choices.len()); &choices[index] } /// pick_index is typically used for picking an index from a slice to later refer to the element /// at that index. -pub fn pick_index(choices: usize, rng: &mut R) -> usize { +pub fn pick_index(choices: usize, rng: &mut R) -> usize { rng.random_range(0..choices) } /// pick_n_unique is a helper function for uniformly picking N unique elements from a range. /// The elements themselves are usize, typically representing indices. -pub fn pick_n_unique( +pub fn pick_n_unique( range: std::ops::Range, n: usize, rng: &mut R, @@ -155,7 +166,7 @@ pub fn pick_n_unique( /// gen_random_text uses `anarchist_readable_name_generator_lib` to generate random /// readable names for tables, columns, text values etc. -pub fn gen_random_text(rng: &mut T) -> String { +pub fn gen_random_text(rng: &mut R) -> String { let big_text = rng.random_ratio(1, 1000); if big_text { // let max_size: u64 = 2 * 1024 * 1024 * 1024; @@ -172,10 +183,10 @@ pub fn gen_random_text(rng: &mut T) -> String { } } -pub fn pick_unique<'a, T: PartialEq>( +pub fn pick_unique<'a, T: PartialEq, R: Rng + ?Sized>( items: &'a [T], count: usize, - rng: &mut impl rand::Rng, + rng: &mut R, ) -> impl Iterator { let mut picked: Vec<&T> = Vec::new(); while picked.len() < count { diff --git a/sql_generation/generation/predicate/binary.rs b/sql_generation/generation/predicate/binary.rs index 9867a561a..37b2e4e93 100644 --- a/sql_generation/generation/predicate/binary.rs +++ b/sql_generation/generation/predicate/binary.rs @@ -17,7 +17,7 @@ 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, @@ -55,7 +55,7 @@ impl Predicate { } /// Produces a true [ast::Expr::Binary] [Predicate] that is true for the provided row in the given table - pub fn true_binary( + pub fn true_binary( rng: &mut R, context: &C, t: &Table, @@ -168,7 +168,7 @@ impl Predicate { } /// Produces an [ast::Expr::Binary] [Predicate] that is false for the provided row in the given table - pub fn false_binary( + pub fn false_binary( rng: &mut R, context: &C, t: &Table, @@ -253,7 +253,7 @@ 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, @@ -311,7 +311,7 @@ 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, @@ -373,7 +373,7 @@ 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, diff --git a/sql_generation/generation/predicate/mod.rs b/sql_generation/generation/predicate/mod.rs index 78fa30ae4..d0dd375bb 100644 --- a/sql_generation/generation/predicate/mod.rs +++ b/sql_generation/generation/predicate/mod.rs @@ -21,7 +21,7 @@ struct CompoundPredicate(Predicate); struct SimplePredicate(Predicate); impl, T: TableContext> ArbitraryFrom<(&T, A, bool)> for SimplePredicate { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, context: &C, (table, row, predicate_value): (&T, A, bool), @@ -46,7 +46,7 @@ impl, T: TableContext> ArbitraryFrom<(&T, A, bool)> for Sim } impl ArbitraryFrom<(&T, bool)> for CompoundPredicate { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, context: &C, (table, predicate_value): (&T, bool), @@ -56,14 +56,18 @@ impl ArbitraryFrom<(&T, bool)> for CompoundPredicate { } impl ArbitraryFrom<&T> for Predicate { - fn arbitrary_from(rng: &mut R, context: &C, 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, context, (table, predicate_value)).parens() } } impl ArbitraryFrom<(&T, bool)> for Predicate { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, context: &C, (table, predicate_value): (&T, bool), @@ -73,7 +77,7 @@ impl ArbitraryFrom<(&T, bool)> for Predicate { } impl ArbitraryFrom<(&str, &SimValue)> for Predicate { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, context: &C, (column_name, value): (&str, &SimValue), @@ -83,7 +87,7 @@ impl ArbitraryFrom<(&str, &SimValue)> for Predicate { } impl ArbitraryFrom<(&Table, &Vec)> for Predicate { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, context: &C, (t, row): (&Table, &Vec), diff --git a/sql_generation/generation/predicate/unary.rs b/sql_generation/generation/predicate/unary.rs index 1cc0e0d24..31dfc2a7a 100644 --- a/sql_generation/generation/predicate/unary.rs +++ b/sql_generation/generation/predicate/unary.rs @@ -17,7 +17,7 @@ use crate::{ pub struct TrueValue(pub SimValue); impl ArbitraryFromMaybe<&SimValue> for TrueValue { - fn arbitrary_from_maybe( + fn arbitrary_from_maybe( _rng: &mut R, _context: &C, value: &SimValue, @@ -31,7 +31,7 @@ impl ArbitraryFromMaybe<&SimValue> for TrueValue { } impl ArbitraryFromMaybe<&Vec<&SimValue>> for TrueValue { - fn arbitrary_from_maybe( + fn arbitrary_from_maybe( rng: &mut R, context: &C, values: &Vec<&SimValue>, @@ -51,7 +51,7 @@ impl ArbitraryFromMaybe<&Vec<&SimValue>> for TrueValue { pub struct FalseValue(pub SimValue); impl ArbitraryFromMaybe<&SimValue> for FalseValue { - fn arbitrary_from_maybe( + fn arbitrary_from_maybe( _rng: &mut R, _context: &C, value: &SimValue, @@ -65,7 +65,7 @@ impl ArbitraryFromMaybe<&SimValue> for FalseValue { } impl ArbitraryFromMaybe<&Vec<&SimValue>> for FalseValue { - fn arbitrary_from_maybe( + fn arbitrary_from_maybe( rng: &mut R, context: &C, values: &Vec<&SimValue>, @@ -86,7 +86,7 @@ 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), @@ -101,7 +101,7 @@ 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), @@ -121,7 +121,7 @@ impl ArbitraryFromMaybe<(&Vec<&SimValue>, bool)> for BitNotValue { // 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, @@ -187,7 +187,7 @@ 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, diff --git a/sql_generation/generation/query.rs b/sql_generation/generation/query.rs index a0e0e47b0..f2264720e 100644 --- a/sql_generation/generation/query.rs +++ b/sql_generation/generation/query.rs @@ -18,7 +18,7 @@ use turso_parser::ast::{Expr, SortOrder}; use super::{backtrack, pick}; impl Arbitrary for Create { - fn arbitrary(rng: &mut R, context: &C) -> Self { + fn arbitrary(rng: &mut R, context: &C) -> Self { Create { table: Table::arbitrary(rng, context), } @@ -26,7 +26,7 @@ impl Arbitrary for Create { } impl Arbitrary for FromClause { - fn arbitrary(rng: &mut R, context: &C) -> Self { + 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; @@ -85,7 +85,7 @@ impl Arbitrary for FromClause { } impl Arbitrary for SelectInner { - fn arbitrary(rng: &mut R, env: &C) -> Self { + 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); @@ -144,7 +144,7 @@ impl Arbitrary for SelectInner { } impl ArbitrarySized for SelectInner { - fn arbitrary_sized( + fn arbitrary_sized( rng: &mut R, env: &C, num_result_columns: usize, @@ -179,7 +179,7 @@ impl ArbitrarySized for SelectInner { } impl Arbitrary for Distinctness { - fn arbitrary(rng: &mut R, _context: &C) -> Self { + fn arbitrary(rng: &mut R, _context: &C) -> Self { match rng.random_range(0..=5) { 0..4 => Distinctness::All, _ => Distinctness::Distinct, @@ -188,7 +188,7 @@ impl Arbitrary for Distinctness { } impl Arbitrary for CompoundOperator { - fn arbitrary(rng: &mut R, _context: &C) -> Self { + fn arbitrary(rng: &mut R, _context: &C) -> Self { match rng.random_range(0..=1) { 0 => CompoundOperator::Union, 1 => CompoundOperator::UnionAll, @@ -203,7 +203,7 @@ impl Arbitrary for CompoundOperator { pub struct SelectFree(pub Select); impl Arbitrary for SelectFree { - fn arbitrary(rng: &mut R, env: &C) -> Self { + fn arbitrary(rng: &mut R, env: &C) -> Self { let expr = Predicate(Expr::arbitrary_sized(rng, env, 8)); let select = Select::expr(expr); Self(select) @@ -211,7 +211,7 @@ impl Arbitrary for SelectFree { } impl Arbitrary for Select { - fn arbitrary(rng: &mut R, env: &C) -> Self { + 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 @@ -259,7 +259,7 @@ impl Arbitrary for Select { } impl Arbitrary for Insert { - fn arbitrary(rng: &mut R, env: &C) -> Self { + fn arbitrary(rng: &mut R, env: &C) -> Self { let opts = &env.opts().query.insert; let gen_values = |rng: &mut R| { let table = pick(env.tables(), rng); @@ -300,7 +300,7 @@ impl Arbitrary for Insert { } impl Arbitrary for Delete { - fn arbitrary(rng: &mut R, env: &C) -> Self { + fn arbitrary(rng: &mut R, env: &C) -> Self { let table = pick(env.tables(), rng); Self { table: table.name.clone(), @@ -310,7 +310,7 @@ impl Arbitrary for Delete { } impl Arbitrary for Drop { - fn arbitrary(rng: &mut R, env: &C) -> Self { + fn arbitrary(rng: &mut R, env: &C) -> Self { let table = pick(env.tables(), rng); Self { table: table.name.clone(), @@ -319,7 +319,7 @@ impl Arbitrary for Drop { } impl Arbitrary for CreateIndex { - fn arbitrary(rng: &mut R, env: &C) -> Self { + fn arbitrary(rng: &mut R, env: &C) -> Self { assert!( !env.tables().is_empty(), "Cannot create an index when no tables exist in the environment." @@ -366,7 +366,7 @@ impl Arbitrary for CreateIndex { } impl Arbitrary for Update { - fn arbitrary(rng: &mut R, env: &C) -> Self { + 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); diff --git a/sql_generation/generation/table.rs b/sql_generation/generation/table.rs index ce0ff97f4..9f038a379 100644 --- a/sql_generation/generation/table.rs +++ b/sql_generation/generation/table.rs @@ -10,14 +10,14 @@ use crate::model::table::{Column, ColumnType, Name, SimValue, Table}; use super::ArbitraryFromMaybe; impl Arbitrary for Name { - fn arbitrary(rng: &mut R, _c: &C) -> Self { + fn arbitrary(rng: &mut R, _c: &C) -> Self { let name = readable_name_custom("_", rng); Name(name.replace("-", "_")) } } impl Arbitrary for Table { - fn arbitrary(rng: &mut R, context: &C) -> Self { + fn arbitrary(rng: &mut R, context: &C) -> Self { let opts = context.opts().table.clone(); let name = Name::arbitrary(rng, context).0; let large_table = @@ -45,7 +45,7 @@ impl Arbitrary for Table { } impl Arbitrary for Column { - fn arbitrary(rng: &mut R, context: &C) -> Self { + fn arbitrary(rng: &mut R, context: &C) -> Self { let name = Name::arbitrary(rng, context).0; let column_type = ColumnType::arbitrary(rng, context); Self { @@ -58,13 +58,13 @@ impl Arbitrary for Column { } impl Arbitrary for ColumnType { - fn arbitrary(rng: &mut R, _context: &C) -> 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( + fn arbitrary_from( rng: &mut R, context: &C, table: &Table, @@ -79,7 +79,7 @@ impl ArbitraryFrom<&Table> for Vec { } impl ArbitraryFrom<&Vec<&SimValue>> for SimValue { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, _context: &C, values: &Vec<&Self>, @@ -93,7 +93,7 @@ impl ArbitraryFrom<&Vec<&SimValue>> for SimValue { } impl ArbitraryFrom<&ColumnType> for SimValue { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, _context: &C, column_type: &ColumnType, @@ -111,7 +111,7 @@ impl ArbitraryFrom<&ColumnType> for SimValue { pub struct LTValue(pub SimValue); impl ArbitraryFrom<&Vec<&SimValue>> for LTValue { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, context: &C, values: &Vec<&SimValue>, @@ -127,7 +127,7 @@ impl ArbitraryFrom<&Vec<&SimValue>> for LTValue { } impl ArbitraryFrom<&SimValue> for LTValue { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, _context: &C, value: &SimValue, @@ -181,7 +181,7 @@ impl ArbitraryFrom<&SimValue> for LTValue { pub struct GTValue(pub SimValue); impl ArbitraryFrom<&Vec<&SimValue>> for GTValue { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, context: &C, values: &Vec<&SimValue>, @@ -197,7 +197,7 @@ impl ArbitraryFrom<&Vec<&SimValue>> for GTValue { } impl ArbitraryFrom<&SimValue> for GTValue { - fn arbitrary_from( + fn arbitrary_from( rng: &mut R, _context: &C, value: &SimValue, @@ -251,7 +251,7 @@ impl ArbitraryFrom<&SimValue> for GTValue { pub struct LikeValue(pub SimValue); impl ArbitraryFromMaybe<&SimValue> for LikeValue { - fn arbitrary_from_maybe( + fn arbitrary_from_maybe( rng: &mut R, _context: &C, value: &SimValue,