mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-23 08:55:40 +01:00
Merge 'simulator: add DROP TABLE <t> support' from Alperen Keleş
I wanted to assist the current development in an up-to-date fashion, this PR adds drop table(which is being implemented currently in https://github.com/tursodatabase/limbo/pull/897) testing support to the generator. Unfortunately, we don't have feature flags in the simulator yet, so the users should manually fix the generation probability in `simulator/generation/plan.rs#L644` and `simulator/generation/property.rs#L629`. Closes #949
This commit is contained in:
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
model::{
|
||||
query::{Create, Delete, Distinctness, Insert, Query, Select},
|
||||
query::{select::Distinctness, Create, Delete, Drop, Insert, Query, Select},
|
||||
table::Value,
|
||||
},
|
||||
runner::env::SimConnection,
|
||||
@@ -201,14 +201,19 @@ pub(crate) struct InteractionStats {
|
||||
pub(crate) write_count: usize,
|
||||
pub(crate) delete_count: usize,
|
||||
pub(crate) create_count: usize,
|
||||
pub(crate) drop_count: usize,
|
||||
}
|
||||
|
||||
impl Display for InteractionStats {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Read: {}, Write: {}, Delete: {}, Create: {}",
|
||||
self.read_count, self.write_count, self.delete_count, self.create_count
|
||||
"Read: {}, Write: {}, Delete: {}, Create: {}, Drop: {}",
|
||||
self.read_count,
|
||||
self.write_count,
|
||||
self.delete_count,
|
||||
self.create_count,
|
||||
self.drop_count
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -307,37 +312,40 @@ impl Interactions {
|
||||
}
|
||||
select.shadow(env);
|
||||
}
|
||||
Property::DropSelect {
|
||||
table,
|
||||
queries,
|
||||
select,
|
||||
} => {
|
||||
let drop = Query::Drop(Drop {
|
||||
table: table.clone(),
|
||||
});
|
||||
|
||||
drop.shadow(env);
|
||||
for query in queries {
|
||||
query.shadow(env);
|
||||
}
|
||||
select.shadow(env);
|
||||
}
|
||||
}
|
||||
for interaction in property.interactions() {
|
||||
match interaction {
|
||||
Interaction::Query(query) => match query {
|
||||
Query::Create(create) => {
|
||||
if !env.tables.iter().any(|t| t.name == create.table.name) {
|
||||
env.tables.push(create.table.clone());
|
||||
}
|
||||
create.shadow(env);
|
||||
}
|
||||
Query::Insert(insert) => {
|
||||
let values = match &insert {
|
||||
Insert::Values { values, .. } => values.clone(),
|
||||
Insert::Select { select, .. } => select.shadow(env),
|
||||
};
|
||||
let table = env
|
||||
.tables
|
||||
.iter_mut()
|
||||
.find(|t| t.name == insert.table())
|
||||
.unwrap();
|
||||
table.rows.extend(values);
|
||||
insert.shadow(env);
|
||||
}
|
||||
Query::Delete(delete) => {
|
||||
let table = env
|
||||
.tables
|
||||
.iter_mut()
|
||||
.find(|t| t.name == delete.table)
|
||||
.unwrap();
|
||||
let t2 = &table.clone();
|
||||
table.rows.retain_mut(|r| delete.predicate.test(r, t2));
|
||||
delete.shadow(env);
|
||||
}
|
||||
Query::Drop(drop) => {
|
||||
drop.shadow(env);
|
||||
}
|
||||
Query::Select(select) => {
|
||||
select.shadow(env);
|
||||
}
|
||||
Query::Select(_) => {}
|
||||
},
|
||||
Interaction::Assertion(_) => {}
|
||||
Interaction::Assumption(_) => {}
|
||||
@@ -363,6 +371,7 @@ impl InteractionPlan {
|
||||
let mut write = 0;
|
||||
let mut delete = 0;
|
||||
let mut create = 0;
|
||||
let mut drop = 0;
|
||||
|
||||
for interactions in &self.plan {
|
||||
match interactions {
|
||||
@@ -374,6 +383,7 @@ impl InteractionPlan {
|
||||
Query::Insert(_) => write += 1,
|
||||
Query::Delete(_) => delete += 1,
|
||||
Query::Create(_) => create += 1,
|
||||
Query::Drop(_) => drop += 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -383,6 +393,7 @@ impl InteractionPlan {
|
||||
Query::Insert(_) => write += 1,
|
||||
Query::Delete(_) => delete += 1,
|
||||
Query::Create(_) => create += 1,
|
||||
Query::Drop(_) => drop += 1,
|
||||
},
|
||||
Interactions::Fault(_) => {}
|
||||
}
|
||||
@@ -393,6 +404,7 @@ impl InteractionPlan {
|
||||
write_count: write,
|
||||
delete_count: delete,
|
||||
create_count: create,
|
||||
drop_count: drop,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -579,7 +591,7 @@ impl Interaction {
|
||||
}
|
||||
}
|
||||
|
||||
fn create_table<R: rand::Rng>(rng: &mut R, _env: &SimulatorEnv) -> Interactions {
|
||||
fn random_create<R: rand::Rng>(rng: &mut R, _env: &SimulatorEnv) -> Interactions {
|
||||
Interactions::Query(Query::Create(Create::arbitrary(rng)))
|
||||
}
|
||||
|
||||
@@ -588,8 +600,15 @@ fn random_read<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Interactions {
|
||||
}
|
||||
|
||||
fn random_write<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Interactions {
|
||||
let insert_query = Query::Insert(Insert::arbitrary_from(rng, env));
|
||||
Interactions::Query(insert_query)
|
||||
Interactions::Query(Query::Insert(Insert::arbitrary_from(rng, env)))
|
||||
}
|
||||
|
||||
fn random_delete<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Interactions {
|
||||
Interactions::Query(Query::Delete(Delete::arbitrary_from(rng, env)))
|
||||
}
|
||||
|
||||
fn random_drop<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Interactions {
|
||||
Interactions::Query(Query::Drop(Drop::arbitrary_from(rng, env)))
|
||||
}
|
||||
|
||||
fn random_fault<R: rand::Rng>(_rng: &mut R, _env: &SimulatorEnv) -> Interactions {
|
||||
@@ -620,7 +639,16 @@ impl ArbitraryFrom<(&SimulatorEnv, InteractionStats)> for Interactions {
|
||||
),
|
||||
(
|
||||
remaining_.create,
|
||||
Box::new(|rng: &mut R| create_table(rng, env)),
|
||||
Box::new(|rng: &mut R| random_create(rng, env)),
|
||||
),
|
||||
(
|
||||
remaining_.delete,
|
||||
Box::new(|rng: &mut R| random_delete(rng, env)),
|
||||
),
|
||||
(
|
||||
// remaining_.drop,
|
||||
0.0,
|
||||
Box::new(|rng: &mut R| random_drop(rng, env)),
|
||||
),
|
||||
(
|
||||
remaining_
|
||||
|
||||
@@ -3,7 +3,10 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
model::{
|
||||
query::{Create, Delete, Distinctness, Insert, Predicate, Query, Select},
|
||||
query::{
|
||||
select::{Distinctness, Predicate},
|
||||
Create, Delete, Drop, Insert, Query, Select,
|
||||
},
|
||||
table::Value,
|
||||
},
|
||||
runner::env::SimulatorEnv,
|
||||
@@ -94,6 +97,23 @@ pub(crate) enum Property {
|
||||
predicate: Predicate,
|
||||
queries: Vec<Query>,
|
||||
},
|
||||
// Drop-Select is a property in which selecting from a dropped table
|
||||
// should result in an error.
|
||||
// The execution of the property is as follows
|
||||
// DROP TABLE <t>
|
||||
// I_0
|
||||
// I_1
|
||||
// ...
|
||||
// I_n
|
||||
// SELECT * FROM <t> WHERE <predicate> -> Error
|
||||
// The interactions in the middle has the following constraints;
|
||||
// - There will be no errors in the middle interactions.
|
||||
// - The table `t` will not be created, no table will be renamed to `t`.
|
||||
DropSelect {
|
||||
table: String,
|
||||
queries: Vec<Query>,
|
||||
select: Select,
|
||||
},
|
||||
}
|
||||
|
||||
impl Property {
|
||||
@@ -103,6 +123,7 @@ impl Property {
|
||||
Property::DoubleCreateFailure { .. } => "Double-Create-Failure".to_string(),
|
||||
Property::SelectLimit { .. } => "Select-Limit".to_string(),
|
||||
Property::DeleteSelect { .. } => "Delete-Select".to_string(),
|
||||
Property::DropSelect { .. } => "Drop-Select".to_string(),
|
||||
}
|
||||
}
|
||||
/// interactions construct a list of interactions, which is an executable representation of the property.
|
||||
@@ -287,6 +308,55 @@ impl Property {
|
||||
interactions.push(select);
|
||||
interactions.push(assertion);
|
||||
|
||||
interactions
|
||||
}
|
||||
Property::DropSelect {
|
||||
table,
|
||||
queries,
|
||||
select,
|
||||
} => {
|
||||
let assumption = Interaction::Assumption(Assertion {
|
||||
message: format!("table {} exists", table),
|
||||
func: Box::new({
|
||||
let table = table.clone();
|
||||
move |_: &Vec<ResultSet>, env: &SimulatorEnv| {
|
||||
Ok(env.tables.iter().any(|t| t.name == table))
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
let table_name = table.clone();
|
||||
|
||||
let assertion = Interaction::Assertion(Assertion {
|
||||
message: format!(
|
||||
"select query should result in an error for table '{}'",
|
||||
table
|
||||
),
|
||||
func: Box::new(move |stack: &Vec<ResultSet>, _: &SimulatorEnv| {
|
||||
let last = stack.last().unwrap();
|
||||
match last {
|
||||
Ok(_) => Ok(false),
|
||||
Err(e) => Ok(e
|
||||
.to_string()
|
||||
.contains(&format!("Table {table_name} does not exist"))),
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
let drop = Interaction::Query(Query::Drop(Drop {
|
||||
table: table.clone(),
|
||||
}));
|
||||
|
||||
let select = Interaction::Query(Query::Select(select.clone()));
|
||||
|
||||
let mut interactions = Vec::new();
|
||||
|
||||
interactions.push(assumption);
|
||||
interactions.push(drop);
|
||||
interactions.extend(queries.clone().into_iter().map(Interaction::Query));
|
||||
interactions.push(select);
|
||||
interactions.push(assertion);
|
||||
|
||||
interactions
|
||||
}
|
||||
}
|
||||
@@ -298,6 +368,8 @@ pub(crate) struct Remaining {
|
||||
pub(crate) read: f64,
|
||||
pub(crate) write: f64,
|
||||
pub(crate) create: f64,
|
||||
pub(crate) delete: f64,
|
||||
pub(crate) drop: f64,
|
||||
}
|
||||
|
||||
pub(crate) fn remaining(env: &SimulatorEnv, stats: &InteractionStats) -> Remaining {
|
||||
@@ -310,11 +382,19 @@ pub(crate) fn remaining(env: &SimulatorEnv, stats: &InteractionStats) -> Remaini
|
||||
let remaining_create = ((env.opts.max_interactions as f64 * env.opts.create_percent / 100.0)
|
||||
- (stats.create_count as f64))
|
||||
.max(0.0);
|
||||
let remaining_delete = ((env.opts.max_interactions as f64 * env.opts.delete_percent / 100.0)
|
||||
- (stats.delete_count as f64))
|
||||
.max(0.0);
|
||||
let remaining_drop = ((env.opts.max_interactions as f64 * env.opts.drop_percent / 100.0)
|
||||
- (stats.drop_count as f64))
|
||||
.max(0.0);
|
||||
|
||||
Remaining {
|
||||
read: remaining_read,
|
||||
write: remaining_write,
|
||||
create: remaining_create,
|
||||
delete: remaining_delete,
|
||||
drop: remaining_drop,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,6 +559,47 @@ fn property_delete_select<R: rand::Rng>(
|
||||
queries,
|
||||
}
|
||||
}
|
||||
|
||||
fn property_drop_select<R: rand::Rng>(
|
||||
rng: &mut R,
|
||||
env: &SimulatorEnv,
|
||||
remaining: &Remaining,
|
||||
) -> Property {
|
||||
// Get a random table
|
||||
let table = pick(&env.tables, rng);
|
||||
|
||||
// Create random queries respecting the constraints
|
||||
let mut queries = Vec::new();
|
||||
// - [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.gen_range(0..3) {
|
||||
let query = Query::arbitrary_from(rng, (env, remaining));
|
||||
match &query {
|
||||
Query::Create(Create { table: t }) => {
|
||||
// - The table `t` will not be created
|
||||
if t.name == table.name {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
queries.push(query);
|
||||
}
|
||||
|
||||
let select = Select {
|
||||
table: table.name.clone(),
|
||||
predicate: Predicate::arbitrary_from(rng, table),
|
||||
limit: None,
|
||||
distinct: Distinctness::All,
|
||||
};
|
||||
|
||||
Property::DropSelect {
|
||||
table: table.name.clone(),
|
||||
queries,
|
||||
select,
|
||||
}
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<(&SimulatorEnv, &InteractionStats)> for Property {
|
||||
fn arbitrary_from<R: rand::Rng>(
|
||||
rng: &mut R,
|
||||
@@ -503,6 +624,11 @@ impl ArbitraryFrom<(&SimulatorEnv, &InteractionStats)> for Property {
|
||||
f64::min(remaining_.read, remaining_.write),
|
||||
Box::new(|rng: &mut R| property_delete_select(rng, env, &remaining_)),
|
||||
),
|
||||
(
|
||||
// remaining_.drop,
|
||||
0.0,
|
||||
Box::new(|rng: &mut R| property_drop_select(rng, env, &remaining_)),
|
||||
),
|
||||
],
|
||||
rng,
|
||||
)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::generation::table::{GTValue, LTValue};
|
||||
use crate::generation::{one_of, Arbitrary, ArbitraryFrom};
|
||||
|
||||
use crate::model::query::{Create, Delete, Distinctness, Insert, Predicate, Query, Select};
|
||||
use crate::model::query::select::{Distinctness, Predicate};
|
||||
use crate::model::query::{Create, Delete, Drop, Insert, Query, Select};
|
||||
use crate::model::table::{Table, Value};
|
||||
use crate::SimulatorEnv;
|
||||
use rand::seq::SliceRandom as _;
|
||||
@@ -96,6 +97,15 @@ impl ArbitraryFrom<&SimulatorEnv> for Delete {
|
||||
}
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<&SimulatorEnv> for Drop {
|
||||
fn arbitrary_from<R: Rng>(rng: &mut R, env: &SimulatorEnv) -> Self {
|
||||
let table = pick(&env.tables, rng);
|
||||
Self {
|
||||
table: table.name.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<(&SimulatorEnv, &Remaining)> for Query {
|
||||
fn arbitrary_from<R: Rng>(rng: &mut R, (env, remaining): (&SimulatorEnv, &Remaining)) -> Self {
|
||||
frequency(
|
||||
|
||||
38
simulator/model/query/create.rs
Normal file
38
simulator/model/query/create.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
model::table::{Table, Value},
|
||||
SimulatorEnv,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) struct Create {
|
||||
pub(crate) table: Table,
|
||||
}
|
||||
|
||||
impl Create {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
if !env.tables.iter().any(|t| t.name == self.table.name) {
|
||||
env.tables.push(self.table.clone());
|
||||
}
|
||||
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Create {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "CREATE TABLE {} (", self.table.name)?;
|
||||
|
||||
for (i, column) in self.table.columns.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ",")?;
|
||||
}
|
||||
write!(f, "{} {}", column.name, column.column_type)?;
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
35
simulator/model/query/delete.rs
Normal file
35
simulator/model/query/delete.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{model::table::Value, SimulatorEnv};
|
||||
|
||||
use super::select::Predicate;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) struct Delete {
|
||||
pub(crate) table: String,
|
||||
pub(crate) predicate: Predicate,
|
||||
}
|
||||
|
||||
impl Delete {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
let table = env
|
||||
.tables
|
||||
.iter_mut()
|
||||
.find(|t| t.name == self.table)
|
||||
.unwrap();
|
||||
|
||||
let t2 = table.clone();
|
||||
|
||||
table.rows.retain_mut(|r| self.predicate.test(r, &t2));
|
||||
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Delete {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "DELETE FROM {} WHERE {}", self.table, self.predicate)
|
||||
}
|
||||
}
|
||||
23
simulator/model/query/drop.rs
Normal file
23
simulator/model/query/drop.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{model::table::Value, SimulatorEnv};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) struct Drop {
|
||||
pub(crate) table: String,
|
||||
}
|
||||
|
||||
impl Drop {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
env.tables.retain(|t| t.name != self.table);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Drop {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "DROP TABLE {}", self.table)
|
||||
}
|
||||
}
|
||||
73
simulator/model/query/insert.rs
Normal file
73
simulator/model/query/insert.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{model::table::Value, SimulatorEnv};
|
||||
|
||||
use super::select::Select;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) enum Insert {
|
||||
Values {
|
||||
table: String,
|
||||
values: Vec<Vec<Value>>,
|
||||
},
|
||||
Select {
|
||||
table: String,
|
||||
select: Box<Select>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Insert {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
match self {
|
||||
Insert::Values { table, values } => {
|
||||
if let Some(t) = env.tables.iter_mut().find(|t| &t.name == table) {
|
||||
t.rows.extend(values.clone());
|
||||
}
|
||||
}
|
||||
Insert::Select { table, select } => {
|
||||
let rows = select.shadow(env);
|
||||
if let Some(t) = env.tables.iter_mut().find(|t| &t.name == table) {
|
||||
t.rows.extend(rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub(crate) fn table(&self) -> &str {
|
||||
match self {
|
||||
Insert::Values { table, .. } | Insert::Select { table, .. } => table,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Insert {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Insert::Values { table, values } => {
|
||||
write!(f, "INSERT INTO {} VALUES ", table)?;
|
||||
for (i, row) in values.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "(")?;
|
||||
for (j, value) in row.iter().enumerate() {
|
||||
if j != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", value)?;
|
||||
}
|
||||
write!(f, ")")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Insert::Select { table, select } => {
|
||||
write!(f, "INSERT INTO {} ", table)?;
|
||||
write!(f, "{}", select)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
simulator/model/query/mod.rs
Normal file
71
simulator/model/query/mod.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
pub(crate) use create::Create;
|
||||
pub(crate) use delete::Delete;
|
||||
pub(crate) use drop::Drop;
|
||||
pub(crate) use insert::Insert;
|
||||
pub(crate) use select::Select;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{model::table::Value, runner::env::SimulatorEnv};
|
||||
|
||||
pub mod create;
|
||||
pub mod delete;
|
||||
pub mod drop;
|
||||
pub mod insert;
|
||||
pub mod select;
|
||||
|
||||
// This type represents the potential queries on the database.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) enum Query {
|
||||
Create(Create),
|
||||
Select(Select),
|
||||
Insert(Insert),
|
||||
Delete(Delete),
|
||||
Drop(Drop),
|
||||
}
|
||||
|
||||
impl Query {
|
||||
pub(crate) fn dependencies(&self) -> Vec<String> {
|
||||
match self {
|
||||
Query::Create(_) => vec![],
|
||||
Query::Select(Select { table, .. })
|
||||
| Query::Insert(Insert::Select { table, .. })
|
||||
| Query::Insert(Insert::Values { table, .. })
|
||||
| Query::Delete(Delete { table, .. })
|
||||
| Query::Drop(Drop { table, .. }) => vec![table.clone()],
|
||||
}
|
||||
}
|
||||
pub(crate) fn uses(&self) -> Vec<String> {
|
||||
match self {
|
||||
Query::Create(Create { table }) => vec![table.name.clone()],
|
||||
Query::Select(Select { table, .. })
|
||||
| Query::Insert(Insert::Select { table, .. })
|
||||
| Query::Insert(Insert::Values { table, .. })
|
||||
| Query::Delete(Delete { table, .. })
|
||||
| Query::Drop(Drop { table, .. }) => vec![table.clone()],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
match self {
|
||||
Query::Create(create) => create.shadow(env),
|
||||
Query::Insert(insert) => insert.shadow(env),
|
||||
Query::Delete(delete) => delete.shadow(env),
|
||||
Query::Select(select) => select.shadow(env),
|
||||
Query::Drop(drop) => drop.shadow(env),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Query {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Create(create) => write!(f, "{}", create),
|
||||
Self::Select(select) => write!(f, "{}", select),
|
||||
Self::Insert(insert) => write!(f, "{}", insert),
|
||||
Self::Delete(delete) => write!(f, "{}", delete),
|
||||
Self::Drop(drop) => write!(f, "{}", drop),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,55 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
model::table::{Table, Value},
|
||||
runner::env::SimulatorEnv,
|
||||
SimulatorEnv,
|
||||
};
|
||||
|
||||
/// `SELECT` distinctness
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Distinctness {
|
||||
/// `DISTINCT`
|
||||
Distinct,
|
||||
/// `ALL`
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) struct Select {
|
||||
pub(crate) table: String,
|
||||
pub(crate) predicate: Predicate,
|
||||
pub(crate) distinct: Distinctness,
|
||||
pub(crate) limit: Option<usize>,
|
||||
}
|
||||
|
||||
impl Select {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
let table = env.tables.iter().find(|t| t.name == self.table.as_str());
|
||||
if let Some(table) = table {
|
||||
table
|
||||
.rows
|
||||
.iter()
|
||||
.filter(|row| self.predicate.test(row, table))
|
||||
.cloned()
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Select {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"SELECT * FROM {} WHERE {}{}",
|
||||
self.table,
|
||||
self.predicate,
|
||||
self.limit
|
||||
.map_or("".to_string(), |l| format!(" LIMIT {}", l))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) enum Predicate {
|
||||
And(Vec<Predicate>), // p1 AND p2 AND p3... AND pn
|
||||
@@ -127,201 +173,3 @@ impl Display for Predicate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This type represents the potential queries on the database.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) enum Query {
|
||||
Create(Create),
|
||||
Select(Select),
|
||||
Insert(Insert),
|
||||
Delete(Delete),
|
||||
}
|
||||
|
||||
impl Query {
|
||||
pub(crate) fn dependencies(&self) -> Vec<String> {
|
||||
match self {
|
||||
Query::Create(_) => vec![],
|
||||
Query::Select(Select { table, .. })
|
||||
| Query::Insert(Insert::Select { table, .. })
|
||||
| Query::Insert(Insert::Values { table, .. })
|
||||
| Query::Delete(Delete { table, .. }) => vec![table.clone()],
|
||||
}
|
||||
}
|
||||
pub(crate) fn uses(&self) -> Vec<String> {
|
||||
match self {
|
||||
Query::Create(Create { table }) => vec![table.name.clone()],
|
||||
Query::Select(Select { table, .. })
|
||||
| Query::Insert(Insert::Select { table, .. })
|
||||
| Query::Insert(Insert::Values { table, .. })
|
||||
| Query::Delete(Delete { table, .. }) => vec![table.clone()],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
match self {
|
||||
Query::Create(create) => create.shadow(env),
|
||||
Query::Insert(insert) => insert.shadow(env),
|
||||
Query::Delete(delete) => delete.shadow(env),
|
||||
Query::Select(select) => select.shadow(env),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) struct Create {
|
||||
pub(crate) table: Table,
|
||||
}
|
||||
|
||||
impl Create {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
if !env.tables.iter().any(|t| t.name == self.table.name) {
|
||||
env.tables.push(self.table.clone());
|
||||
}
|
||||
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Create {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "CREATE TABLE {} (", self.table.name)?;
|
||||
|
||||
for (i, column) in self.table.columns.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ",")?;
|
||||
}
|
||||
write!(f, "{} {}", column.name, column.column_type)?;
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
/// `SELECT` distinctness
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Distinctness {
|
||||
/// `DISTINCT`
|
||||
Distinct,
|
||||
/// `ALL`
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) struct Select {
|
||||
pub(crate) table: String,
|
||||
pub(crate) predicate: Predicate,
|
||||
pub(crate) distinct: Distinctness,
|
||||
pub(crate) limit: Option<usize>,
|
||||
}
|
||||
|
||||
impl Select {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
let table = env.tables.iter().find(|t| t.name == self.table.as_str());
|
||||
if let Some(table) = table {
|
||||
table
|
||||
.rows
|
||||
.iter()
|
||||
.filter(|row| self.predicate.test(row, table))
|
||||
.cloned()
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Select {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"SELECT * FROM {} WHERE {}{}",
|
||||
self.table,
|
||||
self.predicate,
|
||||
self.limit
|
||||
.map_or("".to_string(), |l| format!(" LIMIT {}", l))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) enum Insert {
|
||||
Values {
|
||||
table: String,
|
||||
values: Vec<Vec<Value>>,
|
||||
},
|
||||
Select {
|
||||
table: String,
|
||||
select: Box<Select>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Insert {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
match self {
|
||||
Insert::Values { table, values } => {
|
||||
if let Some(t) = env.tables.iter_mut().find(|t| &t.name == table) {
|
||||
t.rows.extend(values.clone());
|
||||
}
|
||||
}
|
||||
Insert::Select { table, select } => {
|
||||
let rows = select.shadow(env);
|
||||
if let Some(t) = env.tables.iter_mut().find(|t| &t.name == table) {
|
||||
t.rows.extend(rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub(crate) fn table(&self) -> &str {
|
||||
match self {
|
||||
Insert::Values { table, .. } | Insert::Select { table, .. } => table,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) struct Delete {
|
||||
pub(crate) table: String,
|
||||
pub(crate) predicate: Predicate,
|
||||
}
|
||||
|
||||
impl Delete {
|
||||
pub(crate) fn shadow(&self, _env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Query {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Create(create) => write!(f, "{}", create),
|
||||
Self::Select(select) => write!(f, "{}", select),
|
||||
Self::Insert(Insert::Values { table, values }) => {
|
||||
write!(f, "INSERT INTO {} VALUES ", table)?;
|
||||
for (i, row) in values.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "(")?;
|
||||
for (j, value) in row.iter().enumerate() {
|
||||
if j != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", value)?;
|
||||
}
|
||||
write!(f, ")")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Self::Insert(Insert::Select { table, select }) => {
|
||||
write!(f, "INSERT INTO {} ", table)?;
|
||||
write!(f, "{}", select)
|
||||
}
|
||||
Self::Delete(Delete {
|
||||
table,
|
||||
predicate: guard,
|
||||
}) => write!(f, "DELETE FROM {} WHERE {}", table, guard),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,18 +26,28 @@ impl SimulatorEnv {
|
||||
pub(crate) fn new(seed: u64, cli_opts: &SimulatorCLI, db_path: &Path) -> Self {
|
||||
let mut rng = ChaCha8Rng::seed_from_u64(seed);
|
||||
|
||||
let (create_percent, read_percent, write_percent, delete_percent) = {
|
||||
let mut remaining = 100.0;
|
||||
let read_percent = rng.gen_range(0.0..=remaining);
|
||||
remaining -= read_percent;
|
||||
let write_percent = rng.gen_range(0.0..=remaining);
|
||||
remaining -= write_percent;
|
||||
let delete_percent = remaining;
|
||||
let (create_percent, read_percent, write_percent, delete_percent, drop_percent) = {
|
||||
let total = 100.0;
|
||||
|
||||
let create_percent = write_percent / 10.0;
|
||||
let write_percent = write_percent - create_percent;
|
||||
let read_percent = rng.gen_range(0.0..=total);
|
||||
let write_percent = total - read_percent;
|
||||
|
||||
(create_percent, read_percent, write_percent, delete_percent)
|
||||
// Create percent should be 5-15% of the write percent
|
||||
let create_percent = rng.gen_range(0.05..=0.15) * write_percent;
|
||||
// Drop percent should be 2-5% of the write percent
|
||||
let drop_percent = rng.gen_range(0.02..=0.05) * write_percent;
|
||||
// Delete percent should be 10-20% of the write percent
|
||||
let delete_percent = rng.gen_range(0.1..=0.2) * write_percent;
|
||||
|
||||
let write_percent = write_percent - create_percent - delete_percent - drop_percent;
|
||||
|
||||
(
|
||||
create_percent,
|
||||
read_percent,
|
||||
write_percent,
|
||||
delete_percent,
|
||||
drop_percent,
|
||||
)
|
||||
};
|
||||
|
||||
let opts = SimulatorOpts {
|
||||
@@ -49,6 +59,7 @@ impl SimulatorEnv {
|
||||
read_percent,
|
||||
write_percent,
|
||||
delete_percent,
|
||||
drop_percent,
|
||||
page_size: 4096, // TODO: randomize this too
|
||||
max_interactions: rng.gen_range(cli_opts.minimum_size..=cli_opts.maximum_size),
|
||||
max_time_simulation: cli_opts.maximum_time,
|
||||
@@ -98,6 +109,7 @@ pub(crate) struct SimulatorOpts {
|
||||
pub(crate) read_percent: f64,
|
||||
pub(crate) write_percent: f64,
|
||||
pub(crate) delete_percent: f64,
|
||||
pub(crate) drop_percent: f64,
|
||||
pub(crate) max_interactions: usize,
|
||||
pub(crate) page_size: usize,
|
||||
pub(crate) max_time_simulation: usize,
|
||||
|
||||
@@ -32,7 +32,8 @@ impl InteractionPlan {
|
||||
match p {
|
||||
Property::InsertValuesSelect { queries, .. }
|
||||
| Property::DoubleCreateFailure { queries, .. }
|
||||
| Property::DeleteSelect { queries, .. } => {
|
||||
| Property::DeleteSelect { queries, .. }
|
||||
| Property::DropSelect { queries, .. } => {
|
||||
queries.clear();
|
||||
}
|
||||
Property::SelectLimit { .. } => {}
|
||||
|
||||
Reference in New Issue
Block a user