diff --git a/src/db/database.rs b/src/db/database.rs index bd95578..5995f8b 100644 --- a/src/db/database.rs +++ b/src/db/database.rs @@ -84,10 +84,12 @@ impl Database { Ok(None) } SqlStatement::Commit => { - self.transaction = None; - self.tables.iter_mut().for_each(|(_, table)| { - table.commit_transaction(); - }); + if let Some(transaction) = self.transaction.take() { + for transaction_entry in transaction.entries.iter() { + let table = self.get_table_mut(transaction_entry.table_name.as_str())?; + table.commit_transaction(&transaction_entry.affected_rows)?; + } + } Ok(None) } SqlStatement::Rollback(_) => { diff --git a/src/db/table/alter_table/mod.rs b/src/db/table/alter_table/mod.rs index 00a2273..36d478d 100644 --- a/src/db/table/alter_table/mod.rs +++ b/src/db/table/alter_table/mod.rs @@ -5,6 +5,7 @@ use crate::interpreter::ast::{AlterTableAction, AlterTableStatement}; pub fn alter_table(database: &mut Database, statement: AlterTableStatement) -> Result<(), String> { return match statement.action { AlterTableAction::RenameTable { new_table_name } => { + // TODO: add transaction support to table name changes let table = database.tables.remove(&statement.table_name); match table { Some(table) => database.tables.insert(new_table_name, table), @@ -13,6 +14,7 @@ pub fn alter_table(database: &mut Database, statement: AlterTableStatement) -> R Ok(()) } AlterTableAction::RenameColumn { + // TODO: add transaction support to column name changes old_column_name, new_column_name, } => { @@ -23,7 +25,7 @@ pub fn alter_table(database: &mut Database, statement: AlterTableStatement) -> R old_column_name, statement.table_name )); } - table.columns.iter_mut().for_each(|column| { + table.get_columns_mut().iter_mut().for_each(|column| { if column.name == old_column_name { column.name = new_column_name.clone(); } @@ -38,7 +40,7 @@ pub fn alter_table(database: &mut Database, statement: AlterTableStatement) -> R column_def.name, statement.table_name )); } - table.columns.push(column_def); + table.push_column(column_def); table.get_rows_mut().iter_mut().for_each(|row| { row.push(Value::Null); }); @@ -102,7 +104,7 @@ mod tests { assert!( table .unwrap() - .columns + .get_columns() .iter() .any(|column| column.name == "new_name") ); @@ -126,7 +128,7 @@ mod tests { let table = database.get_table("users"); assert!(table.is_ok()); let table = table.unwrap(); - assert!(table.columns.last().unwrap().name == "new_column"); + assert!(table.get_columns().last().unwrap().name == "new_column"); assert!(table.columns.len() == table[0].len()); assert!( table @@ -150,7 +152,12 @@ mod tests { let table = database.get_table("users"); assert!(table.is_ok()); let table = table.unwrap(); - assert!(!table.columns.iter().any(|column| column.name == "age")); + assert!( + !table + .get_columns() + .iter() + .any(|column| column.name == "age") + ); assert!(table.columns.len() == table[0].len()); let expected_columns_in_order = vec![ ColumnDefinition { @@ -169,7 +176,7 @@ mod tests { constraints: vec![], }, ]; - assert_eq!(expected_columns_in_order, table.columns); + assert_eq!(expected_columns_in_order, table.get_columns_clone()); let expected_rows = vec![ Row(vec![ Value::Integer(1), diff --git a/src/db/table/helpers/common.rs b/src/db/table/helpers/common.rs index 556361f..a3712b6 100644 --- a/src/db/table/helpers/common.rs +++ b/src/db/table/helpers/common.rs @@ -16,10 +16,12 @@ pub fn validate_and_clone_row(table: &Table, row: &Row) -> Result { 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 { + if value.get_type() != table.get_columns()[i].data_type + && value.get_type() != DataType::Null + { return Err(format!( "Data type mismatch for column {}", - table.columns[i].name + table.get_columns()[i].name )); } row_values.push(row[i].clone()); @@ -35,7 +37,7 @@ pub fn get_columns_from_row( let mut row_values: Row = Row(vec![]); let mut column_values = HashMap::new(); - for (i, column) in table.get_columns().into_iter().enumerate() { + for (i, column) in table.get_column_names().into_iter().enumerate() { if let Some(value) = row.get(i) { column_values.insert(column, value); } else { diff --git a/src/db/table/insert/mod.rs b/src/db/table/insert/mod.rs index 50dfc0d..00f18a1 100644 --- a/src/db/table/insert/mod.rs +++ b/src/db/table/insert/mod.rs @@ -8,7 +8,12 @@ pub fn insert(table: &mut Table, statement: InsertIntoStatement) -> Result Result, -} - -#[derive(Debug, PartialEq, Clone)] -pub struct ColumnConstraint { - pub constraint_type: String, -} - #[derive(Debug, PartialOrd, Clone)] pub enum Value { Integer(i64), @@ -182,10 +170,35 @@ impl RowStack { } } +#[derive(Debug, PartialEq, Clone)] +pub struct ColumnDefinition { + pub name: String, + pub data_type: DataType, + pub constraints: Vec, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct ColumnConstraint { + pub constraint_type: String, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct ColumnStack { + pub stack: Vec, +} + +impl ColumnStack { + pub fn new(column: ColumnDefinition) -> Self { + Self { + stack: vec![column], + } + } +} + #[derive(Debug)] pub struct Table { pub name: String, - pub columns: Vec, + columns: Vec, rows: Vec, } @@ -203,13 +216,11 @@ impl IndexMut for Table { } } -// TODO: add swap -// TODO: add pop impl Table { pub fn new(name: String, columns: Vec) -> Self { Self { name, - columns, + columns: columns.into_iter().map(|c| ColumnStack::new(c)).collect(), rows: vec![], } } @@ -264,8 +275,16 @@ impl Table { self.rows.pop().and_then(|mut value| value.stack.pop()) } - pub fn commit_transaction(&mut self) { - todo!() + pub fn commit_transaction(&mut self, affected_row_indices: &Vec) -> Result<(), String> { + // Keep only the top of the each row stack. + for index in affected_row_indices { + if let Some(row_stack) = self.rows.get_mut(*index) { + row_stack.stack = vec![row_stack.stack.last().unwrap().clone()]; + } else { + return Err("Error committing transaction. Row stack is empty".to_string()); + } + } + Ok(()) } pub fn rollback_transaction(&mut self) { @@ -274,7 +293,7 @@ impl Table { 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 { + if self.get_column_names()[i] == column { return &value; } } @@ -282,7 +301,7 @@ impl Table { } pub fn has_column(&self, column: &String) -> bool { - self.columns.iter().any(|c| c.name == *column) + self.get_columns().iter().any(|c| c.name == *column) } fn width(&self) -> usize { @@ -290,7 +309,7 @@ impl Table { } pub fn get_index_of_column(&self, column: &String) -> Result { - for (i, c) in self.columns.iter().enumerate() { + for (i, c) in self.get_columns().iter().enumerate() { if c.name == *column { return Ok(i); } @@ -301,7 +320,33 @@ impl Table { )); } - pub fn get_columns(&self) -> Vec<&String> { - self.columns.iter().map(|column| &column.name).collect() + pub fn get_columns(&self) -> Vec<&ColumnDefinition> { + self.columns + .iter() + .map(|c| c.stack.last().unwrap()) + .collect() + } + + pub fn get_columns_mut(&mut self) -> Vec<&mut ColumnDefinition> { + self.columns + .iter_mut() + .map(|c| c.stack.last_mut().unwrap()) + .collect() + } + + pub fn get_column_names(&self) -> Vec<&String> { + self.get_columns() + .iter() + .map(|column| &column.name) + .collect() + } + + pub fn push_column(&mut self, column: ColumnDefinition) { + self.columns.push(ColumnStack::new(column)); + } + + #[cfg(test)] + pub fn get_columns_clone(&self) -> Vec { + self.get_columns().iter().map(|c| (*c).clone()).collect() } } diff --git a/src/db/table/select/mod.rs b/src/db/table/select/mod.rs index 7fa58e2..28ca11b 100644 --- a/src/db/table/select/mod.rs +++ b/src/db/table/select/mod.rs @@ -119,7 +119,7 @@ fn expand_all_column_names(table: &Table, column_names: &Vec) -> Vec Result Table { Table { name: "users".to_string(), columns: vec![ - ColumnDefinition { - name: "id".to_string(), - data_type: DataType::Integer, - constraints: vec![], - }, - ColumnDefinition { + ColumnStack::new({ + ColumnDefinition { + name: "id".to_string(), + data_type: DataType::Integer, + constraints: vec![], + } + }), + ColumnStack::new(ColumnDefinition { name: "name".to_string(), data_type: DataType::Text, constraints: vec![], - }, - ColumnDefinition { + }), + ColumnStack::new(ColumnDefinition { name: "age".to_string(), data_type: DataType::Integer, constraints: vec![], - }, - ColumnDefinition { + }), + ColumnStack::new(ColumnDefinition { name: "money".to_string(), data_type: DataType::Real, constraints: vec![], - }, + }), ], rows: vec![ RowStack::new(Row(vec![ @@ -72,26 +74,26 @@ pub fn default_database() -> Database { Table { name: "users".to_string(), columns: vec![ - ColumnDefinition { + ColumnStack::new(ColumnDefinition { name: "id".to_string(), data_type: DataType::Integer, constraints: vec![], - }, - ColumnDefinition { + }), + ColumnStack::new(ColumnDefinition { name: "name".to_string(), data_type: DataType::Text, constraints: vec![], - }, - ColumnDefinition { + }), + ColumnStack::new(ColumnDefinition { name: "age".to_string(), data_type: DataType::Integer, constraints: vec![], - }, - ColumnDefinition { + }), + ColumnStack::new(ColumnDefinition { name: "money".to_string(), data_type: DataType::Real, constraints: vec![], - }, + }), ], rows: vec![ RowStack::new(Row(vec![ diff --git a/src/db/table/update/mod.rs b/src/db/table/update/mod.rs index 8e7c0af..56ee3cf 100644 --- a/src/db/table/update/mod.rs +++ b/src/db/table/update/mod.rs @@ -22,7 +22,7 @@ fn update_rows_from_indicies( 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() + if table.get_columns()[column_index].data_type != update_value.value.get_type() && update_value.value.get_type() != DataType::Null { return Err(format!(