mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-26 20:44:23 +01:00
Merge 'Modify Interactions Generation to only generate possible queries' from Pedro Muniz
I simplified `Interactions` generation code to first check the possible queries we can generate and then create a Weighted Distribution to generate `Query` and `Property`. This change allows us to not generate certain types of queries if we have no tables, as it is not sound for the Simulator to generate an `Update` query if there are no tables in the database. I made this change for correctness and because I want to introduce schema changes to the simulator and this is necessary. Also, there is the additional benefit of us allocating less memory, doing static function dispatch instead of creating closures all the time for generation, and the code, in my opinion, being more readable. Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com> Reviewed-by: bit-aloo (@Shourya742) Closes #3585
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2346,6 +2346,7 @@ name = "limbo_sim"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.9.4",
|
||||
"bitmaps",
|
||||
"chrono",
|
||||
"clap",
|
||||
|
||||
@@ -46,3 +46,4 @@ either = "1.15.0"
|
||||
similar = { workspace = true }
|
||||
similar-asserts = { workspace = true }
|
||||
bitmaps = { workspace = true }
|
||||
bitflags.workspace = true
|
||||
|
||||
@@ -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<u32>;
|
||||
fn sample<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
) -> Self::GenItem;
|
||||
}
|
||||
|
||||
@@ -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<R: rand::Rng>(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<R: rand::Rng>(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<R: rand::Rng>(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<R: rand::Rng>(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<R: rand::Rng>(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<R: rand::Rng>(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<R: rand::Rng>(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<R: rand::Rng>(
|
||||
fn random_fault<R: rand::Rng + ?Sized>(
|
||||
rng: &mut R,
|
||||
env: &SimulatorEnv,
|
||||
conn_index: usize,
|
||||
) -> Option<Interactions> {
|
||||
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<R: rand::Rng>(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<R: rand::Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
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,
|
||||
|
||||
@@ -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<R: rand::Rng>(
|
||||
fn property_insert_values_select<R: rand::Rng + ?Sized>(
|
||||
rng: &mut R,
|
||||
remaining: &Remaining,
|
||||
query_distr: &QueryDistribution,
|
||||
ctx: &impl GenerationContext,
|
||||
mvcc: bool,
|
||||
) -> Property {
|
||||
@@ -1251,7 +1258,7 @@ fn property_insert_values_select<R: rand::Rng>(
|
||||
}));
|
||||
}
|
||||
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<R: rand::Rng>(
|
||||
}
|
||||
}
|
||||
|
||||
fn property_read_your_updates_back<R: rand::Rng>(
|
||||
fn property_read_your_updates_back<R: rand::Rng + ?Sized>(
|
||||
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<R: rand::Rng>(
|
||||
Property::ReadYourUpdatesBack { update, select }
|
||||
}
|
||||
|
||||
fn property_table_has_expected_content<R: rand::Rng>(
|
||||
fn property_table_has_expected_content<R: rand::Rng + ?Sized>(
|
||||
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<R: rand::Rng>(
|
||||
}
|
||||
}
|
||||
|
||||
fn property_select_limit<R: rand::Rng>(rng: &mut R, ctx: &impl GenerationContext) -> Property {
|
||||
fn property_select_limit<R: rand::Rng + ?Sized>(
|
||||
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<R: rand::Rng>(rng: &mut R, ctx: &impl GenerationContext
|
||||
Property::SelectLimit { select }
|
||||
}
|
||||
|
||||
fn property_double_create_failure<R: rand::Rng>(
|
||||
fn property_double_create_failure<R: rand::Rng + ?Sized>(
|
||||
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<R: rand::Rng>(
|
||||
// - [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<R: rand::Rng>(
|
||||
}
|
||||
}
|
||||
|
||||
fn property_delete_select<R: rand::Rng>(
|
||||
fn property_delete_select<R: rand::Rng + ?Sized>(
|
||||
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<R: rand::Rng>(
|
||||
// - [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<R: rand::Rng>(
|
||||
}
|
||||
}
|
||||
|
||||
fn property_drop_select<R: rand::Rng>(
|
||||
fn property_drop_select<R: rand::Rng + ?Sized>(
|
||||
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<R: rand::Rng>(
|
||||
// - [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<R: rand::Rng>(
|
||||
}
|
||||
}
|
||||
|
||||
fn property_select_select_optimizer<R: rand::Rng>(
|
||||
fn property_select_select_optimizer<R: rand::Rng + ?Sized>(
|
||||
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<R: rand::Rng>(
|
||||
}
|
||||
}
|
||||
|
||||
fn property_where_true_false_null<R: rand::Rng>(
|
||||
fn property_where_true_false_null<R: rand::Rng + ?Sized>(
|
||||
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<R: rand::Rng>(
|
||||
}
|
||||
}
|
||||
|
||||
fn property_union_all_preserves_cardinality<R: rand::Rng>(
|
||||
fn property_union_all_preserves_cardinality<R: rand::Rng + ?Sized>(
|
||||
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<R: rand::Rng>(
|
||||
}
|
||||
}
|
||||
|
||||
fn property_fsync_no_wait<R: rand::Rng>(
|
||||
fn property_fsync_no_wait<R: rand::Rng + ?Sized>(
|
||||
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<R: rand::Rng>(
|
||||
fn property_faulty_query<R: rand::Rng + ?Sized>(
|
||||
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<R: rand::Rng, C: GenerationContext>(
|
||||
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<R, G> = fn(&mut R, &QueryDistribution, &G, bool) -> Property;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
let choices: Vec<(_, Box<dyn Fn(&mut R) -> Property>)> = vec![
|
||||
(
|
||||
impl PropertyDiscriminants {
|
||||
pub(super) fn gen_function<R, G>(&self) -> PropertyGenFunc<R, G>
|
||||
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<PropertyDiscriminants> {
|
||||
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<PropertyDiscriminants> {
|
||||
let queries = possible_queries(tables);
|
||||
PropertyDiscriminants::can_generate(queries)
|
||||
}
|
||||
|
||||
pub(super) struct PropertyDistribution<'a> {
|
||||
properties: Vec<PropertyDiscriminants>,
|
||||
weights: WeightedIndex<u32>,
|
||||
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<u32> {
|
||||
&self.weights
|
||||
}
|
||||
|
||||
fn sample<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
&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<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
conn_ctx: &C,
|
||||
property_distr: &PropertyDistribution<'a>,
|
||||
) -> Self {
|
||||
property_distr.sample(rng, conn_ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<R: Rng, C: GenerationContext>(
|
||||
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<R: rand::Rng + ?Sized>(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<R: rand::Rng + ?Sized>(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<R: rand::Rng + ?Sized>(rng: &mut R, conn_ctx: &impl GenerationContext) -> Query {
|
||||
assert!(!conn_ctx.tables().is_empty());
|
||||
Query::Insert(Insert::arbitrary(rng, conn_ctx))
|
||||
}
|
||||
|
||||
fn random_delete<R: rand::Rng + ?Sized>(rng: &mut R, conn_ctx: &impl GenerationContext) -> Query {
|
||||
assert!(!conn_ctx.tables().is_empty());
|
||||
Query::Delete(Delete::arbitrary(rng, conn_ctx))
|
||||
}
|
||||
|
||||
fn random_update<R: rand::Rng + ?Sized>(rng: &mut R, conn_ctx: &impl GenerationContext) -> Query {
|
||||
assert!(!conn_ctx.tables().is_empty());
|
||||
Query::Update(Update::arbitrary(rng, conn_ctx))
|
||||
}
|
||||
|
||||
fn random_drop<R: rand::Rng + ?Sized>(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<R: rand::Rng + ?Sized>(
|
||||
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<R, G> = fn(&mut R, &G) -> Query;
|
||||
|
||||
impl QueryDiscriminants {
|
||||
pub fn gen_function<R, G>(&self) -> QueryGenFunc<R, G>
|
||||
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<u32>,
|
||||
}
|
||||
|
||||
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<u32> {
|
||||
&self.weights
|
||||
}
|
||||
|
||||
fn sample<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
&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<R: Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
query_distr: &QueryDistribution,
|
||||
) -> Self {
|
||||
query_distr.sample(rng, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<QueryDiscriminants> 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<Vec<Vec<SimValue>>>;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ impl<T> Arbitrary for Box<T>
|
||||
where
|
||||
T: Arbitrary,
|
||||
{
|
||||
fn arbitrary<R: rand::Rng, C: GenerationContext>(rng: &mut R, context: &C) -> Self {
|
||||
fn arbitrary<R: rand::Rng + ?Sized, C: GenerationContext>(rng: &mut R, context: &C) -> Self {
|
||||
Box::from(T::arbitrary(rng, context))
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ impl<T> ArbitrarySized for Box<T>
|
||||
where
|
||||
T: ArbitrarySized,
|
||||
{
|
||||
fn arbitrary_sized<R: rand::Rng, C: GenerationContext>(
|
||||
fn arbitrary_sized<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
size: usize,
|
||||
@@ -36,7 +36,7 @@ impl<A, T> ArbitrarySizedFrom<A> for Box<T>
|
||||
where
|
||||
T: ArbitrarySizedFrom<A>,
|
||||
{
|
||||
fn arbitrary_sized_from<R: rand::Rng, C: GenerationContext>(
|
||||
fn arbitrary_sized_from<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
t: A,
|
||||
@@ -50,7 +50,7 @@ impl<T> Arbitrary for Option<T>
|
||||
where
|
||||
T: Arbitrary,
|
||||
{
|
||||
fn arbitrary<R: rand::Rng, C: GenerationContext>(rng: &mut R, context: &C) -> Self {
|
||||
fn arbitrary<R: rand::Rng + ?Sized, C: GenerationContext>(rng: &mut R, context: &C) -> Self {
|
||||
rng.random_bool(0.5).then_some(T::arbitrary(rng, context))
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ impl<A, T> ArbitrarySizedFrom<A> for Option<T>
|
||||
where
|
||||
T: ArbitrarySizedFrom<A>,
|
||||
{
|
||||
fn arbitrary_sized_from<R: rand::Rng, C: GenerationContext>(
|
||||
fn arbitrary_sized_from<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
t: A,
|
||||
@@ -74,7 +74,11 @@ impl<A: Copy, T> ArbitraryFrom<A> for Vec<T>
|
||||
where
|
||||
T: ArbitraryFrom<A>,
|
||||
{
|
||||
fn arbitrary_from<R: rand::Rng, C: GenerationContext>(rng: &mut R, context: &C, t: A) -> Self {
|
||||
fn arbitrary_from<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: rand::Rng, C: GenerationContext>(
|
||||
fn arbitrary_sized<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
size: usize,
|
||||
@@ -188,7 +192,7 @@ impl ArbitrarySized for Expr {
|
||||
}
|
||||
|
||||
impl Arbitrary for Operator {
|
||||
fn arbitrary<R: rand::Rng, C: GenerationContext>(rng: &mut R, _context: &C) -> Self {
|
||||
fn arbitrary<R: rand::Rng + ?Sized, C: GenerationContext>(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<R: rand::Rng, C: GenerationContext>(rng: &mut R, _context: &C) -> Self {
|
||||
fn arbitrary<R: rand::Rng + ?Sized, C: GenerationContext>(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<R: rand::Rng, C: GenerationContext>(rng: &mut R, context: &C) -> Self {
|
||||
fn arbitrary<R: rand::Rng + ?Sized, C: GenerationContext>(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<R: rand::Rng, C: GenerationContext>(rng: &mut R, _t: &C) -> Self {
|
||||
fn arbitrary<R: rand::Rng + ?Sized, C: GenerationContext>(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<R: rand::Rng, C: GenerationContext>(rng: &mut R, _t: &C) -> Self {
|
||||
fn arbitrary<R: rand::Rng + ?Sized, C: GenerationContext>(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<R: rand::Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: rand::Rng, C: GenerationContext>(rng: &mut R, _t: &C) -> Self {
|
||||
fn arbitrary<R: rand::Rng + ?Sized, C: GenerationContext>(rng: &mut R, _t: &C) -> Self {
|
||||
let choice = rng.random_range(0..4);
|
||||
match choice {
|
||||
0 => Self::BitwiseNot,
|
||||
|
||||
@@ -19,7 +19,7 @@ type Choice<'a, R, T> = (usize, Box<dyn Fn(&mut R) -> Option<T> + 'a>);
|
||||
/// the possible values of the type, with a bias towards smaller values for
|
||||
/// practicality.
|
||||
pub trait Arbitrary {
|
||||
fn arbitrary<R: Rng, C: GenerationContext>(rng: &mut R, context: &C) -> Self;
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, context: &C, size: usize)
|
||||
-> Self;
|
||||
fn arbitrary_sized<R: Rng + ?Sized, C: GenerationContext>(
|
||||
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<T> {
|
||||
fn arbitrary_from<R: Rng, C: GenerationContext>(rng: &mut R, context: &C, t: T) -> Self;
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
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<T> {
|
||||
/// 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<T> {
|
||||
fn arbitrary_sized_from<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_sized_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
t: T,
|
||||
@@ -61,7 +68,7 @@ pub trait ArbitrarySizedFrom<T> {
|
||||
|
||||
/// ArbitraryFromMaybe trait for fallibally generating random values from a given value
|
||||
pub trait ArbitraryFromMaybe<T> {
|
||||
fn arbitrary_from_maybe<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from_maybe<R: Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
t: T,
|
||||
@@ -77,7 +84,11 @@ pub trait ArbitraryFromMaybe<T> {
|
||||
/// 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<T, R: Rng, N: Sum + PartialOrd + Copy + Default + SampleUniform + SubAssign>(
|
||||
pub fn frequency<
|
||||
T,
|
||||
R: Rng + ?Sized,
|
||||
N: Sum + PartialOrd + Copy + Default + SampleUniform + SubAssign,
|
||||
>(
|
||||
choices: Vec<(N, ArbitraryFromFunc<R, T>)>,
|
||||
rng: &mut R,
|
||||
) -> T {
|
||||
@@ -95,7 +106,7 @@ pub fn frequency<T, R: Rng, N: Sum + PartialOrd + Copy + Default + SampleUniform
|
||||
}
|
||||
|
||||
/// one_of is a helper function for composing different generators with equal probability of occurrence.
|
||||
pub fn one_of<T, R: Rng>(choices: Vec<ArbitraryFromFunc<R, T>>, rng: &mut R) -> T {
|
||||
pub fn one_of<T, R: Rng + ?Sized>(choices: Vec<ArbitraryFromFunc<R, T>>, rng: &mut R) -> T {
|
||||
let index = rng.random_range(0..choices.len());
|
||||
choices[index](rng)
|
||||
}
|
||||
@@ -103,7 +114,7 @@ pub fn one_of<T, R: Rng>(choices: Vec<ArbitraryFromFunc<R, T>>, rng: &mut R) ->
|
||||
/// backtrack is a helper function for composing different "failable" generators.
|
||||
/// The function takes a list of functions that return an Option<T>, along with number of retries
|
||||
/// to make before giving up.
|
||||
pub fn backtrack<T, R: Rng>(mut choices: Vec<Choice<R, T>>, rng: &mut R) -> Option<T> {
|
||||
pub fn backtrack<T, R: Rng + ?Sized>(mut choices: Vec<Choice<R, T>>, rng: &mut R) -> Option<T> {
|
||||
loop {
|
||||
// If there are no more choices left, we give up
|
||||
let choices_ = choices
|
||||
@@ -129,20 +140,20 @@ pub fn backtrack<T, R: Rng>(mut choices: Vec<Choice<R, T>>, 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<R: Rng>(choices: usize, rng: &mut R) -> usize {
|
||||
pub fn pick_index<R: Rng + ?Sized>(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<R: Rng>(
|
||||
pub fn pick_n_unique<R: Rng + ?Sized>(
|
||||
range: std::ops::Range<usize>,
|
||||
n: usize,
|
||||
rng: &mut R,
|
||||
@@ -155,7 +166,7 @@ pub fn pick_n_unique<R: Rng>(
|
||||
|
||||
/// 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<T: Rng>(rng: &mut T) -> String {
|
||||
pub fn gen_random_text<R: Rng + ?Sized>(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<T: Rng>(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<Item = &'a T> {
|
||||
let mut picked: Vec<&T> = Vec::new();
|
||||
while picked.len() < count {
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::{
|
||||
|
||||
impl Predicate {
|
||||
/// Generate an [ast::Expr::Binary] [Predicate] from a column and [SimValue]
|
||||
pub fn from_column_binary<R: rand::Rng, C: GenerationContext>(
|
||||
pub fn from_column_binary<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: rand::Rng, C: GenerationContext>(
|
||||
pub fn true_binary<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: rand::Rng, C: GenerationContext>(
|
||||
pub fn false_binary<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: rand::Rng, C: GenerationContext, T: TableContext>(
|
||||
pub fn true_binary<R: rand::Rng + ?Sized, C: GenerationContext, T: TableContext>(
|
||||
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<R: rand::Rng, C: GenerationContext, T: TableContext>(
|
||||
pub fn false_binary<R: rand::Rng + ?Sized, C: GenerationContext, T: TableContext>(
|
||||
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<R: rand::Rng, C: GenerationContext, T: TableContext>(
|
||||
pub fn from_table_binary<R: rand::Rng + ?Sized, C: GenerationContext, T: TableContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
table: &T,
|
||||
|
||||
@@ -21,7 +21,7 @@ struct CompoundPredicate(Predicate);
|
||||
struct SimplePredicate(Predicate);
|
||||
|
||||
impl<A: AsRef<[SimValue]>, T: TableContext> ArbitraryFrom<(&T, A, bool)> for SimplePredicate {
|
||||
fn arbitrary_from<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
(table, row, predicate_value): (&T, A, bool),
|
||||
@@ -46,7 +46,7 @@ impl<A: AsRef<[SimValue]>, T: TableContext> ArbitraryFrom<(&T, A, bool)> for Sim
|
||||
}
|
||||
|
||||
impl<T: TableContext> ArbitraryFrom<(&T, bool)> for CompoundPredicate {
|
||||
fn arbitrary_from<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
(table, predicate_value): (&T, bool),
|
||||
@@ -56,14 +56,18 @@ impl<T: TableContext> ArbitraryFrom<(&T, bool)> for CompoundPredicate {
|
||||
}
|
||||
|
||||
impl<T: TableContext> ArbitraryFrom<&T> for Predicate {
|
||||
fn arbitrary_from<R: Rng, C: GenerationContext>(rng: &mut R, context: &C, table: &T) -> Self {
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
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<T: TableContext> ArbitraryFrom<(&T, bool)> for Predicate {
|
||||
fn arbitrary_from<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
(table, predicate_value): (&T, bool),
|
||||
@@ -73,7 +77,7 @@ impl<T: TableContext> ArbitraryFrom<(&T, bool)> for Predicate {
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<(&str, &SimValue)> for Predicate {
|
||||
fn arbitrary_from<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
(column_name, value): (&str, &SimValue),
|
||||
@@ -83,7 +87,7 @@ impl ArbitraryFrom<(&str, &SimValue)> for Predicate {
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<(&Table, &Vec<SimValue>)> for Predicate {
|
||||
fn arbitrary_from<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
(t, row): (&Table, &Vec<SimValue>),
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::{
|
||||
pub struct TrueValue(pub SimValue);
|
||||
|
||||
impl ArbitraryFromMaybe<&SimValue> for TrueValue {
|
||||
fn arbitrary_from_maybe<R: rand::Rng, C: GenerationContext>(
|
||||
fn arbitrary_from_maybe<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
_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<R: rand::Rng, C: GenerationContext>(
|
||||
fn arbitrary_from_maybe<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: rand::Rng, C: GenerationContext>(
|
||||
fn arbitrary_from_maybe<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
_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<R: rand::Rng, C: GenerationContext>(
|
||||
fn arbitrary_from_maybe<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: rand::Rng, C: GenerationContext>(
|
||||
fn arbitrary_from_maybe<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
_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<R: rand::Rng, C: GenerationContext>(
|
||||
fn arbitrary_from_maybe<R: rand::Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: rand::Rng, C: GenerationContext, T: TableContext>(
|
||||
pub fn true_unary<R: rand::Rng + ?Sized, C: GenerationContext, T: TableContext>(
|
||||
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<R: rand::Rng, C: GenerationContext, T: TableContext>(
|
||||
pub fn false_unary<R: rand::Rng + ?Sized, C: GenerationContext, T: TableContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
_table: &T,
|
||||
|
||||
@@ -18,7 +18,7 @@ use turso_parser::ast::{Expr, SortOrder};
|
||||
use super::{backtrack, pick};
|
||||
|
||||
impl Arbitrary for Create {
|
||||
fn arbitrary<R: Rng, C: GenerationContext>(rng: &mut R, context: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, context: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, env: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_sized<R: Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
env: &C,
|
||||
num_result_columns: usize,
|
||||
@@ -179,7 +179,7 @@ impl ArbitrarySized for SelectInner {
|
||||
}
|
||||
|
||||
impl Arbitrary for Distinctness {
|
||||
fn arbitrary<R: Rng, C: GenerationContext>(rng: &mut R, _context: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, _context: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, env: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, env: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, env: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, env: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, env: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, env: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, env: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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);
|
||||
|
||||
@@ -10,14 +10,14 @@ use crate::model::table::{Column, ColumnType, Name, SimValue, Table};
|
||||
use super::ArbitraryFromMaybe;
|
||||
|
||||
impl Arbitrary for Name {
|
||||
fn arbitrary<R: Rng, C: GenerationContext>(rng: &mut R, _c: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(rng: &mut R, _c: &C) -> Self {
|
||||
let name = readable_name_custom("_", rng);
|
||||
Name(name.replace("-", "_"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Table {
|
||||
fn arbitrary<R: Rng, C: GenerationContext>(rng: &mut R, context: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, context: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(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<R: Rng, C: GenerationContext>(rng: &mut R, _context: &C) -> Self {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(rng: &mut R, _context: &C) -> Self {
|
||||
pick(&[Self::Integer, Self::Float, Self::Text, Self::Blob], rng).to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<&Table> for Vec<SimValue> {
|
||||
fn arbitrary_from<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
context: &C,
|
||||
table: &Table,
|
||||
@@ -79,7 +79,7 @@ impl ArbitraryFrom<&Table> for Vec<SimValue> {
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<&Vec<&SimValue>> for SimValue {
|
||||
fn arbitrary_from<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from<R: Rng + ?Sized, C: GenerationContext>(
|
||||
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<R: Rng, C: GenerationContext>(
|
||||
fn arbitrary_from_maybe<R: Rng + ?Sized, C: GenerationContext>(
|
||||
rng: &mut R,
|
||||
_context: &C,
|
||||
value: &SimValue,
|
||||
|
||||
Reference in New Issue
Block a user