From 642060f2837cca01d4773fe02e375391cfbe40f6 Mon Sep 17 00:00:00 2001 From: pedrocarlo Date: Mon, 25 Aug 2025 15:53:45 -0300 Subject: [PATCH] refactor sql_generation/model/query --- parser/src/ast.rs | 35 +++ sql_generation/model/query/create.rs | 22 +- sql_generation/model/query/create_index.rs | 67 ---- sql_generation/model/query/delete.rs | 24 -- sql_generation/model/query/drop.rs | 20 -- sql_generation/model/query/insert.rs | 34 +-- sql_generation/model/query/mod.rs | 28 +- sql_generation/model/query/select.rs | 339 +++++---------------- sql_generation/model/query/transaction.rs | 28 -- sql_generation/model/query/update.rs | 39 +-- 10 files changed, 123 insertions(+), 513 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 5626ffbaa..44a427dcb 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -982,6 +982,41 @@ impl std::fmt::Display for QualifiedName { } } +impl QualifiedName { + /// Constructor + pub fn single(name: Name) -> Self { + Self { + db_name: None, + name, + alias: None, + } + } + /// Constructor + pub fn fullname(db_name: Name, name: Name) -> Self { + Self { + db_name: Some(db_name), + name, + alias: None, + } + } + /// Constructor + pub fn xfullname(db_name: Name, name: Name, alias: Name) -> Self { + Self { + db_name: Some(db_name), + name, + alias: Some(alias), + } + } + /// Constructor + pub fn alias(name: Name, alias: Name) -> Self { + Self { + db_name: None, + name, + alias: Some(alias), + } + } +} + /// `ALTER TABLE` body // https://sqlite.org/lang_altertable.html #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/sql_generation/model/query/create.rs b/sql_generation/model/query/create.rs index ab0cd9789..e628b5cc5 100644 --- a/sql_generation/model/query/create.rs +++ b/sql_generation/model/query/create.rs @@ -2,33 +2,13 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::{ - generation::Shadow, - model::table::{SimValue, Table}, - runner::env::SimulatorTables, -}; +use crate::model::table::Table; #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct Create { pub(crate) table: Table, } -impl Shadow for Create { - type Result = anyhow::Result>>; - - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { - if !tables.iter().any(|t| t.name == self.table.name) { - tables.push(self.table.clone()); - Ok(vec![]) - } else { - Err(anyhow::anyhow!( - "Table {} already exists. CREATE TABLE statement ignored.", - self.table.name - )) - } - } -} - impl Display for Create { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "CREATE TABLE {} (", self.table.name)?; diff --git a/sql_generation/model/query/create_index.rs b/sql_generation/model/query/create_index.rs index cc7f7566a..aba0f98bf 100644 --- a/sql_generation/model/query/create_index.rs +++ b/sql_generation/model/query/create_index.rs @@ -1,9 +1,3 @@ -use crate::{ - generation::{gen_random_text, pick, pick_n_unique, ArbitraryFrom, Shadow}, - model::table::SimValue, - runner::env::{SimulatorEnv, SimulatorTables}, -}; -use rand::Rng; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] @@ -28,19 +22,6 @@ pub(crate) struct CreateIndex { pub(crate) columns: Vec<(String, SortOrder)>, } -impl Shadow for CreateIndex { - type Result = Vec>; - fn shadow(&self, env: &mut SimulatorTables) -> Vec> { - env.tables - .iter_mut() - .find(|t| t.name == self.table_name) - .unwrap() - .indexes - .push(self.index_name.clone()); - vec![] - } -} - impl std::fmt::Display for CreateIndex { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -56,51 +37,3 @@ impl std::fmt::Display for CreateIndex { ) } } - -impl ArbitraryFrom<&SimulatorEnv> for CreateIndex { - fn arbitrary_from(rng: &mut R, env: &SimulatorEnv) -> Self { - assert!( - !env.tables.is_empty(), - "Cannot create an index when no tables exist in the environment." - ); - - let table = pick(&env.tables, rng); - - if table.columns.is_empty() { - panic!( - "Cannot create an index on table '{}' as it has no columns.", - table.name - ); - } - - let num_columns_to_pick = rng.random_range(1..=table.columns.len()); - let picked_column_indices = pick_n_unique(0..table.columns.len(), num_columns_to_pick, rng); - - let columns = picked_column_indices - .into_iter() - .map(|i| { - let column = &table.columns[i]; - ( - column.name.clone(), - if rng.random_bool(0.5) { - SortOrder::Asc - } else { - SortOrder::Desc - }, - ) - }) - .collect::>(); - - let index_name = format!( - "idx_{}_{}", - table.name, - gen_random_text(rng).chars().take(8).collect::() - ); - - CreateIndex { - index_name, - table_name: table.name.clone(), - columns, - } - } -} diff --git a/sql_generation/model/query/delete.rs b/sql_generation/model/query/delete.rs index 265cdfe96..a86479850 100644 --- a/sql_generation/model/query/delete.rs +++ b/sql_generation/model/query/delete.rs @@ -2,8 +2,6 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::{generation::Shadow, model::table::SimValue, runner::env::SimulatorTables}; - use super::predicate::Predicate; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -12,28 +10,6 @@ pub(crate) struct Delete { pub(crate) predicate: Predicate, } -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); - - if let Some(table) = table { - // If the table exists, we can delete from it - let t2 = table.clone(); - table.rows.retain_mut(|r| !self.predicate.test(r, &t2)); - } else { - // If the table does not exist, we return an error - return Err(anyhow::anyhow!( - "Table {} does not exist. DELETE statement ignored.", - self.table - )); - } - - Ok(vec![]) - } -} - impl Display for Delete { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "DELETE FROM {} WHERE {}", self.table, self.predicate) diff --git a/sql_generation/model/query/drop.rs b/sql_generation/model/query/drop.rs index 2b4379ff9..d9a34a9e9 100644 --- a/sql_generation/model/query/drop.rs +++ b/sql_generation/model/query/drop.rs @@ -2,31 +2,11 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::{generation::Shadow, model::table::SimValue, runner::env::SimulatorTables}; - #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub(crate) struct Drop { pub(crate) table: String, } -impl Shadow for Drop { - type Result = anyhow::Result>>; - - fn shadow(&self, tables: &mut SimulatorTables) -> 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!( - "Table {} does not exist. DROP statement ignored.", - self.table - )); - } - - tables.tables.retain(|t| t.name != self.table); - - Ok(vec![]) - } -} - impl Display for Drop { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "DROP TABLE {}", self.table) diff --git a/sql_generation/model/query/insert.rs b/sql_generation/model/query/insert.rs index 3dc8659df..9fd391612 100644 --- a/sql_generation/model/query/insert.rs +++ b/sql_generation/model/query/insert.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::{generation::Shadow, model::table::SimValue, runner::env::SimulatorTables}; +use crate::model::table::SimValue; use super::select::Select; @@ -18,38 +18,6 @@ pub(crate) enum Insert { }, } -impl Shadow for Insert { - type Result = anyhow::Result>>; - - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { - match self { - Insert::Values { table, values } => { - if let Some(t) = tables.tables.iter_mut().find(|t| &t.name == table) { - t.rows.extend(values.clone()); - } else { - return Err(anyhow::anyhow!( - "Table {} does not exist. INSERT statement ignored.", - table - )); - } - } - Insert::Select { table, select } => { - let rows = select.shadow(tables)?; - if let Some(t) = tables.tables.iter_mut().find(|t| &t.name == table) { - t.rows.extend(rows); - } else { - return Err(anyhow::anyhow!( - "Table {} does not exist. INSERT statement ignored.", - table - )); - } - } - } - - Ok(vec![]) - } -} - impl Insert { pub(crate) fn table(&self) -> &str { match self { diff --git a/sql_generation/model/query/mod.rs b/sql_generation/model/query/mod.rs index 2c8704003..9ae222a9a 100644 --- a/sql_generation/model/query/mod.rs +++ b/sql_generation/model/query/mod.rs @@ -10,14 +10,7 @@ use serde::{Deserialize, Serialize}; use turso_parser::ast::fmt::ToSqlContext; use update::Update; -use crate::{ - generation::Shadow, - model::{ - query::transaction::{Begin, Commit, Rollback}, - table::SimValue, - }, - runner::env::SimulatorTables, -}; +use crate::model::query::transaction::{Begin, Commit, Rollback}; pub mod create; pub mod create_index; @@ -75,25 +68,6 @@ impl Query { } } -impl Shadow for Query { - type Result = anyhow::Result>>; - - fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { - match self { - Query::Create(create) => create.shadow(env), - Query::Insert(insert) => insert.shadow(env), - Query::Delete(delete) => delete.shadow(env), - Query::Select(select) => select.shadow(env), - Query::Update(update) => update.shadow(env), - Query::Drop(drop) => drop.shadow(env), - Query::CreateIndex(create_index) => Ok(create_index.shadow(env)), - Query::Begin(begin) => Ok(begin.shadow(env)), - Query::Commit(commit) => Ok(commit.shadow(env)), - Query::Rollback(rollback) => Ok(rollback.shadow(env)), - } - } -} - impl Display for Query { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/sql_generation/model/query/select.rs b/sql_generation/model/query/select.rs index b5e516a0e..6c7897e1d 100644 --- a/sql_generation/model/query/select.rs +++ b/sql_generation/model/query/select.rs @@ -1,18 +1,12 @@ use std::{collections::HashSet, fmt::Display}; -use anyhow::Context; pub use ast::Distinctness; -use itertools::Itertools; use serde::{Deserialize, Serialize}; use turso_parser::ast::{self, fmt::ToTokens, SortOrder}; -use crate::{ - generation::Shadow, - model::{ - query::EmptyContext, - table::{JoinTable, JoinType, JoinedTable, SimValue, Table, TableContext}, - }, - runner::env::SimulatorTables, +use crate::model::{ + query::EmptyContext, + table::{JoinType, JoinedTable}, }; use super::predicate::Predicate; @@ -188,45 +182,30 @@ pub struct FromClause { impl FromClause { fn to_sql_ast(&self) -> ast::FromClause { ast::FromClause { - select: Some(Box::new(ast::SelectTable::Table( - ast::QualifiedName::single(ast::Name::from_str(&self.table)), + select: Box::new(ast::SelectTable::Table( + ast::QualifiedName::single(ast::Name::new(&self.table)), None, None, - ))), - joins: if self.joins.is_empty() { - None - } else { - Some( - self.joins - .iter() - .map(|join| ast::JoinedSelectTable { - operator: match join.join_type { - JoinType::Inner => { - ast::JoinOperator::TypedJoin(Some(ast::JoinType::INNER)) - } - JoinType::Left => { - ast::JoinOperator::TypedJoin(Some(ast::JoinType::LEFT)) - } - JoinType::Right => { - ast::JoinOperator::TypedJoin(Some(ast::JoinType::RIGHT)) - } - JoinType::Full => { - ast::JoinOperator::TypedJoin(Some(ast::JoinType::OUTER)) - } - JoinType::Cross => { - ast::JoinOperator::TypedJoin(Some(ast::JoinType::CROSS)) - } - }, - table: ast::SelectTable::Table( - ast::QualifiedName::single(ast::Name::from_str(&join.table)), - None, - None, - ), - constraint: Some(ast::JoinConstraint::On(join.on.0.clone())), - }) - .collect(), - ) - }, + )), + joins: self + .joins + .iter() + .map(|join| ast::JoinedSelectTable { + operator: match join.join_type { + JoinType::Inner => ast::JoinOperator::TypedJoin(Some(ast::JoinType::INNER)), + JoinType::Left => ast::JoinOperator::TypedJoin(Some(ast::JoinType::LEFT)), + JoinType::Right => ast::JoinOperator::TypedJoin(Some(ast::JoinType::RIGHT)), + JoinType::Full => ast::JoinOperator::TypedJoin(Some(ast::JoinType::OUTER)), + JoinType::Cross => ast::JoinOperator::TypedJoin(Some(ast::JoinType::CROSS)), + }, + table: Box::new(ast::SelectTable::Table( + ast::QualifiedName::single(ast::Name::new(&join.table)), + None, + None, + )), + constraint: Some(ast::JoinConstraint::On(Box::new(join.on.0.clone()))), + }) + .collect(), } } @@ -239,166 +218,12 @@ impl FromClause { } } -impl Shadow for FromClause { - type Result = anyhow::Result; - fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { - let tables = &mut env.tables; - - let first_table = tables - .iter() - .find(|t| t.name == self.table) - .context("Table not found")?; - - let mut join_table = JoinTable { - tables: vec![first_table.clone()], - rows: Vec::new(), - }; - - for join in &self.joins { - let joined_table = tables - .iter() - .find(|t| t.name == join.table) - .context("Joined table not found")?; - - join_table.tables.push(joined_table.clone()); - - match join.join_type { - JoinType::Inner => { - // Implement inner join logic - let join_rows = joined_table - .rows - .iter() - .filter(|row| join.on.test(row, joined_table)) - .cloned() - .collect::>(); - // take a cartesian product of the rows - let all_row_pairs = join_table - .rows - .clone() - .into_iter() - .cartesian_product(join_rows.iter()); - - for (row1, row2) in all_row_pairs { - let row = row1.iter().chain(row2.iter()).cloned().collect::>(); - - let is_in = join.on.test(&row, &join_table); - - if is_in { - join_table.rows.push(row); - } - } - } - _ => todo!(), - } - } - Ok(join_table) - } -} - -impl Shadow for SelectInner { - type Result = anyhow::Result; - - fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { - if let Some(from) = &self.from { - let mut join_table = from.shadow(env)?; - let col_count = join_table.columns().count(); - for row in &mut join_table.rows { - assert_eq!( - row.len(), - col_count, - "Row length does not match column length after join" - ); - } - let join_clone = join_table.clone(); - - join_table - .rows - .retain(|row| self.where_clause.test(row, &join_clone)); - - if self.distinctness == Distinctness::Distinct { - join_table.rows.sort_unstable(); - join_table.rows.dedup(); - } - - Ok(join_table) - } else { - assert!(self - .columns - .iter() - .all(|col| matches!(col, ResultColumn::Expr(_)))); - - // If `WHERE` is false, just return an empty table - if !self.where_clause.test(&[], &Table::anonymous(vec![])) { - return Ok(JoinTable { - tables: Vec::new(), - rows: Vec::new(), - }); - } - - // Compute the results of the column expressions and make a row - let mut row = Vec::new(); - for col in &self.columns { - match col { - ResultColumn::Expr(expr) => { - let value = expr.eval(&[], &Table::anonymous(vec![])); - if let Some(value) = value { - row.push(value); - } else { - return Err(anyhow::anyhow!( - "Failed to evaluate expression in free select ({})", - expr.0.format_with_context(&EmptyContext {}).unwrap() - )); - } - } - _ => unreachable!("Only expressions are allowed in free selects"), - } - } - - Ok(JoinTable { - tables: Vec::new(), - rows: vec![row], - }) - } - } -} - -impl Shadow for Select { - type Result = anyhow::Result>>; - - fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { - let first_result = self.body.select.shadow(env)?; - - let mut rows = first_result.rows; - - for compound in self.body.compounds.iter() { - let compound_results = compound.select.shadow(env)?; - - match compound.operator { - CompoundOperator::Union => { - // Union means we need to combine the results, removing duplicates - let mut new_rows = compound_results.rows; - new_rows.extend(rows.clone()); - new_rows.sort_unstable(); - new_rows.dedup(); - rows = new_rows; - } - CompoundOperator::UnionAll => { - // Union all means we just concatenate the results - rows.extend(compound_results.rows.into_iter()); - } - } - } - - Ok(rows) - } -} - impl Select { pub fn to_sql_ast(&self) -> ast::Select { ast::Select { with: None, body: ast::SelectBody { - select: Box::new(ast::OneSelect::Select(Box::new(ast::SelectInner { + select: ast::OneSelect::Select { distinctness: if self.body.select.distinctness == Distinctness::Distinct { Some(ast::Distinctness::Distinct) } else { @@ -411,77 +236,81 @@ impl Select { .iter() .map(|col| match col { ResultColumn::Expr(expr) => { - ast::ResultColumn::Expr(expr.0.clone(), None) + ast::ResultColumn::Expr(expr.0.clone().into_boxed(), None) } ResultColumn::Star => ast::ResultColumn::Star, ResultColumn::Column(name) => ast::ResultColumn::Expr( - ast::Expr::Id(ast::Name::Ident(name.clone())), + ast::Expr::Id(ast::Name::Ident(name.clone())).into_boxed(), None, ), }) .collect(), from: self.body.select.from.as_ref().map(|f| f.to_sql_ast()), - where_clause: Some(self.body.select.where_clause.0.clone()), + where_clause: Some(self.body.select.where_clause.0.clone().into_boxed()), group_by: None, - window_clause: None, - }))), - compounds: Some( - self.body - .compounds - .iter() - .map(|compound| ast::CompoundSelect { - operator: match compound.operator { - CompoundOperator::Union => ast::CompoundOperator::Union, - CompoundOperator::UnionAll => ast::CompoundOperator::UnionAll, - }, - select: Box::new(ast::OneSelect::Select(Box::new(ast::SelectInner { - distinctness: Some(compound.select.distinctness), - columns: compound - .select - .columns - .iter() - .map(|col| match col { - ResultColumn::Expr(expr) => { - ast::ResultColumn::Expr(expr.0.clone(), None) - } - ResultColumn::Star => ast::ResultColumn::Star, - ResultColumn::Column(name) => ast::ResultColumn::Expr( - ast::Expr::Id(ast::Name::Ident(name.clone())), - None, - ), - }) - .collect(), - from: compound.select.from.as_ref().map(|f| f.to_sql_ast()), - where_clause: Some(compound.select.where_clause.0.clone()), - group_by: None, - window_clause: None, - }))), - }) - .collect(), - ), - }, - order_by: self.body.select.order_by.as_ref().map(|o| { - o.columns + window_clause: Vec::new(), + }, + compounds: self + .body + .compounds .iter() - .map(|(name, order)| ast::SortedColumn { - expr: ast::Expr::Id(ast::Name::Ident(name.clone())), - order: match order { - SortOrder::Asc => Some(ast::SortOrder::Asc), - SortOrder::Desc => Some(ast::SortOrder::Desc), + .map(|compound| ast::CompoundSelect { + operator: match compound.operator { + CompoundOperator::Union => ast::CompoundOperator::Union, + CompoundOperator::UnionAll => ast::CompoundOperator::UnionAll, + }, + select: ast::OneSelect::Select { + distinctness: Some(compound.select.distinctness), + columns: compound + .select + .columns + .iter() + .map(|col| match col { + ResultColumn::Expr(expr) => { + ast::ResultColumn::Expr(expr.0.clone().into_boxed(), None) + } + ResultColumn::Star => ast::ResultColumn::Star, + ResultColumn::Column(name) => ast::ResultColumn::Expr( + ast::Expr::Id(ast::Name::Ident(name.clone())).into_boxed(), + None, + ), + }) + .collect(), + from: compound.select.from.as_ref().map(|f| f.to_sql_ast()), + where_clause: Some(compound.select.where_clause.0.clone().into_boxed()), + group_by: None, + window_clause: Vec::new(), }, - nulls: None, }) - .collect() - }), - limit: self.limit.map(|l| { - Box::new(ast::Limit { - expr: ast::Expr::Literal(ast::Literal::Numeric(l.to_string())), - offset: None, + .collect(), + }, + order_by: self + .body + .select + .order_by + .as_ref() + .map(|o| { + o.columns + .iter() + .map(|(name, order)| ast::SortedColumn { + expr: ast::Expr::Id(ast::Name::Ident(name.clone())).into_boxed(), + order: match order { + SortOrder::Asc => Some(ast::SortOrder::Asc), + SortOrder::Desc => Some(ast::SortOrder::Desc), + }, + nulls: None, + }) + .collect() }) + .unwrap_or(Vec::new()), + limit: self.limit.map(|l| ast::Limit { + expr: ast::Expr::Literal(ast::Literal::Numeric(l.to_string())).into_boxed(), + offset: None, }), } } } + impl Display for Select { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.to_sql_ast().to_fmt_with_context(f, &EmptyContext {}) diff --git a/sql_generation/model/query/transaction.rs b/sql_generation/model/query/transaction.rs index a73fb076e..2280357fa 100644 --- a/sql_generation/model/query/transaction.rs +++ b/sql_generation/model/query/transaction.rs @@ -2,8 +2,6 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::{generation::Shadow, model::table::SimValue, runner::env::SimulatorTables}; - #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct Begin { pub(crate) immediate: bool, @@ -15,32 +13,6 @@ pub(crate) struct Commit; #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct Rollback; -impl Shadow for Begin { - type Result = Vec>; - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { - tables.snapshot = Some(tables.tables.clone()); - vec![] - } -} - -impl Shadow for Commit { - type Result = Vec>; - fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { - tables.snapshot = None; - 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_; - } - vec![] - } -} - impl Display for Begin { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "BEGIN {}", if self.immediate { "IMMEDIATE" } else { "" }) diff --git a/sql_generation/model/query/update.rs b/sql_generation/model/query/update.rs index a4cc13fa8..c7c3a5a58 100644 --- a/sql_generation/model/query/update.rs +++ b/sql_generation/model/query/update.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::{generation::Shadow, model::table::SimValue, runner::env::SimulatorTables}; +use crate::model::table::SimValue; use super::predicate::Predicate; @@ -19,43 +19,6 @@ impl Update { } } -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); - - let table = if let Some(table) = table { - table - } else { - return Err(anyhow::anyhow!( - "Table {} does not exist. UPDATE statement ignored.", - self.table - )); - }; - - let t2 = table.clone(); - for row in table - .rows - .iter_mut() - .filter(|r| self.predicate.test(r, &t2)) - { - for (column, set_value) in &self.set_values { - if let Some((idx, _)) = table - .columns - .iter() - .enumerate() - .find(|(_, c)| &c.name == column) - { - row[idx] = set_value.clone(); - } - } - } - - Ok(vec![]) - } -} - impl Display for Update { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "UPDATE {} SET ", self.table)?;