Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

simulator: add DROP TABLE <t> support #949

Merged
merged 2 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 56 additions & 28 deletions simulator/generation/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
)
}
}
Expand Down Expand Up @@ -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(_) => {}
Expand All @@ -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 {
Expand All @@ -374,6 +383,7 @@ impl InteractionPlan {
Query::Insert(_) => write += 1,
Query::Delete(_) => delete += 1,
Query::Create(_) => create += 1,
Query::Drop(_) => drop += 1,
}
}
}
Expand All @@ -383,6 +393,7 @@ impl InteractionPlan {
Query::Insert(_) => write += 1,
Query::Delete(_) => delete += 1,
Query::Create(_) => create += 1,
Query::Drop(_) => drop += 1,
},
Interactions::Fault(_) => {}
}
Expand All @@ -393,6 +404,7 @@ impl InteractionPlan {
write_count: write,
delete_count: delete,
create_count: create,
drop_count: drop,
}
}
}
Expand Down Expand Up @@ -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)))
}

Expand All @@ -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 {
Expand Down Expand Up @@ -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_
Expand Down
128 changes: 127 additions & 1 deletion simulator/generation/property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand All @@ -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.
Expand Down Expand Up @@ -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
}
}
Expand All @@ -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 {
Expand All @@ -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,
}
}

Expand Down Expand Up @@ -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,
Expand All @@ -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,
)
Expand Down
12 changes: 11 additions & 1 deletion simulator/generation/query.rs
Original file line number Diff line number Diff line change
@@ -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 _;
Expand Down Expand Up @@ -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(
Expand Down
Loading
Loading