Skip to content

Commit 1344280

Browse files
committed
update properties to add extensional interactions between them
1 parent 82fcc27 commit 1344280

File tree

5 files changed

+189
-25
lines changed

5 files changed

+189
-25
lines changed

simulator/generation/property.rs

Lines changed: 105 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
model::{
3-
query::{Create, Insert, Predicate, Query, Select},
3+
query::{Create, Delete, Insert, Predicate, Query, Select},
44
table::Value,
55
},
66
runner::env::SimulatorEnv,
@@ -35,7 +35,7 @@ pub(crate) enum Property {
3535
/// The insert query
3636
insert: Insert,
3737
/// Additional interactions in the middle of the property
38-
interactions: Vec<Query>,
38+
queries: Vec<Query>,
3939
/// The select query
4040
select: Select,
4141
},
@@ -55,7 +55,7 @@ pub(crate) enum Property {
5555
/// The create query
5656
create: Create,
5757
/// Additional interactions in the middle of the property
58-
interactions: Vec<Query>,
58+
queries: Vec<Query>,
5959
},
6060
}
6161

@@ -70,7 +70,7 @@ impl Property {
7070
match self {
7171
Property::InsertSelect {
7272
insert,
73-
interactions: _, // todo: add extensional interactions
73+
queries,
7474
select,
7575
} => {
7676
// Check that the row is there
@@ -106,21 +106,34 @@ impl Property {
106106
}),
107107
});
108108

109-
vec![
110-
assumption,
111-
Interaction::Query(Query::Insert(insert.clone())),
112-
Interaction::Query(Query::Select(select.clone())),
113-
assertion,
114-
]
109+
let mut interactions = Vec::new();
110+
interactions.push(assumption);
111+
interactions.push(Interaction::Query(Query::Insert(insert.clone())));
112+
interactions.extend(queries.clone().into_iter().map(Interaction::Query));
113+
interactions.push(Interaction::Query(Query::Select(select.clone())));
114+
interactions.push(assertion);
115+
116+
interactions
115117
}
116118
Property::DoubleCreateFailure {
117119
create,
118-
interactions: _, // todo: add extensional interactions
120+
queries,
119121
} => {
120122
let table_name = create.table.name.clone();
123+
124+
let assumption = Interaction::Assumption(Assertion {
125+
message: "Double-Create-Failure should not be called on an existing table"
126+
.to_string(),
127+
func: Box::new(move |_: &Vec<ResultSet>, env: &SimulatorEnv| {
128+
!env.tables.iter().any(|t| t.name == table_name)
129+
}),
130+
});
131+
121132
let cq1 = Interaction::Query(Query::Create(create.clone()));
122133
let cq2 = Interaction::Query(Query::Create(create.clone()));
123134

135+
let table_name = create.table.name.clone();
136+
124137
let assertion = Interaction::Assertion(Assertion {
125138
message:
126139
"creating two tables with the name should result in a failure for the second query"
@@ -136,13 +149,26 @@ impl Property {
136149
}),
137150
});
138151

139-
vec![cq1, cq2, assertion]
152+
let mut interactions = Vec::new();
153+
interactions.push(assumption);
154+
interactions.push(cq1);
155+
interactions.extend(queries.clone().into_iter().map(Interaction::Query));
156+
interactions.push(cq2);
157+
interactions.push(assertion);
158+
159+
interactions
140160
}
141161
}
142162
}
143163
}
144164

145-
fn remaining(env: &SimulatorEnv, stats: &InteractionStats) -> (f64, f64, f64) {
165+
pub(crate) struct Remaining {
166+
pub(crate) read: f64,
167+
pub(crate) write: f64,
168+
pub(crate) create: f64,
169+
}
170+
171+
fn remaining(env: &SimulatorEnv, stats: &InteractionStats) -> Remaining {
146172
let remaining_read = ((env.opts.max_interactions as f64 * env.opts.read_percent / 100.0)
147173
- (stats.read_count as f64))
148174
.max(0.0);
@@ -153,10 +179,14 @@ fn remaining(env: &SimulatorEnv, stats: &InteractionStats) -> (f64, f64, f64) {
153179
- (stats.create_count as f64))
154180
.max(0.0);
155181

156-
(remaining_read, remaining_write, remaining_create)
182+
Remaining {
183+
read: remaining_read,
184+
write: remaining_write,
185+
create: remaining_create,
186+
}
157187
}
158188

159-
fn property_insert_select<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Property {
189+
fn property_insert_select<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv, remaining: &Remaining) -> Property {
160190
// Get a random table
161191
let table = pick(&env.tables, rng);
162192
// Pick a random column
@@ -181,6 +211,36 @@ fn property_insert_select<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Prop
181211
values: vec![row.clone()],
182212
};
183213

214+
// Create random queries respecting the constraints
215+
let mut queries = Vec::new();
216+
// - [x] There will be no errors in the middle interactions. (this constraint is impossible to check, so this is just best effort)
217+
// - [x] The inserted row will not be deleted.
218+
// - [ ] The inserted row will not be updated. (todo: add this constraint once UPDATE is implemented)
219+
// - [ ] The table `t` will not be renamed, dropped, or altered. (todo: add this constraint once ALTER or DROP is implemented)
220+
for _ in 0..rng.gen_range(0..3) {
221+
let query = Query::arbitrary_from(rng, &(table, remaining));
222+
match &query {
223+
Query::Delete(Delete {
224+
table: t,
225+
predicate,
226+
}) => {
227+
// The inserted row will not be deleted.
228+
if t == &table.name && predicate.test(&row, &table) {
229+
continue;
230+
}
231+
}
232+
Query::Create(Create { table: t }) => {
233+
// There will be no errors in the middle interactions.
234+
// - Creating the same table is an error
235+
if t.name == table.name {
236+
continue;
237+
}
238+
}
239+
_ => (),
240+
}
241+
queries.push(query);
242+
}
243+
184244
// Select the row
185245
let select_query = Select {
186246
table: table.name.clone(),
@@ -189,40 +249,62 @@ fn property_insert_select<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Prop
189249

190250
Property::InsertSelect {
191251
insert: insert_query,
192-
interactions: Vec::new(),
252+
queries,
193253
select: select_query,
194254
}
195255
}
196256

197-
fn property_double_create_failure<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Property {
257+
fn property_double_create_failure<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv, remaining: &Remaining) -> Property {
198258
// Get a random table
199259
let table = pick(&env.tables, rng);
200260
// Create the table
201261
let create_query = Create {
202262
table: table.clone(),
203263
};
204264

265+
// Create random queries respecting the constraints
266+
let mut queries = Vec::new();
267+
// The interactions in the middle has the following constraints;
268+
// - [x] There will be no errors in the middle interactions.(best effort)
269+
// - [ ] Table `t` will not be renamed or dropped.(todo: add this constraint once ALTER or DROP is implemented)
270+
for _ in 0..rng.gen_range(0..3) {
271+
let query = Query::arbitrary_from(rng, &(table, remaining));
272+
match &query {
273+
Query::Create(Create { table: t }) => {
274+
// There will be no errors in the middle interactions.
275+
// - Creating the same table is an error
276+
if t.name == table.name {
277+
continue;
278+
}
279+
}
280+
_ => (),
281+
}
282+
queries.push(query);
283+
}
284+
205285
Property::DoubleCreateFailure {
206286
create: create_query,
207-
interactions: Vec::new(),
287+
queries,
208288
}
209289
}
210290

291+
292+
211293
impl ArbitraryFrom<(&SimulatorEnv, &InteractionStats)> for Property {
212294
fn arbitrary_from<R: rand::Rng>(
213295
rng: &mut R,
214296
(env, stats): &(&SimulatorEnv, &InteractionStats),
215297
) -> Self {
216-
let (remaining_read, remaining_write, remaining_create) = remaining(env, stats);
298+
let remaining_ = remaining(env, stats);
217299
frequency(
218300
vec![
219301
(
220-
f64::min(remaining_read, remaining_write),
221-
Box::new(|rng: &mut R| property_insert_select(rng, env)),
302+
f64::min(remaining_.read, remaining_.write),
303+
Box::new(|rng: &mut R| property_insert_select(rng, env, &remaining_)),
222304
),
223305
(
224-
remaining_create / 2.0,
225-
Box::new(|rng: &mut R| property_double_create_failure(rng, env)),
306+
remaining_.create / 2.0,
307+
Box::new(|rng: &mut R| property_double_create_failure(rng, env, &remaining_)),
226308
),
227309
],
228310
rng,

simulator/generation/query.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::model::table::{Table, Value};
66
use rand::seq::SliceRandom as _;
77
use rand::Rng;
88

9+
use super::property::Remaining;
910
use super::{frequency, pick};
1011

1112
impl Arbitrary for Create {
@@ -87,6 +88,32 @@ impl ArbitraryFrom<Table> for Query {
8788
}
8889
}
8990

91+
impl ArbitraryFrom<(&Table, &Remaining)> for Query {
92+
fn arbitrary_from<R: Rng>(rng: &mut R, (table, remaining): &(&Table, &Remaining)) -> Self {
93+
frequency(
94+
vec![
95+
(
96+
remaining.create,
97+
Box::new(|rng| Self::Create(Create::arbitrary(rng))),
98+
),
99+
(
100+
remaining.read,
101+
Box::new(|rng| Self::Select(Select::arbitrary_from(rng, &vec![*table]))),
102+
),
103+
(
104+
remaining.write,
105+
Box::new(|rng| Self::Insert(Insert::arbitrary_from(rng, table))),
106+
),
107+
(
108+
0.0,
109+
Box::new(|rng| Self::Delete(Delete::arbitrary_from(rng, table))),
110+
),
111+
],
112+
rng,
113+
)
114+
}
115+
}
116+
90117
struct CompoundPredicate(Predicate);
91118
struct SimplePredicate(Predicate);
92119

@@ -322,7 +349,6 @@ impl ArbitraryFrom<(&Table, &Vec<Value>)> for Predicate {
322349

323350
// Start building a top level predicate from a true predicate
324351
let mut result = true_predicates.pop().unwrap();
325-
println!("True predicate: {:?}", result);
326352

327353
let mut predicates = true_predicates
328354
.iter()

simulator/model/query.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,26 @@ impl Predicate {
2020
pub(crate) fn false_() -> Self {
2121
Self::Or(vec![])
2222
}
23+
24+
pub(crate) fn test(&self, row: &[Value], table: &Table) -> bool {
25+
let get_value = |name: &str| {
26+
table
27+
.columns
28+
.iter()
29+
.zip(row.iter())
30+
.find(|(column, _)| column.name == name)
31+
.map(|(_, value)| value)
32+
};
33+
34+
match self {
35+
Predicate::And(vec) => vec.iter().all(|p| p.test(row, table)),
36+
Predicate::Or(vec) => vec.iter().any(|p| p.test(row, table)),
37+
Predicate::Eq(column, value) => get_value(column) == Some(value),
38+
Predicate::Neq(column, value) => get_value(column) != Some(value),
39+
Predicate::Gt(column, value) => get_value(column).map(|v| v > value).unwrap_or(false),
40+
Predicate::Lt(column, value) => get_value(column).map(|v| v < value).unwrap_or(false),
41+
}
42+
}
2343
}
2444

2545
impl Display for Predicate {

simulator/model/table.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,22 @@ pub(crate) enum Value {
5353
Blob(Vec<u8>),
5454
}
5555

56+
impl PartialOrd for Value {
57+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
58+
match (self, other) {
59+
(Self::Null, Self::Null) => Some(std::cmp::Ordering::Equal),
60+
(Self::Null, _) => Some(std::cmp::Ordering::Less),
61+
(_, Self::Null) => Some(std::cmp::Ordering::Greater),
62+
(Self::Integer(i1), Self::Integer(i2)) => i1.partial_cmp(i2),
63+
(Self::Float(f1), Self::Float(f2)) => f1.partial_cmp(f2),
64+
(Self::Text(t1), Self::Text(t2)) => t1.partial_cmp(t2),
65+
(Self::Blob(b1), Self::Blob(b2)) => b1.partial_cmp(b2),
66+
// todo: add type coercions here
67+
_ => None,
68+
}
69+
}
70+
}
71+
5672
fn to_sqlite_blob(bytes: &[u8]) -> String {
5773
format!(
5874
"X'{}'",

simulator/shrink/plan.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::{generation::plan::InteractionPlan, runner::execution::Execution};
1+
use crate::{
2+
generation::plan::{Interaction, InteractionPlan, Interactions},
3+
runner::execution::Execution,
4+
};
25

36
impl InteractionPlan {
47
/// Create a smaller interaction plan by deleting a property
@@ -19,6 +22,23 @@ impl InteractionPlan {
1922
plan.plan
2023
.retain(|p| p.uses().iter().any(|t| depending_tables.contains(t)));
2124

25+
// Remove the extensional parts of the properties
26+
for interaction in plan.plan.iter_mut() {
27+
if let Interactions::Property(p) = interaction {
28+
match p {
29+
crate::generation::property::Property::InsertSelect {
30+
queries,
31+
..
32+
} |
33+
crate::generation::property::Property::DoubleCreateFailure {
34+
queries,
35+
..
36+
} => {
37+
queries.clear();
38+
}
39+
}
40+
}
41+
}
2242
let after = plan.plan.len();
2343

2444
log::info!(

0 commit comments

Comments
 (0)