diff --git a/simulator/generation/mod.rs b/simulator/generation/mod.rs index 140075d32..d2ccf4a00 100644 --- a/simulator/generation/mod.rs +++ b/simulator/generation/mod.rs @@ -1,6 +1,6 @@ use sql_generation::generation::GenerationContext; -use crate::runner::env::SimulatorTables; +use crate::runner::env::ShadowTablesMut; pub mod plan; pub mod property; @@ -17,7 +17,7 @@ pub mod query; /// might return a vector of rows that were inserted into the table. pub(crate) trait Shadow { type Result; - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result; + fn shadow(&self, tables: &mut ShadowTablesMut<'_>) -> Self::Result; } /// Generation context that will always panic when called diff --git a/simulator/generation/plan.rs b/simulator/generation/plan.rs index 9641b5d7c..8b8f97aa8 100644 --- a/simulator/generation/plan.rs +++ b/simulator/generation/plan.rs @@ -27,7 +27,7 @@ use crate::{ SimulatorEnv, generation::{PanicGenerationContext, Shadow}, model::Query, - runner::env::{SimConnection, SimulationType, SimulatorTables}, + runner::env::{ShadowTablesMut, SimConnection, SimulationType}, }; use super::property::{Property, remaining}; @@ -228,7 +228,7 @@ impl InteractionPlan { tracing::debug!("Generating interaction {}/{}", plan.len(), num_interactions); let interactions = Interactions::arbitrary_from(rng, &PanicGenerationContext, (env, plan.stats())); - interactions.shadow(env.get_conn_tables_mut(interactions.connection_index)); + interactions.shadow(&mut env.get_conn_tables_mut(interactions.connection_index)); plan.push(interactions); } @@ -311,7 +311,7 @@ pub enum InteractionsType { impl Shadow for Interactions { type Result = (); - fn shadow(&self, tables: &mut SimulatorTables) { + fn shadow(&self, tables: &mut ShadowTablesMut) { match &self.interactions { InteractionsType::Property(property) => { let initial_tables = tables.clone(); @@ -319,7 +319,7 @@ impl Shadow for Interactions { let res = interaction.shadow(tables); if res.is_err() { // If any interaction fails, we reset the tables to the initial state - *tables = initial_tables.clone(); + **tables = initial_tables.clone(); break; } } @@ -576,7 +576,7 @@ impl Display for InteractionType { impl Shadow for InteractionType { type Result = anyhow::Result>>; - fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { + fn shadow(&self, env: &mut ShadowTablesMut) -> Self::Result { match self { Self::Query(query) => query.shadow(env), Self::FsyncQuery(query) => { diff --git a/simulator/generation/property.rs b/simulator/generation/property.rs index afa3f1b0c..bb4678872 100644 --- a/simulator/generation/property.rs +++ b/simulator/generation/property.rs @@ -796,8 +796,8 @@ impl Property { let last = stack.last().unwrap(); match last { Ok(_) => { - let _ = - query_clone.shadow(env.get_conn_tables_mut(connection_index)); + let _ = query_clone + .shadow(&mut env.get_conn_tables_mut(connection_index)); Ok(Ok(())) } Err(err) => { @@ -1040,7 +1040,8 @@ fn assert_all_table_values( let assertion = InteractionType::Assertion(Assertion::new(format!("table {table} should contain all of its expected values"), { let table = table.clone(); move |stack: &Vec, env: &mut SimulatorEnv| { - let table = env.get_conn_tables(connection_index).iter().find(|t| t.name == table).ok_or_else(|| { + let conn_ctx = env.get_conn_tables(connection_index); + let table = conn_ctx.iter().find(|t| t.name == table).ok_or_else(|| { LimboError::InternalError(format!( "table {table} should exist in simulator env" )) diff --git a/simulator/model/mod.rs b/simulator/model/mod.rs index 237750683..9a3c81e9f 100644 --- a/simulator/model/mod.rs +++ b/simulator/model/mod.rs @@ -15,7 +15,7 @@ use sql_generation::model::{ }; use turso_parser::ast::Distinctness; -use crate::{generation::Shadow, runner::env::SimulatorTables}; +use crate::{generation::Shadow, runner::env::ShadowTablesMut}; // This type represents the potential queries on the database. #[derive(Debug, Clone, Serialize, Deserialize)] @@ -83,7 +83,7 @@ impl Display for Query { impl Shadow for Query { type Result = anyhow::Result>>; - fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { + fn shadow(&self, env: &mut ShadowTablesMut) -> Self::Result { match self { Query::Create(create) => create.shadow(env), Query::Insert(insert) => insert.shadow(env), @@ -102,7 +102,7 @@ impl Shadow for Query { impl Shadow for Create { type Result = anyhow::Result>>; - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { + fn shadow(&self, tables: &mut ShadowTablesMut) -> Self::Result { if !tables.iter().any(|t| t.name == self.table.name) { tables.push(self.table.clone()); Ok(vec![]) @@ -117,9 +117,8 @@ impl Shadow for Create { impl Shadow for CreateIndex { type Result = Vec>; - fn shadow(&self, env: &mut SimulatorTables) -> Vec> { - env.tables - .iter_mut() + fn shadow(&self, env: &mut ShadowTablesMut) -> Vec> { + env.iter_mut() .find(|t| t.name == self.table_name) .unwrap() .indexes @@ -131,8 +130,8 @@ impl Shadow for CreateIndex { impl Shadow for Delete { type Result = anyhow::Result>>; - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { - let table = tables.tables.iter_mut().find(|t| t.name == self.table); + fn shadow(&self, tables: &mut ShadowTablesMut) -> Self::Result { + let table = tables.iter_mut().find(|t| t.name == self.table); if let Some(table) = table { // If the table exists, we can delete from it @@ -153,7 +152,7 @@ impl Shadow for Delete { impl Shadow for Drop { type Result = anyhow::Result>>; - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { + fn shadow(&self, tables: &mut ShadowTablesMut) -> Self::Result { if !tables.iter().any(|t| t.name == self.table) { // If the table does not exist, we return an error return Err(anyhow::anyhow!( @@ -162,7 +161,7 @@ impl Shadow for Drop { )); } - tables.tables.retain(|t| t.name != self.table); + tables.retain(|t| t.name != self.table); Ok(vec![]) } @@ -171,10 +170,10 @@ impl Shadow for Drop { impl Shadow for Insert { type Result = anyhow::Result>>; - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { + fn shadow(&self, tables: &mut ShadowTablesMut) -> Self::Result { match self { Insert::Values { table, values } => { - if let Some(t) = tables.tables.iter_mut().find(|t| &t.name == table) { + if let Some(t) = tables.iter_mut().find(|t| &t.name == table) { t.rows.extend(values.clone()); } else { return Err(anyhow::anyhow!( @@ -185,7 +184,7 @@ impl Shadow for Insert { } Insert::Select { table, select } => { let rows = select.shadow(tables)?; - if let Some(t) = tables.tables.iter_mut().find(|t| &t.name == table) { + if let Some(t) = tables.iter_mut().find(|t| &t.name == table) { t.rows.extend(rows); } else { return Err(anyhow::anyhow!( @@ -202,9 +201,7 @@ impl Shadow for Insert { impl Shadow for FromClause { type Result = anyhow::Result; - fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { - let tables = &mut env.tables; - + fn shadow(&self, tables: &mut ShadowTablesMut) -> Self::Result { let first_table = tables .iter() .find(|t| t.name == self.table) @@ -259,7 +256,7 @@ impl Shadow for FromClause { impl Shadow for SelectInner { type Result = anyhow::Result; - fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { + fn shadow(&self, env: &mut ShadowTablesMut) -> Self::Result { if let Some(from) = &self.from { let mut join_table = from.shadow(env)?; let col_count = join_table.columns().count(); @@ -327,7 +324,7 @@ impl Shadow for SelectInner { impl Shadow for Select { type Result = anyhow::Result>>; - fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { + fn shadow(&self, env: &mut ShadowTablesMut) -> Self::Result { let first_result = self.body.select.shadow(env)?; let mut rows = first_result.rows; @@ -357,28 +354,26 @@ impl Shadow for Select { impl Shadow for Begin { type Result = Vec>; - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { + fn shadow(&self, tables: &mut ShadowTablesMut) -> Self::Result { // FIXME: currently the snapshot is taken eagerly // this is wrong for Deffered transactions - tables.snapshot = Some(tables.tables.clone()); + tables.create_snapshot(); vec![] } } impl Shadow for Commit { type Result = Vec>; - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { - tables.snapshot = None; + fn shadow(&self, tables: &mut ShadowTablesMut) -> Self::Result { + tables.apply_snapshot(); vec![] } } impl Shadow for Rollback { type Result = Vec>; - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { - if let Some(tables_) = tables.snapshot.take() { - tables.tables = tables_; - } + fn shadow(&self, tables: &mut ShadowTablesMut) -> Self::Result { + tables.delete_snapshot(); vec![] } } @@ -386,8 +381,8 @@ impl Shadow for Rollback { impl Shadow for Update { type Result = anyhow::Result>>; - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { - let table = tables.tables.iter_mut().find(|t| t.name == self.table); + fn shadow(&self, tables: &mut ShadowTablesMut) -> Self::Result { + let table = tables.iter_mut().find(|t| t.name == self.table); let table = if let Some(table) = table { table diff --git a/simulator/runner/env.rs b/simulator/runner/env.rs index 922bfe736..9ef9b612a 100644 --- a/simulator/runner/env.rs +++ b/simulator/runner/env.rs @@ -1,6 +1,6 @@ use std::fmt::Display; use std::mem; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use std::panic::UnwindSafe; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -32,6 +32,79 @@ pub(crate) enum SimulationPhase { Shrink, } +#[derive(Debug)] +pub struct ShadowTables<'a> { + commited_tables: &'a Vec, + transaction_tables: Option<&'a Vec
>, +} + +#[derive(Debug)] +pub struct ShadowTablesMut<'a> { + commited_tables: &'a mut Vec
, + transaction_tables: &'a mut Option>, +} + +impl<'a> ShadowTables<'a> { + fn tables(&self) -> &'a Vec
{ + self.transaction_tables.map_or(self.commited_tables, |v| v) + } +} + +impl<'a> Deref for ShadowTables<'a> { + type Target = Vec
; + + fn deref(&self) -> &Self::Target { + self.tables() + } +} + +impl<'a, 'b> ShadowTablesMut<'a> +where + 'a: 'b, +{ + fn tables(&'a self) -> &'a Vec
{ + self.transaction_tables + .as_ref() + .unwrap_or(self.commited_tables) + } + + fn tables_mut(&'b mut self) -> &'b mut Vec
{ + self.transaction_tables + .as_mut() + .unwrap_or(self.commited_tables) + } + + pub fn create_snapshot(&mut self) { + *self.transaction_tables = Some(self.commited_tables.clone()); + } + + pub fn apply_snapshot(&mut self) { + // TODO: as we do not have concurrent tranasactions yet in the simulator + // there is no conflict we are ignoring conflict problems right now + if let Some(transation_tables) = self.transaction_tables.take() { + *self.commited_tables = transation_tables + } + } + + pub fn delete_snapshot(&mut self) { + *self.transaction_tables = None; + } +} + +impl<'a> Deref for ShadowTablesMut<'a> { + type Target = Vec
; + + fn deref(&self) -> &Self::Target { + self.tables() + } +} + +impl<'a> DerefMut for ShadowTablesMut<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.tables_mut() + } +} + #[derive(Debug, Clone)] pub(crate) struct SimulatorTables { pub(crate) tables: Vec
, @@ -75,9 +148,9 @@ pub(crate) struct SimulatorEnv { pub memory_io: bool, /// If connection state is None, means we are not in a transaction - pub connection_tables: Vec>, + pub connection_tables: Vec>>, // Table data that is committed into the database or wal - pub committed_tables: SimulatorTables, + pub committed_tables: Vec
, } impl UnwindSafe for SimulatorEnv {} @@ -300,7 +373,7 @@ impl SimulatorEnv { phase: SimulationPhase::Test, memory_io: cli_opts.memory_io, profile: profile.clone(), - committed_tables: SimulatorTables::new(), + committed_tables: Vec::new(), connection_tables: vec![None; profile.max_connections], } } @@ -363,7 +436,7 @@ impl SimulatorEnv { } } - let tables = &self.get_conn_tables(conn_index).tables; + let tables = self.get_conn_tables(conn_index).tables(); ConnectionGenContext { opts: &self.profile.query.gen_opts, @@ -371,20 +444,18 @@ impl SimulatorEnv { } } - pub fn get_conn_tables(&self, conn_index: usize) -> &SimulatorTables { - self.connection_tables - .get(conn_index) - .unwrap() - .as_ref() - .unwrap_or(&self.committed_tables) + pub fn get_conn_tables<'a>(&'a self, conn_index: usize) -> ShadowTables<'a> { + ShadowTables { + transaction_tables: self.connection_tables.get(conn_index).unwrap().as_ref(), + commited_tables: &self.committed_tables, + } } - pub fn get_conn_tables_mut(&mut self, conn_index: usize) -> &mut SimulatorTables { - self.connection_tables - .get_mut(conn_index) - .unwrap() - .as_mut() - .unwrap_or(&mut self.committed_tables) + pub fn get_conn_tables_mut<'a>(&'a mut self, conn_index: usize) -> ShadowTablesMut<'a> { + ShadowTablesMut { + transaction_tables: self.connection_tables.get_mut(conn_index).unwrap(), + commited_tables: &mut self.committed_tables, + } } } diff --git a/simulator/runner/execution.rs b/simulator/runner/execution.rs index 09c1674fe..3657b995b 100644 --- a/simulator/runner/execution.rs +++ b/simulator/runner/execution.rs @@ -236,7 +236,7 @@ pub fn execute_interaction_turso( } } } - let _ = interaction.shadow(env.get_conn_tables_mut(interaction.connection_index)); + let _ = interaction.shadow(&mut env.get_conn_tables_mut(interaction.connection_index)); Ok(ExecutionContinuation::NextInteraction) } @@ -329,7 +329,7 @@ fn execute_interaction_rusqlite( } } - let _ = interaction.shadow(env.get_conn_tables_mut(interaction.connection_index)); + let _ = interaction.shadow(&mut env.get_conn_tables_mut(interaction.connection_index)); Ok(ExecutionContinuation::NextInteraction) }