mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-17 08:34:19 +01:00
- add Interaction Builder to simplify code for building an interaction.
Modify `generation/property.rs` to use the Builder - add additional metadata to `Interaction` to give more context for shrinking and iterating over interactions that originated from the same interaction. - add Iterator like utilities for `InteractionPlan` to facilitate iterating over interactions that came from the same property:
This commit is contained in:
32
Cargo.lock
generated
32
Cargo.lock
generated
@@ -1225,6 +1225,37 @@ dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
|
||||
dependencies = [
|
||||
"derive_builder_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.0.1"
|
||||
@@ -2617,6 +2648,7 @@ dependencies = [
|
||||
"bitmaps",
|
||||
"chrono",
|
||||
"clap",
|
||||
"derive_builder",
|
||||
"dirs 6.0.0",
|
||||
"either",
|
||||
"garde",
|
||||
|
||||
@@ -47,3 +47,4 @@ similar = { workspace = true }
|
||||
similar-asserts = { workspace = true }
|
||||
bitmaps = { workspace = true }
|
||||
bitflags.workspace = true
|
||||
derive_builder = "0.20.2"
|
||||
|
||||
@@ -181,7 +181,7 @@ impl<'a, R: rand::Rng> PlanGenerator<'a, R> {
|
||||
let remaining_ = Remaining::new(
|
||||
env.opts.max_interactions,
|
||||
&env.profile.query,
|
||||
&stats,
|
||||
stats,
|
||||
env.profile.experimental_mvcc,
|
||||
&conn_ctx,
|
||||
);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
//! we can generate queries that reference tables that do not exist. This is not a correctness issue, but more of
|
||||
//! an optimization issue that is good to point out for the future
|
||||
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
use rand::distr::{Distribution, weighted::WeightedIndex};
|
||||
use sql_generation::{
|
||||
generation::{Arbitrary, ArbitraryFrom, GenerationContext, pick, pick_index},
|
||||
@@ -26,10 +28,12 @@ use turso_parser::ast::{self, Distinctness};
|
||||
|
||||
use crate::{
|
||||
common::print_diff,
|
||||
generation::{Shadow as _, WeightedDistribution, query::QueryDistribution},
|
||||
generation::{Shadow, WeightedDistribution, query::QueryDistribution},
|
||||
model::{
|
||||
Query, QueryCapabilities, QueryDiscriminants, ResultSet,
|
||||
interactions::{Assertion, Interaction, InteractionType},
|
||||
interactions::{
|
||||
Assertion, Interaction, InteractionBuilder, InteractionType, PropertyMetadata, Span,
|
||||
},
|
||||
metrics::Remaining,
|
||||
property::{InteractiveQueryInfo, Property, PropertyDiscriminants},
|
||||
},
|
||||
@@ -231,7 +235,7 @@ impl Property {
|
||||
Property::SelectLimit { .. }
|
||||
| Property::SelectSelectOptimizer { .. }
|
||||
| Property::WhereTrueFalseNull { .. }
|
||||
| Property::UNIONAllPreservesCardinality { .. }
|
||||
| Property::UnionAllPreservesCardinality { .. }
|
||||
| Property::ReadYourUpdatesBack { .. }
|
||||
| Property::TableHasExpectedContent { .. }
|
||||
| Property::AllTableHaveExpectedContent { .. } => {
|
||||
@@ -243,8 +247,12 @@ impl Property {
|
||||
/// interactions construct a list of interactions, which is an executable representation of the property.
|
||||
/// the requirement of property -> vec<interaction> conversion emerges from the need to serialize the property,
|
||||
/// and `interaction` cannot be serialized directly.
|
||||
pub(crate) fn interactions(&self, connection_index: usize) -> Vec<Interaction> {
|
||||
match self {
|
||||
pub(crate) fn interactions(
|
||||
&self,
|
||||
connection_index: usize,
|
||||
id: NonZeroUsize,
|
||||
) -> Vec<Interaction> {
|
||||
let mut interactions: Vec<InteractionBuilder> = match self {
|
||||
Property::AllTableHaveExpectedContent { tables } => {
|
||||
assert_all_table_values(tables, connection_index).collect()
|
||||
}
|
||||
@@ -304,9 +312,9 @@ impl Property {
|
||||
));
|
||||
|
||||
vec![
|
||||
Interaction::new(connection_index, assumption),
|
||||
Interaction::new(connection_index, select_interaction),
|
||||
Interaction::new(connection_index, assertion),
|
||||
InteractionBuilder::with_interaction(assumption),
|
||||
InteractionBuilder::with_interaction(select_interaction),
|
||||
InteractionBuilder::with_interaction(assertion),
|
||||
]
|
||||
}
|
||||
Property::ReadYourUpdatesBack { update, select } => {
|
||||
@@ -368,10 +376,10 @@ impl Property {
|
||||
));
|
||||
|
||||
vec![
|
||||
Interaction::new(connection_index, assumption),
|
||||
Interaction::new(connection_index, update_interaction),
|
||||
Interaction::new(connection_index, select_interaction),
|
||||
Interaction::new(connection_index, assertion),
|
||||
InteractionBuilder::with_interaction(assumption),
|
||||
InteractionBuilder::with_interaction(update_interaction),
|
||||
InteractionBuilder::with_interaction(select_interaction),
|
||||
InteractionBuilder::with_interaction(assertion),
|
||||
]
|
||||
}
|
||||
Property::InsertValuesSelect {
|
||||
@@ -448,22 +456,20 @@ impl Property {
|
||||
));
|
||||
|
||||
let mut interactions = Vec::new();
|
||||
interactions.push(Interaction::new(connection_index, assumption));
|
||||
interactions.push(Interaction::new(
|
||||
connection_index,
|
||||
interactions.push(InteractionBuilder::with_interaction(assumption));
|
||||
interactions.push(InteractionBuilder::with_interaction(
|
||||
InteractionType::Query(Query::Insert(insert.clone())),
|
||||
));
|
||||
interactions.extend(
|
||||
queries
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|q| Interaction::new(connection_index, InteractionType::Query(q))),
|
||||
);
|
||||
interactions.push(Interaction::new(
|
||||
connection_index,
|
||||
interactions.extend(queries.clone().into_iter().map(|q| {
|
||||
let mut builder =
|
||||
InteractionBuilder::with_interaction(InteractionType::Query(q));
|
||||
builder.property_meta(PropertyMetadata::new(self, true));
|
||||
builder
|
||||
}));
|
||||
interactions.push(InteractionBuilder::with_interaction(
|
||||
InteractionType::Query(Query::Select(select.clone())),
|
||||
));
|
||||
interactions.push(Interaction::new(connection_index, assertion));
|
||||
interactions.push(InteractionBuilder::with_interaction(assertion));
|
||||
|
||||
interactions
|
||||
}
|
||||
@@ -505,16 +511,20 @@ impl Property {
|
||||
}) );
|
||||
|
||||
let mut interactions = Vec::new();
|
||||
interactions.push(Interaction::new(connection_index, assumption));
|
||||
interactions.push(Interaction::new(connection_index, cq1));
|
||||
interactions.extend(
|
||||
queries
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|q| Interaction::new(connection_index, InteractionType::Query(q))),
|
||||
);
|
||||
interactions.push(Interaction::new_ignore_error(connection_index, cq2));
|
||||
interactions.push(Interaction::new(connection_index, assertion));
|
||||
interactions.push(InteractionBuilder::with_interaction(assumption));
|
||||
interactions.push(InteractionBuilder::with_interaction(cq1));
|
||||
interactions.extend(queries.clone().into_iter().map(|q| {
|
||||
let mut builder =
|
||||
InteractionBuilder::with_interaction(InteractionType::Query(q));
|
||||
builder.property_meta(PropertyMetadata::new(self, true));
|
||||
builder
|
||||
}));
|
||||
interactions.push({
|
||||
let mut builder = InteractionBuilder::with_interaction(cq2);
|
||||
builder.ignore_error(true);
|
||||
builder
|
||||
});
|
||||
interactions.push(InteractionBuilder::with_interaction(assertion));
|
||||
|
||||
interactions
|
||||
}
|
||||
@@ -574,12 +584,11 @@ impl Property {
|
||||
));
|
||||
|
||||
vec![
|
||||
Interaction::new(connection_index, assumption),
|
||||
Interaction::new(
|
||||
connection_index,
|
||||
InteractionType::Query(Query::Select(select.clone())),
|
||||
),
|
||||
Interaction::new(connection_index, assertion),
|
||||
InteractionBuilder::with_interaction(assumption),
|
||||
InteractionBuilder::with_interaction(InteractionType::Query(Query::Select(
|
||||
select.clone(),
|
||||
))),
|
||||
InteractionBuilder::with_interaction(assertion),
|
||||
]
|
||||
}
|
||||
Property::DeleteSelect {
|
||||
@@ -643,16 +652,16 @@ impl Property {
|
||||
));
|
||||
|
||||
let mut interactions = Vec::new();
|
||||
interactions.push(Interaction::new(connection_index, assumption));
|
||||
interactions.push(Interaction::new(connection_index, delete));
|
||||
interactions.extend(
|
||||
queries
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|q| Interaction::new(connection_index, InteractionType::Query(q))),
|
||||
);
|
||||
interactions.push(Interaction::new(connection_index, select));
|
||||
interactions.push(Interaction::new(connection_index, assertion));
|
||||
interactions.push(InteractionBuilder::with_interaction(assumption));
|
||||
interactions.push(InteractionBuilder::with_interaction(delete));
|
||||
interactions.extend(queries.clone().into_iter().map(|q| {
|
||||
let mut builder =
|
||||
InteractionBuilder::with_interaction(InteractionType::Query(q));
|
||||
builder.property_meta(PropertyMetadata::new(self, true));
|
||||
builder
|
||||
}));
|
||||
interactions.push(InteractionBuilder::with_interaction(select));
|
||||
interactions.push(InteractionBuilder::with_interaction(assertion));
|
||||
|
||||
interactions
|
||||
}
|
||||
@@ -715,16 +724,20 @@ impl Property {
|
||||
|
||||
let mut interactions = Vec::new();
|
||||
|
||||
interactions.push(Interaction::new(connection_index, assumption));
|
||||
interactions.push(Interaction::new(connection_index, drop));
|
||||
interactions.extend(
|
||||
queries
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|q| Interaction::new(connection_index, InteractionType::Query(q))),
|
||||
);
|
||||
interactions.push(Interaction::new_ignore_error(connection_index, select));
|
||||
interactions.push(Interaction::new(connection_index, assertion));
|
||||
interactions.push(InteractionBuilder::with_interaction(assumption));
|
||||
interactions.push(InteractionBuilder::with_interaction(drop));
|
||||
interactions.extend(queries.clone().into_iter().map(|q| {
|
||||
let mut builder =
|
||||
InteractionBuilder::with_interaction(InteractionType::Query(q));
|
||||
builder.property_meta(PropertyMetadata::new(self, true));
|
||||
builder
|
||||
}));
|
||||
interactions.push({
|
||||
let mut builder = InteractionBuilder::with_interaction(select);
|
||||
builder.ignore_error(true);
|
||||
builder
|
||||
});
|
||||
interactions.push(InteractionBuilder::with_interaction(assertion));
|
||||
|
||||
interactions
|
||||
}
|
||||
@@ -811,15 +824,14 @@ impl Property {
|
||||
));
|
||||
|
||||
vec![
|
||||
Interaction::new(connection_index, assumption),
|
||||
Interaction::new(connection_index, select1),
|
||||
Interaction::new(connection_index, select2),
|
||||
Interaction::new(connection_index, assertion),
|
||||
InteractionBuilder::with_interaction(assumption),
|
||||
InteractionBuilder::with_interaction(select1),
|
||||
InteractionBuilder::with_interaction(select2),
|
||||
InteractionBuilder::with_interaction(assertion),
|
||||
]
|
||||
}
|
||||
Property::FsyncNoWait { query } => {
|
||||
vec![Interaction::new(
|
||||
connection_index,
|
||||
vec![InteractionBuilder::with_interaction(
|
||||
InteractionType::FsyncQuery(query.clone()),
|
||||
)]
|
||||
}
|
||||
@@ -855,7 +867,7 @@ impl Property {
|
||||
InteractionType::Assertion(assert),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|i| Interaction::new(connection_index, i))
|
||||
.map(InteractionBuilder::with_interaction)
|
||||
.collect()
|
||||
}
|
||||
Property::WhereTrueFalseNull { select, predicate } => {
|
||||
@@ -1010,13 +1022,13 @@ impl Property {
|
||||
));
|
||||
|
||||
vec![
|
||||
Interaction::new(connection_index, assumption),
|
||||
Interaction::new(connection_index, select),
|
||||
Interaction::new(connection_index, select_tlp),
|
||||
Interaction::new(connection_index, assertion),
|
||||
InteractionBuilder::with_interaction(assumption),
|
||||
InteractionBuilder::with_interaction(select),
|
||||
InteractionBuilder::with_interaction(select_tlp),
|
||||
InteractionBuilder::with_interaction(assertion),
|
||||
]
|
||||
}
|
||||
Property::UNIONAllPreservesCardinality {
|
||||
Property::UnionAllPreservesCardinality {
|
||||
select,
|
||||
where_clause,
|
||||
} => {
|
||||
@@ -1062,21 +1074,42 @@ impl Property {
|
||||
}
|
||||
},
|
||||
)),
|
||||
].into_iter().map(|i| Interaction::new(connection_index, i)).collect()
|
||||
].into_iter().map(InteractionBuilder::with_interaction).collect()
|
||||
}
|
||||
Property::Queries { queries } => queries
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|query| Interaction::new(connection_index, InteractionType::Query(query)))
|
||||
.map(|query| InteractionBuilder::with_interaction(InteractionType::Query(query)))
|
||||
.collect(),
|
||||
}
|
||||
};
|
||||
|
||||
assert!(!interactions.is_empty());
|
||||
|
||||
// Add a span to the interactions that matter
|
||||
if interactions.len() == 1 {
|
||||
interactions.first_mut().unwrap().span(Span::StartEnd);
|
||||
} else {
|
||||
interactions.first_mut().unwrap().span(Span::Start);
|
||||
interactions.last_mut().unwrap().span(Span::End);
|
||||
};
|
||||
|
||||
interactions
|
||||
.into_iter()
|
||||
.map(|mut builder| {
|
||||
if !builder.has_property_meta() {
|
||||
builder.property_meta(PropertyMetadata::new(self, false));
|
||||
}
|
||||
builder.connection_index(connection_index).id(id);
|
||||
builder.build().unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_all_table_values(
|
||||
tables: &[String],
|
||||
connection_index: usize,
|
||||
) -> impl Iterator<Item = Interaction> + use<'_> {
|
||||
) -> impl Iterator<Item = InteractionBuilder> + use<'_> {
|
||||
tables.iter().flat_map(move |table| {
|
||||
let select = InteractionType::Query(Query::Select(Select::simple(
|
||||
table.clone(),
|
||||
@@ -1131,7 +1164,7 @@ fn assert_all_table_values(
|
||||
}
|
||||
}
|
||||
}));
|
||||
[select, assertion].into_iter().map(move |i| Interaction::new(connection_index, i))
|
||||
[select, assertion].into_iter().map(InteractionBuilder::with_interaction)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1411,7 +1444,7 @@ fn property_union_all_preserves_cardinality<R: rand::Rng + ?Sized>(
|
||||
Distinctness::All,
|
||||
);
|
||||
|
||||
Property::UNIONAllPreservesCardinality {
|
||||
Property::UnionAllPreservesCardinality {
|
||||
select,
|
||||
where_clause: p2,
|
||||
}
|
||||
@@ -1460,7 +1493,7 @@ impl PropertyDiscriminants {
|
||||
PropertyDiscriminants::DropSelect => property_drop_select,
|
||||
PropertyDiscriminants::SelectSelectOptimizer => property_select_select_optimizer,
|
||||
PropertyDiscriminants::WhereTrueFalseNull => property_where_true_false_null,
|
||||
PropertyDiscriminants::UNIONAllPreservesCardinality => {
|
||||
PropertyDiscriminants::UnionAllPreservesCardinality => {
|
||||
property_union_all_preserves_cardinality
|
||||
}
|
||||
PropertyDiscriminants::FsyncNoWait => property_fsync_no_wait,
|
||||
@@ -1543,7 +1576,7 @@ impl PropertyDiscriminants {
|
||||
0
|
||||
}
|
||||
}
|
||||
PropertyDiscriminants::UNIONAllPreservesCardinality => {
|
||||
PropertyDiscriminants::UnionAllPreservesCardinality => {
|
||||
if opts.indexes
|
||||
&& !env.opts.disable_union_all_preserves_cardinality
|
||||
&& !ctx.tables().is_empty()
|
||||
@@ -1607,7 +1640,7 @@ impl PropertyDiscriminants {
|
||||
}
|
||||
PropertyDiscriminants::SelectSelectOptimizer => QueryCapabilities::SELECT,
|
||||
PropertyDiscriminants::WhereTrueFalseNull => QueryCapabilities::SELECT,
|
||||
PropertyDiscriminants::UNIONAllPreservesCardinality => QueryCapabilities::SELECT,
|
||||
PropertyDiscriminants::UnionAllPreservesCardinality => QueryCapabilities::SELECT,
|
||||
PropertyDiscriminants::FsyncNoWait => QueryCapabilities::all(),
|
||||
PropertyDiscriminants::FaultyQuery => QueryCapabilities::all(),
|
||||
PropertyDiscriminants::Queries => panic!("queries property should not be generated"),
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
use std::{
|
||||
fmt::{Debug, Display},
|
||||
ops::{Deref, DerefMut},
|
||||
marker::PhantomData,
|
||||
num::NonZeroUsize,
|
||||
ops::{Deref, DerefMut, Range},
|
||||
panic::RefUnwindSafe,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use indexmap::IndexSet;
|
||||
use either::Either;
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sql_generation::model::table::SimValue;
|
||||
use turso_core::{Connection, Result, StepResult};
|
||||
@@ -14,224 +18,262 @@ use crate::{
|
||||
generation::Shadow,
|
||||
model::{
|
||||
Query, ResultSet,
|
||||
metrics::InteractionStats,
|
||||
property::{Property, PropertyDiscriminants},
|
||||
},
|
||||
runner::env::{ShadowTablesMut, SimConnection, SimulationType, SimulatorEnv},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct InteractionPlan {
|
||||
plan: Vec<Interactions>,
|
||||
plan: Vec<Interaction>,
|
||||
stats: InteractionStats,
|
||||
// In the future, this should probably be a stack of interactions
|
||||
// so we can have nested properties
|
||||
last_interactions: Option<Interactions>,
|
||||
pub mvcc: bool,
|
||||
// Len should not count transactions statements, just so we can generate more meaningful interactions per run
|
||||
len: usize,
|
||||
|
||||
/// Counts [Interactions]. Should not count transactions statements, just so we can generate more meaningful interactions per run
|
||||
/// This field is only necessary and valid when generating interactions. For static iteration, we do not care about this field
|
||||
len_properties: usize,
|
||||
next_interaction_id: NonZeroUsize,
|
||||
}
|
||||
|
||||
impl InteractionPlan {
|
||||
pub(crate) fn new(mvcc: bool) -> Self {
|
||||
Self {
|
||||
plan: Vec::new(),
|
||||
stats: InteractionStats::default(),
|
||||
last_interactions: None,
|
||||
mvcc,
|
||||
len: 0,
|
||||
len_properties: 0,
|
||||
next_interaction_id: NonZeroUsize::new(1).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with(plan: Vec<Interactions>, mvcc: bool) -> Self {
|
||||
let len = plan
|
||||
.iter()
|
||||
.filter(|interaction| !interaction.ignore())
|
||||
.count();
|
||||
Self { plan, mvcc, len }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_len(&self) -> usize {
|
||||
self.plan
|
||||
.iter()
|
||||
.filter(|interaction| !interaction.ignore())
|
||||
.count()
|
||||
}
|
||||
|
||||
/// Length of interactions that are not transaction statements
|
||||
/// Count of interactions
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
self.plan.len()
|
||||
}
|
||||
|
||||
/// Count of properties
|
||||
#[inline]
|
||||
pub fn plan(&self) -> &[Interactions] {
|
||||
&self.plan
|
||||
pub fn len_properties(&self) -> usize {
|
||||
self.len_properties
|
||||
}
|
||||
|
||||
pub fn push(&mut self, interactions: Interactions) {
|
||||
pub fn next_property_id(&mut self) -> NonZeroUsize {
|
||||
let id = self.next_interaction_id;
|
||||
self.next_interaction_id = self
|
||||
.next_interaction_id
|
||||
.checked_add(1)
|
||||
.expect("Generated too many interactions, that overflowed ID generation");
|
||||
id
|
||||
}
|
||||
|
||||
pub fn last_interactions(&self) -> Option<&Interactions> {
|
||||
self.last_interactions.as_ref()
|
||||
}
|
||||
|
||||
pub fn push_interactions(&mut self, interactions: Interactions) {
|
||||
if !interactions.ignore() {
|
||||
self.len += 1;
|
||||
self.len_properties += 1;
|
||||
}
|
||||
self.plan.push(interactions);
|
||||
self.last_interactions = Some(interactions);
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, index: usize) -> Interactions {
|
||||
let interactions = self.plan.remove(index);
|
||||
if !interactions.ignore() {
|
||||
self.len -= 1;
|
||||
}
|
||||
interactions
|
||||
pub fn push(&mut self, interaction: Interaction) {
|
||||
self.plan.push(interaction);
|
||||
}
|
||||
|
||||
/// Finds the range of interactions that are contained between the start and end spans for a given ID.
|
||||
pub fn find_interactions_range(&self, id: NonZeroUsize) -> Range<usize> {
|
||||
let interactions = self.interactions_list();
|
||||
let idx = interactions
|
||||
.binary_search_by_key(&id, |interaction| interaction.id())
|
||||
.map_err(|_| format!("Interaction containing id `{id}` should be present"))
|
||||
.unwrap();
|
||||
let interaction = &interactions[idx];
|
||||
|
||||
let backward = || -> usize {
|
||||
interactions
|
||||
.iter()
|
||||
.rev()
|
||||
.skip(interactions.len() - idx)
|
||||
.position(|interaction| {
|
||||
interaction.id() == id
|
||||
&& interaction
|
||||
.span
|
||||
.is_some_and(|span| matches!(span, Span::Start))
|
||||
})
|
||||
.map(|idx| (interactions.len() - 1) - idx - 1)
|
||||
.expect("A start span should have been emitted")
|
||||
};
|
||||
|
||||
let forward = || -> usize {
|
||||
interactions
|
||||
.iter()
|
||||
.skip(idx + 1)
|
||||
.position(|interaction| interaction.id() != id)
|
||||
.map(|idx| idx - 1)
|
||||
.unwrap_or(interactions.len() - 1)
|
||||
// It can happen we do not have an end Span as we can fail in the middle of a property
|
||||
};
|
||||
|
||||
if let Some(span) = interaction.span {
|
||||
match span {
|
||||
Span::Start => {
|
||||
// go forward and find the end span
|
||||
let end_idx = forward();
|
||||
idx..end_idx + 1
|
||||
}
|
||||
Span::End => {
|
||||
// go backward and find the start span
|
||||
let start_idx = backward();
|
||||
start_idx..idx + 1
|
||||
}
|
||||
Span::StartEnd => idx..idx + 1,
|
||||
}
|
||||
} else {
|
||||
// go backward and find the start span
|
||||
let start_idx = backward();
|
||||
// go forward and find the end span
|
||||
let end_idx = forward();
|
||||
start_idx..end_idx + 1
|
||||
}
|
||||
}
|
||||
|
||||
/// Truncates up to a particular interaction
|
||||
pub fn truncate(&mut self, len: usize) {
|
||||
self.plan.truncate(len);
|
||||
self.len = self.new_len();
|
||||
}
|
||||
|
||||
pub fn retain_mut<F>(&mut self, mut f: F)
|
||||
/// Used to remove a particular [Interactions]
|
||||
pub fn remove_property(&mut self, id: NonZeroUsize) {
|
||||
let range = self.find_interactions_range(id);
|
||||
// Consume the drain iterator just to be sure
|
||||
for _interaction in self.plan.drain(range) {}
|
||||
}
|
||||
|
||||
pub fn retain_mut<F>(&mut self, f: F)
|
||||
where
|
||||
F: FnMut(&mut Interactions) -> bool,
|
||||
F: FnMut(&mut Interaction) -> bool,
|
||||
{
|
||||
let f = |t: &mut Interactions| {
|
||||
let ignore = t.ignore();
|
||||
let retain = f(t);
|
||||
// removed an interaction that was not previously ignored
|
||||
if !retain && !ignore {
|
||||
self.len -= 1;
|
||||
}
|
||||
retain
|
||||
};
|
||||
self.plan.retain_mut(f);
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn retain<F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(&Interactions) -> bool,
|
||||
{
|
||||
let f = |t: &Interactions| {
|
||||
let ignore = t.ignore();
|
||||
let retain = f(t);
|
||||
// removed an interaction that was not previously ignored
|
||||
if !retain && !ignore {
|
||||
self.len -= 1;
|
||||
}
|
||||
retain
|
||||
};
|
||||
self.plan.retain(f);
|
||||
self.len = self.new_len();
|
||||
#[inline]
|
||||
pub fn interactions_list(&self) -> &[Interaction] {
|
||||
&self.plan
|
||||
}
|
||||
|
||||
pub fn interactions_list(&self) -> Vec<Interaction> {
|
||||
self.plan
|
||||
.clone()
|
||||
.into_iter()
|
||||
.flat_map(|interactions| interactions.interactions().into_iter())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn interactions_list_with_secondary_index(&self) -> Vec<(usize, Interaction)> {
|
||||
self.plan
|
||||
.clone()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.flat_map(|(idx, interactions)| {
|
||||
interactions
|
||||
.interactions()
|
||||
.into_iter()
|
||||
.map(move |interaction| (idx, interaction))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn stats(&self) -> InteractionStats {
|
||||
let mut stats = InteractionStats::default();
|
||||
|
||||
fn query_stat(q: &Query, stats: &mut InteractionStats) {
|
||||
match q {
|
||||
Query::Select(_) => stats.select_count += 1,
|
||||
Query::Insert(_) => stats.insert_count += 1,
|
||||
Query::Delete(_) => stats.delete_count += 1,
|
||||
Query::Create(_) => stats.create_count += 1,
|
||||
Query::Drop(_) => stats.drop_count += 1,
|
||||
Query::Update(_) => stats.update_count += 1,
|
||||
Query::CreateIndex(_) => stats.create_index_count += 1,
|
||||
Query::Begin(_) => stats.begin_count += 1,
|
||||
Query::Commit(_) => stats.commit_count += 1,
|
||||
Query::Rollback(_) => stats.rollback_count += 1,
|
||||
Query::AlterTable(_) => stats.alter_table_count += 1,
|
||||
Query::DropIndex(_) => stats.drop_index_count += 1,
|
||||
Query::Placeholder => {}
|
||||
Query::Pragma(_) => stats.pragma_count += 1,
|
||||
}
|
||||
}
|
||||
for interactions in &self.plan {
|
||||
match &interactions.interactions {
|
||||
InteractionsType::Property(property) => {
|
||||
if matches!(property, Property::AllTableHaveExpectedContent { .. }) {
|
||||
// Skip Property::AllTableHaveExpectedContent when counting stats
|
||||
// this allows us to generate more relevant interactions as we count less Select's to the Stats
|
||||
continue;
|
||||
}
|
||||
for interaction in &property.interactions(interactions.connection_index) {
|
||||
if let InteractionType::Query(query) = &interaction.interaction {
|
||||
query_stat(query, &mut stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
InteractionsType::Query(query) => {
|
||||
query_stat(query, &mut stats);
|
||||
}
|
||||
InteractionsType::Fault(_) => {}
|
||||
}
|
||||
pub fn iter_properties(
|
||||
&self,
|
||||
) -> IterProperty<
|
||||
std::iter::Peekable<std::iter::Enumerate<std::slice::Iter<'_, Interaction>>>,
|
||||
Forward,
|
||||
> {
|
||||
IterProperty {
|
||||
iter: self.interactions_list().iter().enumerate().peekable(),
|
||||
_direction: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
stats
|
||||
pub fn rev_iter_properties(
|
||||
&self,
|
||||
) -> IterProperty<
|
||||
std::iter::Peekable<
|
||||
std::iter::Enumerate<std::iter::Rev<std::slice::Iter<'_, Interaction>>>,
|
||||
>,
|
||||
Backward,
|
||||
> {
|
||||
IterProperty {
|
||||
iter: self.interactions_list().iter().rev().enumerate().peekable(),
|
||||
_direction: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> &InteractionStats {
|
||||
&self.stats
|
||||
}
|
||||
|
||||
pub fn stats_mut(&mut self) -> &mut InteractionStats {
|
||||
&mut self.stats
|
||||
}
|
||||
|
||||
pub fn static_iterator(&self) -> impl InteractionPlanIterator {
|
||||
PlanIterator {
|
||||
iter: self.interactions_list().into_iter(),
|
||||
iter: self.interactions_list().to_vec().into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for InteractionPlan {
|
||||
type Target = Vec<Interactions>;
|
||||
pub struct Forward;
|
||||
pub struct Backward;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.plan
|
||||
pub struct IterProperty<I, Dir> {
|
||||
iter: I,
|
||||
_direction: PhantomData<Dir>,
|
||||
}
|
||||
|
||||
impl<'a, I> IterProperty<I, Forward>
|
||||
where
|
||||
I: Iterator<Item = (usize, &'a Interaction)> + itertools::PeekingNext + std::fmt::Debug,
|
||||
{
|
||||
pub fn next_property(&mut self) -> Option<impl Iterator<Item = (usize, &'a Interaction)>> {
|
||||
let (idx, interaction) = self.iter.next()?;
|
||||
let id = interaction.id();
|
||||
// get interactions from a particular property
|
||||
let span = interaction
|
||||
.span
|
||||
.expect("we should loop on interactions that have a span");
|
||||
|
||||
let first = std::iter::once((idx, interaction));
|
||||
|
||||
let property_interactions = match span {
|
||||
Span::Start => Either::Left(
|
||||
first.chain(
|
||||
self.iter
|
||||
.peeking_take_while(move |(_idx, interaction)| interaction.id() == id),
|
||||
),
|
||||
),
|
||||
Span::End => panic!("we should always be at the start of an interaction"),
|
||||
Span::StartEnd => Either::Right(first),
|
||||
};
|
||||
|
||||
Some(property_interactions)
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for InteractionPlan {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.plan
|
||||
}
|
||||
}
|
||||
impl<'a, I> IterProperty<I, Backward>
|
||||
where
|
||||
I: Iterator<Item = (usize, &'a Interaction)>
|
||||
+ DoubleEndedIterator
|
||||
+ itertools::PeekingNext
|
||||
+ std::fmt::Debug,
|
||||
{
|
||||
pub fn next_property(&mut self) -> Option<impl Iterator<Item = (usize, &'a Interaction)>> {
|
||||
let (idx, interaction) = self.iter.next()?;
|
||||
let id = interaction.id();
|
||||
// get interactions from a particular property
|
||||
let span = interaction
|
||||
.span
|
||||
.expect("we should loop on interactions that have a span");
|
||||
|
||||
impl IntoIterator for InteractionPlan {
|
||||
type Item = Interactions;
|
||||
let first = std::iter::once((idx, interaction));
|
||||
|
||||
type IntoIter = <Vec<Interactions> as IntoIterator>::IntoIter;
|
||||
let property_interactions = match span {
|
||||
Span::Start => panic!("we should always be at the end of an interaction"),
|
||||
Span::End => Either::Left(
|
||||
self.iter
|
||||
.peeking_take_while(move |(_idx, interaction)| interaction.id() == id)
|
||||
.chain(first),
|
||||
),
|
||||
Span::StartEnd => Either::Right(first),
|
||||
};
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.plan.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a InteractionPlan {
|
||||
type Item = &'a Interactions;
|
||||
|
||||
type IntoIter = <&'a Vec<Interactions> as IntoIterator>::IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.plan.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a mut InteractionPlan {
|
||||
type Item = &'a mut Interactions;
|
||||
|
||||
type IntoIter = <&'a mut Vec<Interactions> as IntoIterator>::IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.plan.iter_mut()
|
||||
Some(property_interactions.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,13 +326,6 @@ impl Interactions {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_extensional_queries(&mut self) -> Option<&mut Vec<Query>> {
|
||||
match &mut self.interactions {
|
||||
InteractionsType::Property(property) => property.get_extensional_queries(),
|
||||
InteractionsType::Query(..) | InteractionsType::Fault(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the interaction needs to check the database tables
|
||||
pub fn check_tables(&self) -> bool {
|
||||
match &self.interactions {
|
||||
@@ -341,75 +376,28 @@ impl InteractionsType {
|
||||
}
|
||||
}
|
||||
|
||||
impl Interactions {
|
||||
pub(crate) fn interactions(&self) -> Vec<Interaction> {
|
||||
match &self.interactions {
|
||||
InteractionsType::Property(property) => property.interactions(self.connection_index),
|
||||
InteractionsType::Query(query) => vec![Interaction::new(
|
||||
self.connection_index,
|
||||
InteractionType::Query(query.clone()),
|
||||
)],
|
||||
InteractionsType::Fault(fault) => vec![Interaction::new(
|
||||
self.connection_index,
|
||||
InteractionType::Fault(*fault),
|
||||
)],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn dependencies(&self) -> IndexSet<String> {
|
||||
match &self.interactions {
|
||||
InteractionsType::Property(property) => property
|
||||
.interactions(self.connection_index)
|
||||
.iter()
|
||||
.fold(IndexSet::new(), |mut acc, i| match &i.interaction {
|
||||
InteractionType::Query(q) => {
|
||||
acc.extend(q.dependencies());
|
||||
acc
|
||||
}
|
||||
_ => acc,
|
||||
}),
|
||||
InteractionsType::Query(query) => query.dependencies(),
|
||||
InteractionsType::Fault(_) => IndexSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn uses(&self) -> Vec<String> {
|
||||
match &self.interactions {
|
||||
InteractionsType::Property(property) => property
|
||||
.interactions(self.connection_index)
|
||||
.iter()
|
||||
.fold(vec![], |mut acc, i| match &i.interaction {
|
||||
InteractionType::Query(q) => {
|
||||
acc.extend(q.uses());
|
||||
acc
|
||||
}
|
||||
_ => acc,
|
||||
}),
|
||||
InteractionsType::Query(query) => query.uses(),
|
||||
InteractionsType::Fault(_) => vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: for the sql display come back and add connection index as a comment
|
||||
impl Display for InteractionPlan {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for interactions in &self.plan {
|
||||
match &interactions.interactions {
|
||||
InteractionsType::Property(property) => {
|
||||
let name = property.name();
|
||||
writeln!(f, "-- begin testing '{name}'")?;
|
||||
for interaction in property.interactions(interactions.connection_index) {
|
||||
writeln!(f, "\t{interaction}")?;
|
||||
}
|
||||
writeln!(f, "-- end testing '{name}'")?;
|
||||
}
|
||||
InteractionsType::Fault(fault) => {
|
||||
writeln!(f, "-- FAULT '{fault}'")?;
|
||||
}
|
||||
InteractionsType::Query(query) => {
|
||||
writeln!(f, "{query}; -- {}", interactions.connection_index)?;
|
||||
}
|
||||
const PAD: usize = 4;
|
||||
let mut indentation_level = 0;
|
||||
for interaction in &self.plan {
|
||||
if let Some(name) = interaction.property_meta.map(|p| p.property.name())
|
||||
&& interaction.span.is_some_and(|span| span.start())
|
||||
{
|
||||
indentation_level += 1;
|
||||
writeln!(f, "-- begin testing '{name}'")?;
|
||||
}
|
||||
|
||||
if indentation_level > 0 {
|
||||
let padding = " ".repeat(indentation_level * PAD);
|
||||
f.pad(&padding)?;
|
||||
}
|
||||
writeln!(f, "{interaction}")?;
|
||||
if let Some(name) = interaction.property_meta.map(|p| p.property.name())
|
||||
&& interaction.span.is_some_and(|span| span.end())
|
||||
{
|
||||
indentation_level -= 1;
|
||||
writeln!(f, "-- end testing '{name}'")?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,45 +405,8 @@ impl Display for InteractionPlan {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub(crate) struct InteractionStats {
|
||||
pub select_count: u32,
|
||||
pub insert_count: u32,
|
||||
pub delete_count: u32,
|
||||
pub update_count: u32,
|
||||
pub create_count: u32,
|
||||
pub create_index_count: u32,
|
||||
pub drop_count: u32,
|
||||
pub begin_count: u32,
|
||||
pub commit_count: u32,
|
||||
pub rollback_count: u32,
|
||||
pub alter_table_count: u32,
|
||||
pub drop_index_count: u32,
|
||||
pub pragma_count: u32,
|
||||
}
|
||||
|
||||
impl Display for InteractionStats {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Read: {}, Insert: {}, Delete: {}, Update: {}, Create: {}, CreateIndex: {}, Drop: {}, Begin: {}, Commit: {}, Rollback: {}, Alter Table: {}, Drop Index: {}",
|
||||
self.select_count,
|
||||
self.insert_count,
|
||||
self.delete_count,
|
||||
self.update_count,
|
||||
self.create_count,
|
||||
self.create_index_count,
|
||||
self.drop_count,
|
||||
self.begin_count,
|
||||
self.commit_count,
|
||||
self.rollback_count,
|
||||
self.alter_table_count,
|
||||
self.drop_index_count,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
type AssertionFunc = dyn Fn(&Vec<ResultSet>, &mut SimulatorEnv) -> Result<Result<(), String>>;
|
||||
type AssertionFunc =
|
||||
dyn Fn(&Vec<ResultSet>, &mut SimulatorEnv) -> Result<Result<(), String>> + RefUnwindSafe;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Assertion {
|
||||
@@ -474,7 +425,9 @@ impl Debug for Assertion {
|
||||
impl Assertion {
|
||||
pub fn new<F>(name: String, func: F) -> Self
|
||||
where
|
||||
F: Fn(&Vec<ResultSet>, &mut SimulatorEnv) -> Result<Result<(), String>> + 'static,
|
||||
F: Fn(&Vec<ResultSet>, &mut SimulatorEnv) -> Result<Result<(), String>>
|
||||
+ 'static
|
||||
+ RefUnwindSafe,
|
||||
{
|
||||
Self {
|
||||
func: Rc::new(func),
|
||||
@@ -532,11 +485,61 @@ impl PropertyMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, derive_builder::Builder)]
|
||||
#[builder(build_fn(validate = "Self::validate"))]
|
||||
pub struct Interaction {
|
||||
pub connection_index: usize,
|
||||
pub interaction: InteractionType,
|
||||
#[builder(default)]
|
||||
pub ignore_error: bool,
|
||||
#[builder(setter(strip_option), default)]
|
||||
pub property_meta: Option<PropertyMetadata>,
|
||||
#[builder(setter(strip_option), default)]
|
||||
pub span: Option<Span>,
|
||||
/// 0 id means the ID was not set
|
||||
id: NonZeroUsize,
|
||||
}
|
||||
|
||||
impl InteractionBuilder {
|
||||
pub fn from_interaction(interaction: &Interaction) -> Self {
|
||||
let mut builder = Self::default();
|
||||
builder
|
||||
.connection_index(interaction.connection_index)
|
||||
.id(interaction.id())
|
||||
.ignore_error(interaction.ignore_error)
|
||||
.interaction(interaction.interaction.clone());
|
||||
if let Some(property_meta) = interaction.property_meta {
|
||||
builder.property_meta(property_meta);
|
||||
}
|
||||
if let Some(span) = interaction.span {
|
||||
builder.span(span);
|
||||
}
|
||||
builder
|
||||
}
|
||||
|
||||
pub fn with_interaction(interaction: InteractionType) -> Self {
|
||||
let mut builder = Self::default();
|
||||
builder.interaction(interaction);
|
||||
builder
|
||||
}
|
||||
|
||||
/// Checks to see if the property metadata was already set
|
||||
pub fn has_property_meta(&self) -> bool {
|
||||
self.property_meta.is_some()
|
||||
}
|
||||
|
||||
fn validate(&self) -> Result<(), InteractionBuilderError> {
|
||||
// Cannot have span and property_meta.extension being true at the same time
|
||||
if let Some(property_meta) = self.property_meta.flatten()
|
||||
&& property_meta.extension
|
||||
&& self.span.flatten().is_some()
|
||||
{
|
||||
return Err(InteractionBuilderError::ValidationError(
|
||||
"cannot have a span set with an extension query".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Interaction {
|
||||
@@ -554,19 +557,16 @@ impl DerefMut for Interaction {
|
||||
}
|
||||
|
||||
impl Interaction {
|
||||
pub fn new(connection_index: usize, interaction: InteractionType) -> Self {
|
||||
Self {
|
||||
connection_index,
|
||||
interaction,
|
||||
ignore_error: false,
|
||||
}
|
||||
pub fn id(&self) -> NonZeroUsize {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn new_ignore_error(connection_index: usize, interaction: InteractionType) -> Self {
|
||||
Self {
|
||||
connection_index,
|
||||
interaction,
|
||||
ignore_error: true,
|
||||
pub fn uses(&self) -> Vec<String> {
|
||||
match &self.interaction {
|
||||
InteractionType::Query(query)
|
||||
| InteractionType::FsyncQuery(query)
|
||||
| InteractionType::FaultyQuery(query) => query.uses(),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@ use crate::model::Query;
|
||||
|
||||
/// Properties are representations of executable specifications
|
||||
/// about the database behavior.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, strum::EnumDiscriminants)]
|
||||
#[strum_discriminants(derive(strum::EnumIter))]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, strum::EnumDiscriminants, strum::IntoStaticStr)]
|
||||
#[strum_discriminants(derive(strum::EnumIter, strum::IntoStaticStr))]
|
||||
#[strum(serialize_all = "Train-Case")]
|
||||
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
|
||||
@@ -161,7 +162,7 @@ pub enum Property {
|
||||
/// should return the same number of rows as `SELECT <predicate> FROM <t> WHERE <predicate>`.
|
||||
/// > The property is succesfull when the UNION ALL of 2 select queries returns the same number of rows
|
||||
/// > as the sum of the two select queries.
|
||||
UNIONAllPreservesCardinality {
|
||||
UnionAllPreservesCardinality {
|
||||
select: Select,
|
||||
where_clause: Predicate,
|
||||
},
|
||||
@@ -192,25 +193,6 @@ pub struct InteractiveQueryInfo {
|
||||
}
|
||||
|
||||
impl Property {
|
||||
pub(crate) fn name(&self) -> &str {
|
||||
match self {
|
||||
Property::InsertValuesSelect { .. } => "Insert-Values-Select",
|
||||
Property::ReadYourUpdatesBack { .. } => "Read-Your-Updates-Back",
|
||||
Property::TableHasExpectedContent { .. } => "Table-Has-Expected-Content",
|
||||
Property::AllTableHaveExpectedContent { .. } => "All-Tables-Have-Expected-Content",
|
||||
Property::DoubleCreateFailure { .. } => "Double-Create-Failure",
|
||||
Property::SelectLimit { .. } => "Select-Limit",
|
||||
Property::DeleteSelect { .. } => "Delete-Select",
|
||||
Property::DropSelect { .. } => "Drop-Select",
|
||||
Property::SelectSelectOptimizer { .. } => "Select-Select-Optimizer",
|
||||
Property::WhereTrueFalseNull { .. } => "Where-True-False-Null",
|
||||
Property::FsyncNoWait { .. } => "FsyncNoWait",
|
||||
Property::FaultyQuery { .. } => "FaultyQuery",
|
||||
Property::UNIONAllPreservesCardinality { .. } => "UNION-All-Preserves-Cardinality",
|
||||
Property::Queries { .. } => "Queries",
|
||||
}
|
||||
}
|
||||
|
||||
/// Property Does some sort of fault injection
|
||||
pub fn check_tables(&self) -> bool {
|
||||
matches!(
|
||||
@@ -219,6 +201,17 @@ impl Property {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn has_extensional_queries(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Property::InsertValuesSelect { .. }
|
||||
| Property::DoubleCreateFailure { .. }
|
||||
| Property::DeleteSelect { .. }
|
||||
| Property::DropSelect { .. }
|
||||
| Property::Queries { .. }
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_extensional_queries(&mut self) -> Option<&mut Vec<Query>> {
|
||||
match self {
|
||||
Property::InsertValuesSelect { queries, .. }
|
||||
@@ -230,10 +223,23 @@ impl Property {
|
||||
Property::SelectLimit { .. }
|
||||
| Property::SelectSelectOptimizer { .. }
|
||||
| Property::WhereTrueFalseNull { .. }
|
||||
| Property::UNIONAllPreservesCardinality { .. }
|
||||
| Property::UnionAllPreservesCardinality { .. }
|
||||
| Property::ReadYourUpdatesBack { .. }
|
||||
| Property::TableHasExpectedContent { .. }
|
||||
| Property::AllTableHaveExpectedContent { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PropertyDiscriminants {
|
||||
pub fn name(&self) -> &'static str {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn check_tables(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::AllTableHaveExpectedContent | Self::TableHasExpectedContent
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ use crate::{
|
||||
model::{
|
||||
Query, ResultSet,
|
||||
interactions::{
|
||||
ConnectionState, Interaction, InteractionPlanIterator, InteractionPlanState,
|
||||
InteractionType,
|
||||
ConnectionState, Interaction, InteractionBuilder, InteractionPlanIterator,
|
||||
InteractionPlanState, InteractionType,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -199,10 +199,10 @@ pub fn execute_interaction_turso(
|
||||
|
||||
stack.push(results);
|
||||
|
||||
let query_interaction = Interaction::new(
|
||||
interaction.connection_index,
|
||||
InteractionType::Query(query.clone()),
|
||||
);
|
||||
let query_interaction = InteractionBuilder::from_interaction(interaction)
|
||||
.interaction(InteractionType::Query(query.clone()))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
execute_interaction(env, &query_interaction, stack)?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user