diff --git a/src/db/database.rs b/src/db/database.rs index e172570..189fdb7 100644 --- a/src/db/database.rs +++ b/src/db/database.rs @@ -1,5 +1,5 @@ -use crate::db::table::{drop_table, Table, Value}; -use crate::interpreter::ast::{CreateTableStatement, DeleteStatement, DropTableStatement, InsertIntoStatement, SelectStatementStack, SqlStatement, UpdateStatement, AlterTableStatement}; +use crate::db::table::{drop_table, Table, Row}; +use crate::interpreter::ast::{SqlStatement}; use crate::db::table::select; use crate::db::table::insert; use crate::db::table::delete; @@ -10,94 +10,121 @@ use std::collections::HashMap; pub struct Database { pub tables: HashMap, + pub transaction: Option, +} + +pub struct TransactionLog { + pub entries: Vec, + pub savepoint_name: Vec, +} + +pub struct TransactionEntry { + pub statement: SqlStatement, + pub table_name: String, + pub affected_rows: Vec, +} + +pub struct Savepoint { + pub name: String, } impl Database { pub fn new() -> Self { Self { tables: HashMap::new(), + transaction: None, } } - pub fn execute(&mut self, sql_statement: SqlStatement) -> Result>>, String> { + pub fn execute(&mut self, sql_statement: SqlStatement) -> Result>, String> { + let sql_statement_clone = sql_statement.clone(); return match sql_statement { SqlStatement::CreateTable(statement) => { - self.create_table(statement)?; + create_table::create_table(self, statement)?; + self.append_to_transaction(sql_statement_clone, vec![])?; Ok(None) }, SqlStatement::InsertInto(statement) => { - self.insert_into_table(statement)?; + let table = self.get_table_mut(&statement.table_name)?; + insert::insert(table, statement)?; + self.append_to_transaction(sql_statement_clone, vec![])?; Ok(None) }, SqlStatement::Select(statement) => { - let result = self.select_statement_stack(statement)?; + let result = select::select_statement_stack(self, statement)?; Ok(Some(result)) }, SqlStatement::UpdateStatement(statement) => { - self.update_table(statement)?; + let table = self.get_table_mut(&statement.table_name)?; + update::update(table, statement)?; + self.append_to_transaction(sql_statement_clone, vec![])?; Ok(None) }, SqlStatement::DeleteStatement(statement) => { - self.delete_from_table(statement)?; + let table = self.get_table_mut(&statement.table_name)?; + delete::delete(table, statement)?; + self.append_to_transaction(sql_statement_clone, vec![])?; Ok(None) }, SqlStatement::DropTable(statement) => { - self.drop_table(statement)?; + drop_table::drop_table(self, statement)?; + self.append_to_transaction(sql_statement_clone, vec![])?; Ok(None) } SqlStatement::AlterTable(statement) => { - self.alter_table(statement)?; + alter_table::alter_table(self, statement)?; + self.append_to_transaction(sql_statement_clone, vec![])?; Ok(None) } - SqlStatement::BeginTransaction(_statement) => { - todo!() + SqlStatement::BeginTransaction(_) => { + self.transaction = Some(TransactionLog { + entries: vec![], + savepoint_name: vec![], + }); + Ok(None) } SqlStatement::Commit => { - todo!() + self.transaction = None; + self.tables.iter_mut().for_each(|(_, table)| { + table.commit_transaction(); + }); + Ok(None) } - SqlStatement::Rollback(_statement) => { - todo!() + SqlStatement::Rollback(_) => { + self.transaction = None; + self.tables.iter_mut().for_each(|(_, table)| { + table.rollback_transaction(); + }); + Ok(None) } - SqlStatement::Savepoint(_statement) => { - todo!() + SqlStatement::Savepoint(statement) => { + match &mut self.transaction { + Some(transaction) => { + transaction.savepoint_name.push(Savepoint { + name: statement.savepoint_name.clone(), + }); + } + None => { + return Err("No transaction is currently active".to_string()); + } + } + self.append_to_transaction(sql_statement_clone, vec![])?; + Ok(None) } - SqlStatement::Release(_statement) => { - todo!() + SqlStatement::Release(statement) => { + match &mut self.transaction { + Some(transaction) => { + transaction.savepoint_name.retain(|savepoint| savepoint.name != statement.savepoint_name); + } + None => { + return Err("No transaction is currently active".to_string()); + } + } + Ok(None) } } } - fn create_table(&mut self, statement: CreateTableStatement) -> Result<(), String> { - create_table::create_table(self, statement) - } - - fn insert_into_table(&mut self, statement: InsertIntoStatement) -> Result<(), String> { - let table = self.get_table_mut(&statement.table_name)?; - insert::insert(table, statement) - } - - fn select_statement_stack(&mut self, statement: SelectStatementStack) -> Result>, String> { - select::select_statement_stack(self, statement) - } - - fn delete_from_table(&mut self, statement: DeleteStatement) -> Result<(), String> { - let table = self.get_table_mut(&statement.table_name)?; - delete::delete(table, statement) - } - - fn update_table(&mut self, statement: UpdateStatement) -> Result<(), String> { - let table = self.get_table_mut(&statement.table_name)?; - update::update(table, statement) - } - - fn drop_table(&mut self, statement: DropTableStatement) -> Result<(), String> { - drop_table::drop_table(self, statement) - } - - fn alter_table(&mut self, statement: AlterTableStatement) -> Result<(), String> { - alter_table::alter_table(self, statement) - } - pub fn has_table(&self, table_name: &str) -> bool { self.tables.contains_key(table_name) } @@ -115,6 +142,28 @@ impl Database { } Ok(self.tables.get_mut(table_name).unwrap()) } + + fn append_to_transaction(&mut self, sql_statement: SqlStatement, affected_rows: Vec) -> Result<(), String> { + let table_name = match &sql_statement { + SqlStatement::CreateTable(statement) => statement.table_name.clone(), + SqlStatement::InsertInto(statement) => statement.table_name.clone(), + SqlStatement::UpdateStatement(statement) => statement.table_name.clone(), + SqlStatement::DeleteStatement(statement) => statement.table_name.clone(), + SqlStatement::DropTable(statement) => statement.table_name.clone(), + SqlStatement::AlterTable(statement) => statement.table_name.clone(), + SqlStatement::Savepoint(_) => "".to_string(), + _ => unreachable!(), + }; + + if let Some(transaction) = &mut self.transaction { + transaction.entries.push(TransactionEntry { + statement: sql_statement, + table_name: table_name, + affected_rows: affected_rows, + }); + } + Ok(()) + } } #[cfg(test)] @@ -138,7 +187,8 @@ mod tests { constraints: vec![] }, ])) - ]) + ]), + transaction: None, } } diff --git a/src/db/table/alter_table/mod.rs b/src/db/table/alter_table/mod.rs index 78b45ad..313aa70 100644 --- a/src/db/table/alter_table/mod.rs +++ b/src/db/table/alter_table/mod.rs @@ -31,7 +31,7 @@ pub fn alter_table(database: &mut Database, statement: AlterTableStatement) -> R return Err(format!("Column `{}` already exists in table `{}`", column_def.name, statement.table_name)); } table.columns.push(column_def); - table.rows.iter_mut().for_each(|row| { + table.get_rows_mut().iter_mut().for_each(|row| { row.push(Value::Null); }); Ok(()) @@ -45,7 +45,7 @@ pub fn alter_table(database: &mut Database, statement: AlterTableStatement) -> R // This is kind of bad because it's an O(n^2) operation however SQLite // preserves the order of the columns after drop column statements. table.columns.remove(index); - table.rows.iter_mut().for_each(|row| { + table.get_rows_mut().iter_mut().for_each(|row| { row.remove(index); }); Ok(()) @@ -57,7 +57,7 @@ pub fn alter_table(database: &mut Database, statement: AlterTableStatement) -> R mod tests { use super::*; use crate::db::table::test_utils::default_database; - use crate::db::table::{ColumnDefinition, DataType}; + use crate::db::table::{ColumnDefinition, DataType, Row}; #[test] fn alter_table_rename_table_works_correctly() { @@ -99,8 +99,8 @@ mod tests { assert!(table.is_ok()); let table = table.unwrap(); assert!(table.columns.last().unwrap().name == "new_column"); - assert!(table.columns.len() == table.rows.first().unwrap().len()); - assert!(table.rows.iter().all(|row| row.last().unwrap() == &Value::Null)); + assert!(table.columns.len() == table[0].len()); + assert!(table.get_rows().iter().all(|row| row.last().unwrap() == &Value::Null)); } #[test] @@ -116,7 +116,7 @@ mod tests { assert!(table.is_ok()); let table = table.unwrap(); assert!(!table.columns.iter().any(|column| column.name == "age")); - assert!(table.columns.len() == table.rows.first().unwrap().len()); + assert!(table.columns.len() == table[0].len()); let expected_columns_in_order = vec![ ColumnDefinition {name: "id".to_string(), data_type: DataType::Integer, constraints: vec![]}, @@ -125,11 +125,11 @@ mod tests { ]; assert_eq!( expected_columns_in_order, table.columns); let expected_rows = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Real(3000.0)], - vec![Value::Integer(4), Value::Null, Value::Real(4000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Real(3000.0)]), + Row(vec![Value::Integer(4), Value::Null, Value::Real(4000.0)]), ]; - assert_eq!(expected_rows, table.rows); + assert_eq!(expected_rows, table.get_rows_clone()); } } \ No newline at end of file diff --git a/src/db/table/delete/mod.rs b/src/db/table/delete/mod.rs index a75910c..544857d 100644 --- a/src/db/table/delete/mod.rs +++ b/src/db/table/delete/mod.rs @@ -5,13 +5,13 @@ use crate::interpreter::ast::DeleteStatement; use crate::db::table::helpers::common::get_row_indicies_matching_clauses; -pub fn delete(table: &mut Table, statement: DeleteStatement) -> Result<(), String> { +pub fn delete(table: &mut Table, statement: DeleteStatement) -> Result, String> { let row_indicies_to_delete = get_row_indicies_matching_clauses(table, None, &statement.where_clause, &statement.order_by_clause, &statement.limit_clause)?; - swap_remove_bulk(table, row_indicies_to_delete)?; - Ok(()) + swap_remove_bulk(table, &row_indicies_to_delete)?; + Ok(row_indicies_to_delete) } -fn swap_remove_bulk(table: &mut Table, row_indicies: Vec) -> Result<(), String> { +fn swap_remove_bulk(table: &mut Table, row_indicies: &Vec) -> Result<(), String> { if table.rows.len() == 0 { if row_indicies.len() != 0 { unreachable!(); @@ -43,7 +43,7 @@ fn swap_remove_bulk(table: &mut Table, row_indicies: Vec) -> Result<(), S #[cfg(test)] mod tests { use super::*; - use crate::db::table::Value; + use crate::db::table::{Value, Row}; use crate::interpreter::ast::{WhereStackElement, Operator, Operand, WhereCondition, OrderByDirection, OrderByClause}; use crate::db::table::test_utils::{default_table, assert_table_rows_eq_unordered}; use crate::interpreter::ast::LimitClause; @@ -66,25 +66,25 @@ mod tests { let result = delete(&mut table, statement); assert!(result.is_ok()); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], - vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)]), + Row(vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)]), ]; - assert_table_rows_eq_unordered(expected, table.rows); + assert_table_rows_eq_unordered(expected, table.get_rows_clone()); } #[test] fn delete_from_table_with_all_clauses_works_correctly() { let mut table = default_table(); - table.rows = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], - vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)], - vec![Value::Integer(5), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(6), Value::Text("John".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(7), Value::Text("John".to_string()), Value::Integer(35), Value::Real(3000.0)], - ]; + table.set_rows(vec![ + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)]), + Row(vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)]), + Row(vec![Value::Integer(5), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(6), Value::Text("John".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(7), Value::Text("John".to_string()), Value::Integer(35), Value::Real(3000.0)]), + ]); let statement = DeleteStatement { table_name: "users".to_string(), where_clause: Some(vec![WhereStackElement::Condition(WhereCondition { l_side: Operand::Identifier("name".to_string()), operator: Operator::Equals, r_side: Operand::Value(Value::Text("John".to_string())) })]), @@ -94,14 +94,14 @@ mod tests { let result = delete(&mut table, statement); assert!(result.is_ok()); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], - vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)], - vec![Value::Integer(6), Value::Text("John".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(7), Value::Text("John".to_string()), Value::Integer(35), Value::Real(3000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)]), + Row(vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)]), + Row(vec![Value::Integer(6), Value::Text("John".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(7), Value::Text("John".to_string()), Value::Integer(35), Value::Real(3000.0)]), ]; - assert_table_rows_eq_unordered(expected, table.rows); + assert_table_rows_eq_unordered(expected, table.get_rows_clone()); } #[test] @@ -115,10 +115,12 @@ mod tests { }; let result = delete(&mut table, statement); assert!(result.is_ok()); + let row_indicies = result.unwrap(); + assert_eq!(vec![1, 2, 3], row_indicies); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), ]; - assert_table_rows_eq_unordered(expected, table.rows); + assert_table_rows_eq_unordered(expected, table.get_rows_clone()); } #[test] @@ -133,7 +135,7 @@ mod tests { let result = delete(&mut table, statement); assert!(result.is_ok()); let expected = vec![]; - assert_table_rows_eq_unordered(expected, table.rows); + assert_table_rows_eq_unordered(expected, table.get_rows_clone()); } #[test] diff --git a/src/db/table/helpers/common.rs b/src/db/table/helpers/common.rs index 58a871b..9fe5842 100644 --- a/src/db/table/helpers/common.rs +++ b/src/db/table/helpers/common.rs @@ -1,6 +1,6 @@ use std::collections::HashSet; -use crate::db::table::{Table, Value, DataType}; +use crate::db::table::{Table, DataType, Row}; use crate::interpreter::ast::{SelectStatementColumns, WhereStackElement, OrderByClause, LimitClause}; use crate::db::table::helpers::where_clause::row_matches_where_stack; use crate::db::table::helpers::{order_by_clause::get_ordered_row_indicies, limit_clause::get_limited_rows}; @@ -9,12 +9,12 @@ pub struct DistinctOn<'a> { pub columns: &'a SelectStatementColumns, } -pub fn validate_and_clone_row(table: &Table, row: &Vec) -> Result, String> { +pub fn validate_and_clone_row(table: &Table, row: &Row) -> Result { if row.len() != table.width() { return Err(format!("Rows have incorrect width")); } - let mut row_values: Vec = vec![]; + let mut row_values: Row = Row(vec![]); for (i, value) in row.iter().enumerate() { if value.get_type() != table.columns[i].data_type && value.get_type() != DataType::Null { return Err(format!("Data type mismatch for column {}", table.columns[i].name)); @@ -24,10 +24,10 @@ pub fn validate_and_clone_row(table: &Table, row: &Vec) -> Result, columns: Option<&SelectStatementColumns>) -> Result>, String> { - let mut rows: Vec> = vec![]; +pub fn get_row_columns_from_indicies(table: &Table, row_indicies: Vec, columns: Option<&SelectStatementColumns>) -> Result, String> { + let mut rows: Vec = vec![]; for index in row_indicies { - let row = table.rows[index].clone(); + let row: Row = table[index].clone(); if let Some(columns) = columns { rows.push(get_columns_from_row(table, &row, columns)?); } @@ -41,7 +41,7 @@ pub fn get_row_columns_from_indicies(table: &Table, row_indicies: Vec, co pub fn get_row_indicies_matching_where_clause(table: &Table, where_clause: &Option>) -> Result, String> { if let Some(where_clause) = where_clause { let mut row_indicies: Vec = vec![]; - for (i, row) in table.rows.iter().enumerate() { + for (i, row) in table.iter().enumerate() { if row_matches_where_stack(table, &row, &where_clause)? { row_indicies.push(i); } @@ -53,8 +53,8 @@ pub fn get_row_indicies_matching_where_clause(table: &Table, where_clause: &Opti } } -pub fn get_columns_from_row(table: &Table, row: &Vec, selected_columns: &SelectStatementColumns) -> Result, String> { - let mut row_values: Vec = vec![]; +pub fn get_columns_from_row(table: &Table, row: &Row, selected_columns: &SelectStatementColumns) -> Result { + let mut row_values: Row = Row(vec![]); if *selected_columns == SelectStatementColumns::All { return Ok(validate_and_clone_row(table, row)?); } else { @@ -88,9 +88,9 @@ pub fn get_row_indicies_matching_clauses(table: &Table, mode: Option return Ok(row_indicies); } -pub fn remove_duplicate_rows(rows: Vec>) -> Vec> { - let set = rows.into_iter().collect::>>(); - let result = set.into_iter().collect::>>(); +pub fn remove_duplicate_rows(rows: Vec) -> Vec { + let set = rows.into_iter().collect::>(); + let result = set.into_iter().collect::>(); return result; } @@ -99,7 +99,7 @@ pub fn remove_duplicate_rows_from_indicies(table: &Table, mut row_indicies: Vec< let mut index = row_indicies.len(); while index > 0 { index -= 1; - let row = get_columns_from_row(table, &table.rows[row_indicies[index]], columns)?; + let row = get_columns_from_row(table, &table[row_indicies[index]], columns)?; if set.contains(&row) { row_indicies.swap_remove(index); } diff --git a/src/db/table/helpers/order_by_clause.rs b/src/db/table/helpers/order_by_clause.rs index 9a61cb6..dc12f04 100644 --- a/src/db/table/helpers/order_by_clause.rs +++ b/src/db/table/helpers/order_by_clause.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; use crate::interpreter::ast::OrderByClause; use crate::db::table::Table; -use crate::db::table::Value; +use crate::db::table::{Row}; @@ -11,12 +11,12 @@ use crate::db::table::Value; pub fn get_ordered_row_indicies(table: &Table, mut row_indicies: Vec, order_by_clauses: &Vec) -> Result, String> { let columns: Vec<&String> = table.columns.iter().map(|column| &column.name).collect(); row_indicies.sort_by(|a, b| { - perform_comparions(&columns, &table.rows[*a], &table.rows[*b], order_by_clauses) + perform_comparions(&columns, &table[*a], &table[*b], order_by_clauses) }); return Ok(row_indicies); } -pub fn perform_comparions(columns: &Vec<&String>, row1: &Vec, row2: &Vec, order_by_clauses: &Vec) -> Ordering { +pub fn perform_comparions(columns: &Vec<&String>, row1: &Row, row2: &Row, order_by_clauses: &Vec) -> Ordering { let mut result = Ordering::Equal; for comparison in order_by_clauses { let index = get_index_of_column(columns, &comparison.column); @@ -46,7 +46,7 @@ fn get_index_of_column(columns: &Vec<&String>, column_name: &String) -> Result Table { @@ -59,13 +59,13 @@ mod tests { ColumnDefinition {name: "some_data".to_string(), data_type: DataType::Blob, constraints: vec![]}, ], rows: vec![ - vec![Value::Integer(3), Value::Text("c_Jim".to_string()), Value::Real(3000.0), Value::Blob(b"0022".to_vec())], - vec![Value::Integer(1), Value::Text("a_John".to_string()), Value::Real(1000.0), Value::Blob(b"0000".to_vec())], - vec![Value::Null, Value::Null, Value::Null, Value::Null], - vec![Value::Integer(2), Value::Text("b_Jane".to_string()), Value::Real(2000.0), Value::Blob(b"0201".to_vec())], - vec![Value::Integer(3), Value::Text("b_Jim".to_string()), Value::Real(1500.0), Value::Blob(b"0102".to_vec())], - vec![Value::Integer(4), Value::Text("a_Jim".to_string()), Value::Real(500.0), Value::Blob(b"0101".to_vec())], - vec![Value::Integer(1), Value::Text("a_Jim".to_string()), Value::Real(5000.0), Value::Blob(b"0401".to_vec())], + RowStack::new(Row(vec![Value::Integer(3), Value::Text("c_Jim".to_string()), Value::Real(3000.0), Value::Blob(b"0022".to_vec())])), + RowStack::new(Row(vec![Value::Integer(1), Value::Text("a_John".to_string()), Value::Real(1000.0), Value::Blob(b"0000".to_vec())])), + RowStack::new(Row(vec![Value::Null, Value::Null, Value::Null, Value::Null])), + RowStack::new(Row(vec![Value::Integer(2), Value::Text("b_Jane".to_string()), Value::Real(2000.0), Value::Blob(b"0201".to_vec())])), + RowStack::new(Row(vec![Value::Integer(3), Value::Text("b_Jim".to_string()), Value::Real(1500.0), Value::Blob(b"0102".to_vec())])), + RowStack::new(Row(vec![Value::Integer(4), Value::Text("a_Jim".to_string()), Value::Real(500.0), Value::Blob(b"0101".to_vec())])), + RowStack::new(Row(vec![Value::Integer(1), Value::Text("a_Jim".to_string()), Value::Real(5000.0), Value::Blob(b"0401".to_vec())])), ], } } diff --git a/src/db/table/helpers/where_clause/mod.rs b/src/db/table/helpers/where_clause/mod.rs index 36288a9..2c222a9 100644 --- a/src/db/table/helpers/where_clause/mod.rs +++ b/src/db/table/helpers/where_clause/mod.rs @@ -1,7 +1,7 @@ mod where_condition; mod where_stack; use crate::interpreter::ast::{WhereStackElement, WhereCondition}; -use crate::db::table::{Table, Value}; +use crate::db::table::{Table, Value, Row}; // We create an interface here to allow us to create a spy for testing short circuiting. @@ -18,7 +18,7 @@ impl MatchesWhereClause for WhereConditionEvaluator { } // This is the public function that is used to check if a row matches a where stack. -pub fn row_matches_where_stack(table: &Table, row: &Vec, where_stack: &Vec) -> Result { +pub fn row_matches_where_stack(table: &Table, row: &Row, where_stack: &Vec) -> Result { where_stack::matches_where_stack(table, row, where_stack, &mut WhereConditionEvaluator{}) } @@ -48,10 +48,10 @@ mod tests { ColumnDefinition {name:"name".to_string(),data_type:DataType::Text, constraints: vec![] }, ]); let mut spy_where_condition_evaluator = SpyWhereConditionEvaluator{conditions_evaluated: vec![]}; - let row = vec![ + let row = Row(vec![ Value::Integer(1), Value::Text("John".to_string()), - ]; + ]); let condition_1 = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(1))}; let condition_2 = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(2))}; let where_stack = vec![ @@ -71,10 +71,10 @@ mod tests { ColumnDefinition {name:"name".to_string(),data_type:DataType::Text, constraints: vec![] }, ]); let mut spy_where_condition_evaluator = SpyWhereConditionEvaluator{conditions_evaluated: vec![]}; - let row = vec![ + let row = Row(vec![ Value::Integer(1), Value::Text("John".to_string()), - ]; + ]); let condition_1 = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(2))}; let condition_2 = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(1))}; let where_stack = vec![ diff --git a/src/db/table/helpers/where_clause/where_stack.rs b/src/db/table/helpers/where_clause/where_stack.rs index 92a0b01..78ae010 100644 --- a/src/db/table/helpers/where_clause/where_stack.rs +++ b/src/db/table/helpers/where_clause/where_stack.rs @@ -1,4 +1,4 @@ -use crate::db::table::{Table, Value}; +use crate::db::table::{Table, Row}; use crate::interpreter::ast::{WhereStackElement, LogicalOperator, WhereCondition}; use crate::db::table::helpers::where_clause::MatchesWhereClause; @@ -12,7 +12,7 @@ enum Condition<'a> { } impl<'a> Condition<'a> { - fn evaluate(&self, table: &Table, row: &Vec, where_clause_evaluator: &mut dyn MatchesWhereClause) -> Result { + fn evaluate(&self, table: &Table, row: &Row, where_clause_evaluator: &mut dyn MatchesWhereClause) -> Result { match self { Condition::Boolean(boolean) => Ok(*boolean), Condition::WhereCondition(where_condition) => where_clause_evaluator.matches_where_clause(table, row, where_condition), @@ -20,7 +20,7 @@ impl<'a> Condition<'a> { } } -pub fn matches_where_stack(table: &Table, row: &Vec, where_stack: &Vec, where_clause_evaluator: &mut dyn MatchesWhereClause) -> Result { +pub fn matches_where_stack(table: &Table, row: &Row, where_stack: &Vec, where_clause_evaluator: &mut dyn MatchesWhereClause) -> Result { let mut result_stack: Vec = vec![]; for where_stack_element in where_stack { match where_stack_element { @@ -67,7 +67,7 @@ pub fn matches_where_stack(table: &Table, row: &Vec, where_stack: &Vec Result<(), String> { +pub fn insert(table: &mut Table, statement: InsertIntoStatement) -> Result, String> { // Validate columns if let Some(columns) = &statement.columns { for column in columns { @@ -15,7 +15,7 @@ pub fn insert(table: &mut Table, statement: InsertIntoStatement) -> Result<(), S } } - let mut rows: Vec> = vec![]; + let mut rows: Vec = vec![]; // Creates a hash map from the statement values with the columns as the keys // The values are stored in a queue to match the order of the columns, we push back to the queue // and then pop off the front when creating the rows. @@ -29,7 +29,7 @@ pub fn insert(table: &mut Table, statement: InsertIntoStatement) -> Result<(), S } } for _ in 0..statement.values.len() { - let mut row: Vec = vec![]; + let mut row: Row = Row(vec![]); for table_column in table.columns.iter() { if map.contains_key(&table_column.name) { let queue = map.get_mut(&table_column.name).unwrap(); @@ -45,16 +45,18 @@ pub fn insert(table: &mut Table, statement: InsertIntoStatement) -> Result<(), S } else { // Inserts entire row in the order provided in the statement for row in statement.values { - let row_values = validate_and_clone_row(table, &row)?; + let row_values = validate_and_clone_row(table, &Row(row))?; rows.push(row_values); } } // Insert rows + let mut row_indicies: Vec = vec![]; for row in rows { - table.rows.push(row); + table.push(row); + row_indicies.push(table.rows.len() - 1); } - return Ok(()); + return Ok(row_indicies); } @@ -85,26 +87,35 @@ mod tests { values: vec![vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]], }; assert!(insert(&mut table, statement).is_ok()); - let expected = vec![vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]]; - assert_eq!(table.rows, expected); + let expected = vec![Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)])]; + assert_eq!(table.get_rows_clone(), expected); } #[test] fn insert_into_table_with_columns_is_generated_correctly() { let mut table = default_table(); + table.set_rows(vec![ + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), + ]); let statement = InsertIntoStatement { table_name: "users".to_string(), columns: Some(vec!["id".to_string(), "name".to_string()]), values: vec![ - vec![Value::Integer(1), Value::Text("John".to_string())], - vec![Value::Integer(2), Value::Text("Jane".to_string())], + vec![Value::Integer(3), Value::Text("John".to_string()),], + vec![Value::Integer(4), Value::Text("Jane".to_string())], ], }; - assert!(insert(&mut table, statement).is_ok()); + let result = insert(&mut table, statement); + assert!(result.is_ok()); + let row_indicies = result.unwrap(); + assert_eq!(row_indicies, vec![2, 3]); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Null, Value::Null], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Null, Value::Null], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("John".to_string()), Value::Null, Value::Null]), + Row(vec![Value::Integer(4), Value::Text("Jane".to_string()), Value::Null, Value::Null]), ]; - assert_eq!(table.rows, expected); + assert_eq!(expected, table.get_rows_clone()); } } \ No newline at end of file diff --git a/src/db/table/mod.rs b/src/db/table/mod.rs index be82630..1c011a3 100644 --- a/src/db/table/mod.rs +++ b/src/db/table/mod.rs @@ -1,5 +1,7 @@ use std::cmp::Ordering; use std::hash::{Hash, Hasher}; +use std::ops::{Deref, DerefMut, Index, IndexMut}; +use std::cmp::Eq; use crate::interpreter::ast::OrderByDirection; @@ -15,7 +17,7 @@ pub mod alter_table; pub mod test_utils; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum DataType { Integer, Real, @@ -24,14 +26,14 @@ pub enum DataType { Null, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct ColumnDefinition { pub name: String, pub data_type: DataType, pub constraints: Vec, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct ColumnConstraint { pub constraint_type: String, } @@ -137,11 +139,50 @@ impl Hash for Value { } } +#[derive(Debug, Eq, PartialEq, Hash, Clone)] +#[repr(transparent)] +pub struct Row(pub Vec); + +#[derive(Debug)] +pub struct RowStack{ + pub stack: Vec, +} + +impl Deref for Row { + type Target = Vec; + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for Row { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + +impl RowStack { + pub fn new(stack: Row) -> Self { + Self { stack: vec![stack] } + } +} + + #[derive(Debug)] pub struct Table { pub name: String, pub columns: Vec, - pub rows: Vec>, + rows: Vec, +} + +impl Index for Table { + type Output = Row; + + fn index(&self, index: usize) -> &Self::Output { + self.rows[index].stack.last().unwrap() + } +} + +impl IndexMut for Table { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + self.rows[index].stack.last_mut().unwrap() + } } impl Table { @@ -153,6 +194,42 @@ impl Table { } } + pub fn iter(&self) -> impl Iterator { + self.rows.iter().map(|s| s.stack.last().unwrap()) + } + + pub fn iter_mut(&mut self) -> impl Iterator { + self.rows.iter_mut().map(|s| s.stack.last_mut().unwrap()) + } + + pub fn get_rows_clone(&self) -> Vec { + self.rows.iter().map(|s| s.stack.last().unwrap().clone()).collect() + } + + pub fn get_rows(&self) -> Vec<&Row> { + self.rows.iter().map(|s| s.stack.last().unwrap()).collect() + } + + pub fn get_rows_mut(&mut self) -> Vec<&mut Row> { + self.rows.iter_mut().map(|s| s.stack.last_mut().unwrap()).collect() + } + + pub fn set_rows(&mut self, rows: Vec) { + self.rows = rows.into_iter().map(|r| RowStack::new(r)).collect(); + } + + pub fn push(&mut self, row: Row) { + self.rows.push(RowStack::new(row)); + } + + pub fn commit_transaction(&mut self) { + todo!() + } + + pub fn rollback_transaction(&mut self) { + todo!() + } + pub fn get_column_from_row<'a>(&self, row: &'a Vec, column: &String) -> &'a Value { for (i, value) in row.iter().enumerate() { if self.columns[i].name == *column { diff --git a/src/db/table/select/mod.rs b/src/db/table/select/mod.rs index 46680fc..7c15502 100644 --- a/src/db/table/select/mod.rs +++ b/src/db/table/select/mod.rs @@ -1,11 +1,11 @@ mod select_statement; mod set_operator_evaluator; -use crate::db::{database::Database, table::Value}; +use crate::db::{database::Database, table::Row}; use crate::interpreter::ast::{SelectStatementStack, SetOperator, SelectStatementStackElement, SelectStatementColumns}; use crate::db::table::helpers::{order_by_clause::{perform_comparions}, limit_clause::get_limited_rows}; -pub fn select_statement_stack(database: &Database, statement: SelectStatementStack) -> Result>, String> { +pub fn select_statement_stack(database: &Database, statement: SelectStatementStack) -> Result, String> { let mut evaluator = set_operator_evaluator::SetOperatorEvaluator::new(); let statement_columns = statement.columns.columns(); let mut columns: Option> = match statement_columns { @@ -75,6 +75,7 @@ pub fn select_statement_stack(database: &Database, statement: SelectStatementSta mod tests { use super::*; use crate::db::table::test_utils::default_database; + use crate::db::table::Value; use crate::interpreter::ast::{SelectStatement, SelectStatementColumns, WhereStackElement, WhereCondition, Operand, Operator, LogicalOperator, SelectMode}; @@ -97,10 +98,10 @@ mod tests { let result = select_statement_stack(&database, statement); assert!(result.is_ok()); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], - vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)]), + Row(vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)]), ]; assert_eq!(expected, result.unwrap()); } @@ -139,7 +140,7 @@ mod tests { let result = select_statement_stack(&database, statement); assert!(result.is_ok()); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), ]; assert_eq!(expected, result.unwrap()); } @@ -197,7 +198,7 @@ mod tests { let result = select_statement_stack(&database, statement); assert!(result.is_ok()); let expected = vec![ - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), ]; assert_eq!(expected, result.unwrap()); } diff --git a/src/db/table/select/select_statement.rs b/src/db/table/select/select_statement.rs index 7bd5f4d..19b40be 100644 --- a/src/db/table/select/select_statement.rs +++ b/src/db/table/select/select_statement.rs @@ -1,11 +1,11 @@ -use crate::db::table::{Table, Value}; +use crate::db::table::{Table, Row}; use crate::interpreter::ast::{SelectStatement, SelectMode}; use crate::db::table::helpers::common::{get_row_indicies_matching_clauses, get_row_columns_from_indicies, DistinctOn}; -pub fn select_statement(table: &Table, statement: &SelectStatement) -> Result>, String> { +pub fn select_statement(table: &Table, statement: &SelectStatement) -> Result, String> { let mode = match statement.mode { SelectMode::All => None, SelectMode::Distinct => Some(DistinctOn { columns: &statement.columns }), @@ -17,7 +17,7 @@ pub fn select_statement(table: &Table, statement: &SelectStatement) -> Result>>, + pub stack: Vec>, } impl SetOperatorEvaluator { @@ -13,18 +13,18 @@ impl SetOperatorEvaluator { } } - pub fn result(&mut self) -> Result>, String> { + pub fn result(&mut self) -> Result, String> { if self.stack.len() != 1 { return Err("Error processing SELECT statement. Stack length is not 1".to_string()); } self.pop() } - pub fn push(&mut self, rows: Vec>) { + pub fn push(&mut self, rows: Vec) { self.stack.push(rows); } - fn pop(&mut self) -> Result>, String> { + fn pop(&mut self) -> Result, String> { self.stack.pop().ok_or("Error processing SELECT statement. Stack is empty".to_string()) } @@ -46,7 +46,7 @@ impl SetOperatorEvaluator { } pub fn intersect(&mut self) -> Result<(), String> { - let second = self.pop()?.into_iter().collect::>>(); + let second = self.pop()?.into_iter().collect::>(); let mut first = self.pop()?; let mut index: usize = 0; while index < first.len() { @@ -62,7 +62,7 @@ impl SetOperatorEvaluator { } pub fn except(&mut self) -> Result<(), String> { - let second = self.pop()?.into_iter().collect::>>(); + let second = self.pop()?.into_iter().collect::>(); let mut first = self.pop()?; let mut index: usize = 0; while index < first.len() { @@ -82,20 +82,21 @@ impl SetOperatorEvaluator { mod test { use super::*; use crate::db::table::test_utils::assert_table_rows_eq_unordered; + use crate::db::table::Value; - fn rows_1() -> Vec> { + fn rows_1() -> Vec { vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)]), ] } - fn rows_2() -> Vec> { + fn rows_2() -> Vec { vec![ - vec![Value::Integer(1), Value::Text("Fletcher".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Null, Value::Real(5000.0)], + Row(vec![Value::Integer(1), Value::Text("Fletcher".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Null, Value::Real(5000.0)]), ] } @@ -108,12 +109,12 @@ mod test { let result = evaluator.result(); assert!(result.is_ok()); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], - vec![Value::Integer(1), Value::Text("Fletcher".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Null, Value::Real(5000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)]), + Row(vec![Value::Integer(1), Value::Text("Fletcher".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Null, Value::Real(5000.0)]), ]; assert!(result.is_ok()); assert_table_rows_eq_unordered(expected, result.unwrap()); @@ -128,7 +129,7 @@ mod test { let result = evaluator.result(); assert!(result.is_ok()); let expected = vec![ - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), ]; assert_table_rows_eq_unordered(expected, result.unwrap()); } @@ -142,8 +143,8 @@ mod test { let result = evaluator.result(); assert!(result.is_ok()); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)]), ]; assert_table_rows_eq_unordered(expected, result.unwrap()); } diff --git a/src/db/table/test_utils.rs b/src/db/table/test_utils.rs index e7acb27..3cb2b8e 100644 --- a/src/db/table/test_utils.rs +++ b/src/db/table/test_utils.rs @@ -6,6 +6,8 @@ use crate::db::table::{Table, Value, DataType, ColumnDefinition}; use crate::interpreter::ast::OrderByDirection; #[cfg(test)] use std::cmp::Ordering; +#[cfg(test)] +use crate::db::table::{Row, RowStack}; #[cfg(test)] @@ -19,10 +21,10 @@ pub fn default_table() -> Table { ColumnDefinition {name: "money".to_string(), data_type: DataType::Real, constraints: vec![]}, ], rows: vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], - vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)], + RowStack::new(Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)])), + RowStack::new(Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)])), + RowStack::new(Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)])), + RowStack::new(Row(vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)])), ], } } @@ -39,17 +41,17 @@ pub fn default_database() -> Database { ColumnDefinition {name: "money".to_string(), data_type: DataType::Real, constraints: vec![]}, ], rows: vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], - vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)], + RowStack::new(Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)])), + RowStack::new(Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)])), + RowStack::new(Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)])), + RowStack::new(Row(vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)])), ], }); database } #[cfg(test)] -pub fn assert_table_rows_eq_unordered(mut expected: Vec>, mut actual: Vec>) { +pub fn assert_table_rows_eq_unordered(mut expected: Vec, mut actual: Vec) { expected.sort_by(|a, b| { let mut i = 0; while i < a.len() && i < b.len() && a[i].compare(&b[i], &OrderByDirection::Asc) == Ordering::Equal { diff --git a/src/db/table/update/mod.rs b/src/db/table/update/mod.rs index 1546c47..257d7d3 100644 --- a/src/db/table/update/mod.rs +++ b/src/db/table/update/mod.rs @@ -3,20 +3,20 @@ use crate::interpreter::ast::{UpdateStatement, ColumnValue}; use crate::db::table::helpers::common::get_row_indicies_matching_clauses; use crate::db::table::DataType; -pub fn update(table: &mut Table, statement: UpdateStatement) -> Result<(), String> { +pub fn update(table: &mut Table, statement: UpdateStatement) -> Result, String> { let row_indicies = get_row_indicies_matching_clauses(table, None, &statement.where_clause, &statement.order_by_clause, &statement.limit_clause)?; - update_rows_from_indicies(table, row_indicies, statement.update_values)?; - Ok(()) + update_rows_from_indicies(table, &row_indicies, statement.update_values)?; + Ok(row_indicies) } -fn update_rows_from_indicies(table: &mut Table, row_indicies: Vec, update_values: Vec) -> Result<(), String> { +fn update_rows_from_indicies(table: &mut Table, row_indicies: &Vec, update_values: Vec) -> Result<(), String> { for row_index in row_indicies { for update_value in &update_values { let column_index = table.get_index_of_column(&update_value.column)?; if table.columns[column_index].data_type != update_value.value.get_type() && update_value.value.get_type() != DataType::Null { return Err(format!("Found different data types for column: {} and value: {:?}", update_value.column, update_value.value.get_type())); } - table.rows[row_index][column_index] = update_value.value.clone(); + table[*row_index][column_index] = update_value.value.clone(); } } Ok(()) @@ -29,6 +29,7 @@ mod tests { use crate::interpreter::ast::ColumnValue; use crate::db::table::test_utils::{default_table, assert_table_rows_eq_unordered}; use crate::interpreter::ast::{WhereStackElement, WhereCondition, Operand, Operator, OrderByClause, OrderByDirection, LimitClause}; + use crate::db::table::Row; #[test] fn update_works_correctly() { @@ -43,26 +44,26 @@ mod tests { let result = update(&mut table, statement); assert!(result.is_ok()); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("John".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("John".to_string()), Value::Integer(35), Value::Real(3000.0)], - vec![Value::Integer(4), Value::Text("John".to_string()), Value::Integer(40), Value::Real(4000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("John".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("John".to_string()), Value::Integer(35), Value::Real(3000.0)]), + Row(vec![Value::Integer(4), Value::Text("John".to_string()), Value::Integer(40), Value::Real(4000.0)]), ]; - assert_table_rows_eq_unordered(expected, table.rows); + assert_table_rows_eq_unordered(expected, table.get_rows_clone()); } #[test] fn update_with_all_clauses_works_correctly() { let mut table = default_table(); - table.rows = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], - vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)], - vec![Value::Integer(5), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(6), Value::Text("John".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(7), Value::Text("John".to_string()), Value::Integer(35), Value::Real(3000.0)], - ]; + table.set_rows(vec![ + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)]), + Row(vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)]), + Row(vec![Value::Integer(5), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(6), Value::Text("John".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(7), Value::Text("John".to_string()), Value::Integer(35), Value::Real(3000.0)]), + ]); let statement = UpdateStatement { table_name: "users".to_string(), update_values: vec![ColumnValue { column: "name".to_string(), value: Value::Text("Fletcher".to_string()) }], @@ -73,15 +74,15 @@ mod tests { let result = update(&mut table, statement); assert!(result.is_ok()); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], - vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)], - vec![Value::Integer(5), Value::Text("Fletcher".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(6), Value::Text("John".to_string()), Value::Integer(30), Value::Real(2000.0)], - vec![Value::Integer(7), Value::Text("John".to_string()), Value::Integer(35), Value::Real(3000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)]), + Row(vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Real(4000.0)]), + Row(vec![Value::Integer(5), Value::Text("Fletcher".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(6), Value::Text("John".to_string()), Value::Integer(30), Value::Real(2000.0)]), + Row(vec![Value::Integer(7), Value::Text("John".to_string()), Value::Integer(35), Value::Real(3000.0)]), ]; - assert_table_rows_eq_unordered(expected, table.rows); + assert_table_rows_eq_unordered(expected, table.get_rows_clone()); } #[test] @@ -96,13 +97,15 @@ mod tests { }; let result = update(&mut table, statement); assert!(result.is_ok()); + let row_indicies = result.unwrap(); + assert_eq!(vec![1, 2, 3], row_indicies); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)], - vec![Value::Integer(2), Value::Text("Fletcher".to_string()), Value::Integer(50), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Fletcher".to_string()), Value::Integer(50), Value::Real(3000.0)], - vec![Value::Integer(4), Value::Text("Fletcher".to_string()), Value::Integer(50), Value::Real(4000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(1000.0)]), + Row(vec![Value::Integer(2), Value::Text("Fletcher".to_string()), Value::Integer(50), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("Fletcher".to_string()), Value::Integer(50), Value::Real(3000.0)]), + Row(vec![Value::Integer(4), Value::Text("Fletcher".to_string()), Value::Integer(50), Value::Real(4000.0)]), ]; - assert_table_rows_eq_unordered(expected, table.rows); + assert_table_rows_eq_unordered(expected, table.get_rows_clone()); } #[test] @@ -122,7 +125,7 @@ mod tests { let result = update(&mut table, statement); assert!(result.is_ok()); let expected = vec![]; - assert_table_rows_eq_unordered(expected, table.rows); + assert_table_rows_eq_unordered(expected, table.get_rows_clone()); } #[test] @@ -168,11 +171,11 @@ mod tests { let result = update(&mut table, statement); assert!(result.is_ok()); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Null], - vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Null], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Null], - vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Null], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Null]), + Row(vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Null]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Null]), + Row(vec![Value::Integer(4), Value::Null, Value::Integer(40), Value::Null]), ]; - assert_table_rows_eq_unordered(expected, table.rows); + assert_table_rows_eq_unordered(expected, table.get_rows_clone()); } } \ No newline at end of file diff --git a/src/interpreter/ast/mod.rs b/src/interpreter/ast/mod.rs index 3abb847..a316003 100644 --- a/src/interpreter/ast/mod.rs +++ b/src/interpreter/ast/mod.rs @@ -22,7 +22,7 @@ pub struct DatabaseSqlStatement { pub statement_text: String, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum SqlStatement { CreateTable(CreateTableStatement), InsertInto(InsertIntoStatement), @@ -38,33 +38,33 @@ pub enum SqlStatement { Release(ReleaseStatement), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct CreateTableStatement { pub table_name: String, pub existence_check: Option, pub columns: Vec, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct DropTableStatement { pub table_name: String, pub existence_check: Option, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum ExistenceCheck { // Eventually expand to temp tables IfNotExists, IfExists, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct InsertIntoStatement { pub table_name: String, pub columns: Option>, pub values: Vec>, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct SelectStatementStack { pub columns: SelectStatementColumns, pub elements: Vec, @@ -72,19 +72,19 @@ pub struct SelectStatementStack { pub limit_clause: Option, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum SelectStatementStackElement { SelectStatement(SelectStatement), SetOperator(SetOperator), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum SelectStackOperators { SetOperator(SetOperator), Parentheses(Parentheses), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum SetOperator { Union, UnionAll, @@ -102,7 +102,7 @@ impl SetOperator { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct SelectStatement { pub table_name: String, pub mode: SelectMode, @@ -112,7 +112,7 @@ pub struct SelectStatement { pub limit_clause: Option, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct DeleteStatement { pub table_name: String, pub where_clause: Option>, @@ -120,7 +120,7 @@ pub struct DeleteStatement { pub limit_clause: Option, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct UpdateStatement { pub table_name: String, pub update_values: Vec, @@ -129,13 +129,13 @@ pub struct UpdateStatement { pub limit_clause: Option, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct AlterTableStatement { pub table_name: String, pub action: AlterTableAction, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum AlterTableAction { RenameTable { new_table_name: String }, RenameColumn { old_column_name: String, new_column_name: String }, @@ -143,36 +143,36 @@ pub enum AlterTableAction { DropColumn { column_name: String }, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum BeginStatement { Deferred, Immediate, Exclusive, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct RollbackStatement { pub savepoint_name: Option, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct SavepointStatement { pub savepoint_name: String, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct ReleaseStatement { pub savepoint_name: String, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct ColumnValue { pub column: String, pub value: Value, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum SelectMode { All, Distinct, @@ -193,8 +193,7 @@ impl SelectStatementColumns { } } -#[derive(Debug, PartialEq)] -#[cfg_attr(test, derive(Clone))] +#[derive(Debug, PartialEq, Clone)] pub enum Operator { Equals, NotEquals, @@ -208,8 +207,7 @@ pub enum Operator { IsNot, } -#[derive(Debug, PartialEq)] -#[cfg_attr(test, derive(Clone))] +#[derive(Debug, PartialEq, Clone)] pub struct WhereCondition { pub l_side: Operand, pub operator: Operator, @@ -217,8 +215,7 @@ pub struct WhereCondition { } -#[derive(Debug, PartialEq)] -#[cfg_attr(test, derive(Clone))] +#[derive(Debug, PartialEq, Clone)] pub enum Operand { Value(Value), ValueList(Vec), @@ -226,14 +223,14 @@ pub enum Operand { } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum WhereStackElement { Condition(WhereCondition), LogicalOperator(LogicalOperator), Parentheses(Parentheses), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum WhereStackOperators { LogicalOperator(LogicalOperator), Parentheses(Parentheses), @@ -248,7 +245,7 @@ impl WhereStackOperators { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum LogicalOperator { Not, And, @@ -270,25 +267,25 @@ impl LogicalOperator { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Parentheses { Left, Right, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum OrderByDirection { Asc, Desc, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct OrderByClause { pub column: String, pub direction: OrderByDirection, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct LimitClause { pub limit: Value, pub offset: Option, diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index ac9c87f..5c62549 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,9 +1,9 @@ use crate::db; -use crate::db::table::Value; +use crate::db::table::Row; pub mod ast; mod tokenizer; -pub fn run_sql(database: &mut db::database::Database, sql: &str) -> Vec>>, String>> { +pub fn run_sql(database: &mut db::database::Database, sql: &str) -> Vec>, String>> { let tokens = tokenizer::tokenize(sql); // println!("{:?}", tokens); let ast = ast::generate(tokens); diff --git a/tests/crud_test.rs b/tests/crud_test.rs index 37fde89..000b4ac 100644 --- a/tests/crud_test.rs +++ b/tests/crud_test.rs @@ -1,6 +1,6 @@ use mollydb::db::database::Database; use mollydb::interpreter::run_sql; -use mollydb::db::table::Value; +use mollydb::db::table::{Value, Row}; #[test] fn test_basic_statements_crud() { @@ -22,8 +22,8 @@ fn test_basic_statements_crud() { let mut result = run_sql(&mut database, sql); assert!(result.iter().all(|result| result.is_ok())); let expected = vec![ - vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(2000.0)], - vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], + Row(vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(2000.0)]), + Row(vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)]), ]; assert_eq!(result.pop().unwrap().unwrap().unwrap(), expected); assert!(result.into_iter().all(|result| result.is_ok() && result.unwrap().is_none())); @@ -53,12 +53,12 @@ fn test_complex_statements_crud() { let mut result = run_sql(&mut database, sql); assert!(result.iter().all(|result| result.is_ok())); let expected_first = vec![ - vec![Value::Text("Jim".to_string()), Value::Integer(35)], - vec![Value::Text("Jane".to_string()), Value::Integer(30)], - vec![Value::Text("John".to_string()), Value::Integer(25)], + Row(vec![Value::Text("Jim".to_string()), Value::Integer(35)]), + Row(vec![Value::Text("Jane".to_string()), Value::Integer(30)]), + Row(vec![Value::Text("John".to_string()), Value::Integer(25)]), ]; let expected_second = vec![ - vec![Value::Integer(80)], + Row(vec![Value::Integer(80)]), ]; assert_eq!(expected_second, result.pop().unwrap().unwrap().unwrap()); assert!(result.pop().unwrap().unwrap().is_none()); @@ -154,7 +154,7 @@ fn test_alter_table() { assert!(result[0..=5].iter().all(|result| result.is_ok() && result.as_ref().unwrap().is_none())); let expected = vec![ - vec![Value::Text("John".to_string()), Value::Null], + Row(vec![Value::Text("John".to_string()), Value::Null]), ]; let row = result[6].as_ref().unwrap().as_ref().unwrap(); assert_eq!(expected, *row); @@ -187,9 +187,9 @@ fn test_distinct_mode() { let result = run_sql(&mut database, sql); assert!(result.iter().all(|result| result.is_ok())); let expected = vec![ - vec![Value::Text("Jane".to_string())], - vec![Value::Text("Jim".to_string())], - vec![Value::Text("John".to_string())], + Row(vec![Value::Text("Jane".to_string())]), + Row(vec![Value::Text("Jim".to_string())]), + Row(vec![Value::Text("John".to_string())]), ]; let row = result[5].as_ref().unwrap().as_ref().unwrap(); assert_eq!(expected, *row); diff --git a/tests/set_operators.rs b/tests/set_operators.rs index 4c0d56e..5246767 100644 --- a/tests/set_operators.rs +++ b/tests/set_operators.rs @@ -2,7 +2,7 @@ mod test_utils; use mollydb::db::database::Database; use mollydb::interpreter::run_sql; -use mollydb::db::table::Value; +use mollydb::db::table::{Value, Row}; #[test] fn test_set_operators() { @@ -18,10 +18,11 @@ fn test_set_operators() { let mut result = run_sql(&mut database, sql); assert!(result.iter().all(|result| result.is_ok())); let expected = vec![ - vec![Value::Text("John".to_string())], - vec![Value::Text("Jack".to_string())], + Row(vec![Value::Text("John".to_string())]), + Row(vec![Value::Text("Jack".to_string())]), ]; - test_utils::assert_table_rows_eq_unordered(expected, result.pop().unwrap().unwrap().unwrap()); + let row = result.pop().unwrap().unwrap().unwrap(); + test_utils::assert_table_rows_eq_unordered(expected, row); assert!(result.into_iter().all(|result| result.is_ok() && result.unwrap().is_none())); } @@ -42,11 +43,11 @@ fn test_set_operators_order_by_clauses_and_parentheses() { let mut result = run_sql(&mut database, sql); assert!(result.iter().all(|result| result.is_ok())); let expected_second = vec![ - vec![Value::Integer(3), Value::Text("Jane".to_string())], - vec![Value::Integer(2), Value::Text("zane".to_string())], + Row(vec![Value::Integer(3), Value::Text("Jane".to_string())]), + Row(vec![Value::Integer(2), Value::Text("zane".to_string())]), ]; let expected_first = vec![ - vec![Value::Integer(2), Value::Text("zane".to_string())], + Row(vec![Value::Integer(2), Value::Text("zane".to_string())]), ]; assert_eq!(expected_first, result.pop().unwrap().unwrap().unwrap()); assert_eq!(expected_second, result.pop().unwrap().unwrap().unwrap()); @@ -81,11 +82,11 @@ fn test_set_operators_with_different_tables_and_clause() { assert_eq!(expected_second, first_result.unwrap_err()); assert!(result.iter().all(|result| result.is_ok())); let expected_first = vec![ - vec![Value::Text("John".to_string())], - vec![Value::Text("Fletcher".to_string())], - vec![Value::Text("Jane".to_string())], - vec![Value::Text("Jim".to_string())], - vec![Value::Text("Jack".to_string())], + Row(vec![Value::Text("John".to_string())]), + Row(vec![Value::Text("Fletcher".to_string())]), + Row(vec![Value::Text("Jane".to_string())]), + Row(vec![Value::Text("Jim".to_string())]), + Row(vec![Value::Text("Jack".to_string())]), ]; test_utils::assert_table_rows_eq_unordered(expected_first, result.pop().unwrap().unwrap().unwrap()); assert!(result.into_iter().all(|result| result.is_ok() && result.unwrap().is_none())); diff --git a/tests/test_utils.rs b/tests/test_utils.rs index 6575f74..363cb34 100644 --- a/tests/test_utils.rs +++ b/tests/test_utils.rs @@ -1,5 +1,5 @@ #[cfg(test)] -use mollydb::db::table::Value; +use mollydb::db::table::Row; #[cfg(test)] use mollydb::interpreter::ast::OrderByDirection; #[cfg(test)] @@ -7,7 +7,7 @@ use std::cmp::Ordering; #[cfg(test)] -pub fn assert_table_rows_eq_unordered(mut expected: Vec>, mut actual: Vec>) { +pub fn assert_table_rows_eq_unordered(mut expected: Vec, mut actual: Vec) { expected.sort_by(|a, b| { let mut i = 0; while i < a.len() && i < b.len() && a[i].compare(&b[i], &OrderByDirection::Asc) == Ordering::Equal {