mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-17 08:34:19 +01:00
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:
246 lines
9.7 KiB
Rust
246 lines
9.7 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use sql_generation::model::query::{Create, Insert, Select, predicate::Predicate, update::Update};
|
|
|
|
use crate::model::Query;
|
|
|
|
/// Properties are representations of executable specifications
|
|
/// about the database behavior.
|
|
#[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
|
|
/// where clause that matches the inserted row.
|
|
/// The execution of the property is as follows
|
|
/// INSERT INTO <t> VALUES (...)
|
|
/// I_0
|
|
/// I_1
|
|
/// ...
|
|
/// I_n
|
|
/// SELECT * FROM <t> WHERE <predicate>
|
|
/// The interactions in the middle has the following constraints;
|
|
/// - There will be no errors in the middle interactions.
|
|
/// - The inserted row will not be deleted.
|
|
/// - The inserted row will not be updated.
|
|
/// - The table `t` will not be renamed, dropped, or altered.
|
|
InsertValuesSelect {
|
|
/// The insert query
|
|
insert: Insert,
|
|
/// Selected row index
|
|
row_index: usize,
|
|
/// Additional interactions in the middle of the property
|
|
queries: Vec<Query>,
|
|
/// The select query
|
|
select: Select,
|
|
/// Interactive query information if any
|
|
interactive: Option<InteractiveQueryInfo>,
|
|
},
|
|
/// ReadYourUpdatesBack is a property in which the updated rows
|
|
/// must be in the resulting rows of a select query that has a
|
|
/// where clause that matches the updated row.
|
|
/// The execution of the property is as follows
|
|
/// UPDATE <t> SET <set_cols=set_vals> WHERE <predicate>
|
|
/// SELECT <set_cols> FROM <t> WHERE <predicate>
|
|
/// These interactions are executed in immediate succession
|
|
/// just to verify the property that our updates did what they
|
|
/// were supposed to do.
|
|
ReadYourUpdatesBack {
|
|
update: Update,
|
|
select: Select,
|
|
},
|
|
/// TableHasExpectedContent is a property in which the table
|
|
/// must have the expected content, i.e. all the insertions and
|
|
/// updates and deletions should have been persisted in the way
|
|
/// we think they were.
|
|
/// The execution of the property is as follows
|
|
/// SELECT * FROM <t>
|
|
/// ASSERT <expected_content>
|
|
TableHasExpectedContent {
|
|
table: String,
|
|
},
|
|
/// AllTablesHaveExpectedContent is a property in which the table
|
|
/// must have the expected content, i.e. all the insertions and
|
|
/// updates and deletions should have been persisted in the way
|
|
/// we think they were.
|
|
/// The execution of the property is as follows
|
|
/// SELECT * FROM <t>
|
|
/// ASSERT <expected_content>
|
|
/// for each table in the simulator model
|
|
AllTableHaveExpectedContent {
|
|
tables: Vec<String>,
|
|
},
|
|
/// Double Create Failure is a property in which creating
|
|
/// the same table twice leads to an error.
|
|
/// The execution of the property is as follows
|
|
/// CREATE TABLE <t> (...)
|
|
/// I_0
|
|
/// I_1
|
|
/// ...
|
|
/// I_n
|
|
/// CREATE TABLE <t> (...) -> Error
|
|
/// The interactions in the middle has the following constraints;
|
|
/// - There will be no errors in the middle interactions.
|
|
/// - Table `t` will not be renamed or dropped.
|
|
DoubleCreateFailure {
|
|
/// The create query
|
|
create: Create,
|
|
/// Additional interactions in the middle of the property
|
|
queries: Vec<Query>,
|
|
},
|
|
/// Select Limit is a property in which the select query
|
|
/// has a limit clause that is respected by the query.
|
|
/// The execution of the property is as follows
|
|
/// SELECT * FROM <t> WHERE <predicate> LIMIT <n>
|
|
/// This property is a single-interaction property.
|
|
/// The interaction has the following constraints;
|
|
/// - The select query will respect the limit clause.
|
|
SelectLimit {
|
|
/// The select query
|
|
select: Select,
|
|
},
|
|
/// Delete-Select is a property in which the deleted row
|
|
/// must not be in the resulting rows of a select query that has a
|
|
/// where clause that matches the deleted row. In practice, `p1` of
|
|
/// the delete query will be used as the predicate for the select query,
|
|
/// hence the select should return NO ROWS.
|
|
/// The execution of the property is as follows
|
|
/// DELETE FROM <t> WHERE <predicate>
|
|
/// I_0
|
|
/// I_1
|
|
/// ...
|
|
/// I_n
|
|
/// SELECT * FROM <t> WHERE <predicate>
|
|
/// The interactions in the middle has the following constraints;
|
|
/// - There will be no errors in the middle interactions.
|
|
/// - A row that holds for the predicate will not be inserted.
|
|
/// - The table `t` will not be renamed, dropped, or altered.
|
|
DeleteSelect {
|
|
table: String,
|
|
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,
|
|
},
|
|
/// Select-Select-Optimizer is a property in which we test the optimizer by
|
|
/// running two equivalent select queries, one with `SELECT <predicate> from <t>`
|
|
/// and the other with `SELECT * from <t> WHERE <predicate>`. As highlighted by
|
|
/// Rigger et al. in Non-Optimizing Reference Engine Construction(NoREC), SQLite
|
|
/// tends to optimize `where` statements while keeping the result column expressions
|
|
/// unoptimized. This property is used to test the optimizer. The property is successful
|
|
/// if the two queries return the same number of rows.
|
|
SelectSelectOptimizer {
|
|
table: String,
|
|
predicate: Predicate,
|
|
},
|
|
/// Where-True-False-Null is a property that tests the boolean logic implementation
|
|
/// in the database. It relies on the fact that `P == true || P == false || P == null` should return true,
|
|
/// as SQLite uses a ternary logic system. This property is invented in "Finding Bugs in Database Systems via Query Partitioning"
|
|
/// by Rigger et al. and it is canonically called Ternary Logic Partitioning (TLP).
|
|
WhereTrueFalseNull {
|
|
select: Select,
|
|
predicate: Predicate,
|
|
},
|
|
/// UNION-ALL-Preserves-Cardinality is a property that tests the UNION ALL operator
|
|
/// implementation in the database. It relies on the fact that `SELECT * FROM <t
|
|
/// > WHERE <predicate> UNION ALL SELECT * FROM <t> WHERE <predicate>`
|
|
/// 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 {
|
|
select: Select,
|
|
where_clause: Predicate,
|
|
},
|
|
/// FsyncNoWait is a property which tests if we do not loose any data after not waiting for fsync.
|
|
///
|
|
/// # Interactions
|
|
/// - Executes the `query` without waiting for fsync
|
|
/// - Drop all connections and Reopen the database
|
|
/// - Execute the `query` again
|
|
/// - Query tables to assert that the values were inserted
|
|
///
|
|
FsyncNoWait {
|
|
query: Query,
|
|
},
|
|
FaultyQuery {
|
|
query: Query,
|
|
},
|
|
/// Property used to subsititute a property with its queries only
|
|
Queries {
|
|
queries: Vec<Query>,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct InteractiveQueryInfo {
|
|
pub start_with_immediate: bool,
|
|
pub end_with_commit: bool,
|
|
}
|
|
|
|
impl Property {
|
|
/// Property Does some sort of fault injection
|
|
pub fn check_tables(&self) -> bool {
|
|
matches!(
|
|
self,
|
|
Property::FsyncNoWait { .. } | Property::FaultyQuery { .. }
|
|
)
|
|
}
|
|
|
|
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, .. }
|
|
| Property::DoubleCreateFailure { queries, .. }
|
|
| Property::DeleteSelect { queries, .. }
|
|
| Property::DropSelect { queries, .. }
|
|
| Property::Queries { queries } => Some(queries),
|
|
Property::FsyncNoWait { .. } | Property::FaultyQuery { .. } => None,
|
|
Property::SelectLimit { .. }
|
|
| Property::SelectSelectOptimizer { .. }
|
|
| Property::WhereTrueFalseNull { .. }
|
|
| 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
|
|
)
|
|
}
|
|
}
|