diff --git a/src/db/database.rs b/src/db/database.rs index 5895df9..5329690 100644 --- a/src/db/database.rs +++ b/src/db/database.rs @@ -2,6 +2,7 @@ use crate::db::table::core::{row::Row, table::Table}; use crate::db::table::operations::{ alter_table, create_table, delete, drop_table, insert, select, update, }; +use crate::db::transactions::rollback::rollback_transaction_entry; use crate::db::transactions::{TransactionEntry, TransactionLog}; use crate::interpreter::ast::SqlStatement; use std::collections::HashMap; @@ -81,10 +82,22 @@ impl Database { Ok(None) } SqlStatement::Rollback(_) => { - self.transaction.commit_transaction()?; - self.tables.iter_mut().for_each(|(_, table)| { - table.rollback_transaction(); - }); + if let Some(transaction_log) = self.transaction.commit_transaction()?.entries { + // We roll back in reverse order because of dependencies. + for transaction_entry in transaction_log.iter().rev() { + match transaction_entry { + TransactionEntry::Statement(statement) => { + // TODO: Some matching needs to be here for table based operations. + // CURRENTLY SUPPORTED STATEMENTS ARE: + // - ALTER TABLE RENAME COLUMN, ALTER TABLE ADD COLUMN, ALTER TABLE DROP COLUMN + rollback_transaction_entry(self, &statement)?; + } + TransactionEntry::Savepoint(_) => {} + } + } + } else { + return Err("No transaction is currently active".to_string()); + } Ok(None) } SqlStatement::Savepoint(_) => { @@ -159,13 +172,13 @@ mod tests { let mut database = default_database(); let table = database.get_table("users"); assert!(table.is_ok()); - assert_eq!("users", table.unwrap().name); + assert_eq!("users", table.unwrap().name().unwrap()); let table = database.get_table("not_users"); assert!(table.is_err()); assert_eq!("Table `not_users` does not exist", table.unwrap_err()); let table = database.get_table_mut("users"); assert!(table.is_ok()); - assert_eq!("users", table.unwrap().name); + assert_eq!("users", table.unwrap().name().unwrap()); let table = database.get_table_mut("not_users"); assert!(table.is_err()); assert_eq!("Table `not_users` does not exist", table.unwrap_err()); diff --git a/src/db/table/core/column.rs b/src/db/table/core/column.rs index a9c6fbb..c4d7588 100644 --- a/src/db/table/core/column.rs +++ b/src/db/table/core/column.rs @@ -40,7 +40,6 @@ impl ColumnStack { &mut self, old_column_name: &String, new_column_name: &String, - table_name: &String, is_transaction: bool, ) -> Result<(), String> { if is_transaction { @@ -53,10 +52,7 @@ impl ColumnStack { match columns { Some(column) => column.name = new_column_name.clone(), None => { - return Err(format!( - "Column `{}` does not exist in table `{}`", - old_column_name, table_name - )); + return Err("Column does not exist".to_string()); } } Ok(()) @@ -65,7 +61,6 @@ impl ColumnStack { pub fn drop_column( &mut self, column_name: &String, - table_name: &String, is_transaction: bool, ) -> Result<(), String> { if is_transaction { @@ -74,10 +69,7 @@ impl ColumnStack { match self.get_index_of_column(column_name) { Ok(index) => self.peek_mut()?.remove(index), Err(_) => { - return Err(format!( - "Column `{}` does not exist in table `{}`", - column_name, table_name - )); + return Err("Column does not exist".to_string()); } }; Ok(()) diff --git a/src/db/table/core/table.rs b/src/db/table/core/table.rs index 8389f14..97fb729 100644 --- a/src/db/table/core/table.rs +++ b/src/db/table/core/table.rs @@ -7,11 +7,16 @@ use std::ops::{Index, IndexMut}; #[derive(Debug)] pub struct Table { - pub name: String, + pub name: NameStack, pub columns: ColumnStack, rows: Vec, } +#[derive(Debug)] +pub struct NameStack { + pub stack: Vec, +} + impl Index for Table { type Output = Row; @@ -29,12 +34,27 @@ impl IndexMut for Table { impl Table { pub fn new(name: String, columns: Vec) -> Self { Self { - name, + name: NameStack { stack: vec![name] }, columns: ColumnStack::new(columns), rows: vec![], } } + pub fn name(&self) -> Result<&String, String> { + self.name + .stack + .last() + .ok_or("Error fetching table name.".to_string()) + } + + pub fn change_name(&mut self, new_name: String, is_transaction: bool) { + if is_transaction { + self.name.stack.push(new_name); + } else { + self.name.stack = vec![new_name]; + } + } + pub fn get(&self, i: usize) -> Option<&Row> { self.rows.get(i)?.stack.last() } @@ -106,60 +126,88 @@ impl Table { Ok(()) } - pub fn rollback_transaction(&mut self) { - todo!() + pub fn rollback_columns(&mut self) { + self.columns.stack.pop(); } - pub fn get_column_from_row<'a>(&self, row: &'a Vec, column: &String) -> &'a Value { + pub fn rollback_all_rows(&mut self) { + for row_stack in self.rows.iter_mut() { + row_stack.stack.pop(); + } + } + + pub fn rollback_name(&mut self) { + self.name.stack.pop(); + } + + pub fn get_column_from_row<'a>( + &self, + row: &'a Vec, + column: &String, + ) -> Result<&'a Value, String> { for (i, value) in row.iter().enumerate() { - if self.get_column_names()[i] == column { - return &value; + if self.get_column_names()?[i] == column { + return Ok(&value); } } - return &Value::Null; + return Ok(&Value::Null); } - pub fn has_column(&self, column: &String) -> bool { - self.get_columns().iter().any(|c| c.name == *column) + pub fn has_column(&self, column: &String) -> Result { + Ok(self.get_columns()?.iter().any(|c| c.name == *column)) } - pub fn width(&self) -> usize { - self.get_columns().len() + pub fn width(&self) -> Result { + Ok(self.get_columns()?.len()) } pub fn get_index_of_column(&self, column: &String) -> Result { - for (i, c) in self.get_columns().iter().enumerate() { + for (i, c) in self.get_columns()?.iter().enumerate() { if c.name == *column { return Ok(i); } } return Err(format!( "Column {} does not exist in table {}", - column, self.name + column, + self.name()? )); } - pub fn get_columns(&self) -> Vec<&ColumnDefinition> { - self.columns.stack.last().unwrap().iter().collect() + pub fn get_columns(&self) -> Result, String> { + Ok(self + .columns + .stack + .last() + .ok_or("Column stack is empty".to_string())? + .iter() + .collect()) } - pub fn get_columns_mut(&mut self) -> Vec<&mut ColumnDefinition> { - self.columns.stack.last_mut().unwrap().iter_mut().collect() + pub fn get_columns_mut(&mut self) -> Result, String> { + Ok(self + .columns + .stack + .last_mut() + .ok_or("Column stack is empty".to_string())? + .iter_mut() + .collect()) } - pub fn get_column_names(&self) -> Vec<&String> { - self.get_columns() + pub fn get_column_names(&self) -> Result, String> { + Ok(self + .get_columns()? .iter() .map(|column| &column.name) - .collect() + .collect()) } - pub fn push_column(&mut self, column: ColumnDefinition) { - self.columns.push_column(column, false); + pub fn push_column(&mut self, column: ColumnDefinition, is_transaction: bool) { + self.columns.push_column(column, is_transaction); } #[cfg(test)] - pub fn get_columns_clone(&self) -> Vec { - self.get_columns().iter().map(|c| (*c).clone()).collect() + pub fn get_columns_clone(&self) -> Result, String> { + Ok(self.get_columns()?.iter().map(|c| (*c).clone()).collect()) } } diff --git a/src/db/table/operations/alter_table/mod.rs b/src/db/table/operations/alter_table/mod.rs index c0608e0..ee9fa98 100644 --- a/src/db/table/operations/alter_table/mod.rs +++ b/src/db/table/operations/alter_table/mod.rs @@ -12,8 +12,8 @@ pub fn alter_table( let table = database.tables.remove(&statement.table_name); match table { Some(mut table) => { - table.name = new_table_name; - database.tables.insert(table.name.clone(), table); + table.change_name(new_table_name, is_transaction); + database.tables.insert(table.name()?.clone(), table); } None => return Err(format!("Table `{}` does not exist", statement.table_name)), }; @@ -24,27 +24,38 @@ pub fn alter_table( new_column_name, } => { let table = database.get_table_mut(&statement.table_name)?; - table.columns.rename_column( - &old_column_name, - &new_column_name, - &table.name, - is_transaction, - ) + if !table.has_column(&old_column_name)? { + return Err(format!( + "Column `{}` does not exist in table `{}`", + old_column_name, statement.table_name + )); + } + let res = + table + .columns + .rename_column(&old_column_name, &new_column_name, is_transaction); + if res.is_err() { + return Err(format!( + "Error renaming column: `{}` to `{}` in Table: `{}`", + old_column_name, new_column_name, statement.table_name + )); + } + Ok(()) } AlterTableAction::AddColumn { column_def } => { let table = database.get_table_mut(&statement.table_name)?; - if table.has_column(&column_def.name) { + if table.has_column(&column_def.name)? { return Err(format!( "Column `{}` already exists in table `{}`", column_def.name, statement.table_name )); } - table.push_column(column_def); if is_transaction { table.get_row_stacks_mut().iter_mut().for_each(|row_stack| { row_stack.append_clone(); }); } + table.push_column(column_def, is_transaction); table.get_rows_mut().iter_mut().for_each(|row| { row.push(Value::Null); }); @@ -52,16 +63,20 @@ pub fn alter_table( } AlterTableAction::DropColumn { column_name } => { let table = database.get_table_mut(&statement.table_name)?; - if !table.has_column(&column_name) { + if !table.has_column(&column_name)? { return Err(format!( "Column `{}` does not exist in table `{}`", column_name, statement.table_name )); } let index = table.columns.get_index_of_column(&column_name)?; - table - .columns - .drop_column(&column_name, &table.name, is_transaction)?; + let res = table.columns.drop_column(&column_name, is_transaction); + if res.is_err() { + return Err(format!( + "Error dropping column: `{}` from Table: `{}`", + column_name, statement.table_name + )); + } // 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. if is_transaction { @@ -100,7 +115,7 @@ mod tests { assert!(result.is_ok()); assert!(!database.tables.contains_key("users")); assert!(database.tables.contains_key("new_users")); - assert!(database.tables.get("new_users").unwrap().name == "new_users"); + assert!(database.tables.get("new_users").unwrap().name().unwrap() == "new_users"); } #[test] @@ -117,13 +132,9 @@ mod tests { assert!(result.is_ok()); let table = database.get_table("users"); assert!(table.is_ok()); - assert!( - table - .unwrap() - .get_columns() - .iter() - .any(|column| column.name == "new_name") - ); + let table_columns = table.unwrap().get_columns().unwrap(); + assert!(table_columns.iter().any(|column| column.name == "new_name")); + assert!(table_columns.iter().any(|column| column.name == "new_name")); } #[test] @@ -144,8 +155,9 @@ mod tests { let table = database.get_table("users"); assert!(table.is_ok()); let table = table.unwrap(); - assert!(table.get_columns().last().unwrap().name == "new_column"); - assert!(table.get_columns().len() == table[0].len()); + let table_columns = table.get_columns().unwrap(); + assert!(table_columns.last().unwrap().name == "new_column"); + assert!(table_columns.len() == table[0].len()); assert!( table .get_rows() @@ -168,13 +180,10 @@ mod tests { let table = database.get_table("users"); assert!(table.is_ok()); let table = table.unwrap(); - assert!( - !table - .get_columns() - .iter() - .any(|column| column.name == "age") - ); - assert!(table.get_columns().len() == table[0].len()); + let table_columns = table.get_columns().unwrap(); + assert!(!table_columns.iter().any(|column| column.name == "age")); + let table_columns_len = table_columns.len(); + assert!(table_columns_len == table[0].len()); let expected_columns_in_order = vec![ ColumnDefinition { name: "id".to_string(), @@ -192,7 +201,10 @@ mod tests { constraints: vec![], }, ]; - assert_eq!(expected_columns_in_order, table.get_columns_clone()); + assert_eq!( + expected_columns_in_order, + table.get_columns_clone().unwrap() + ); let expected_rows = vec![ Row(vec![ Value::Integer(1), @@ -229,12 +241,8 @@ mod tests { let table = database.get_table("users"); assert!(table.is_ok()); let table = table.unwrap(); - assert!( - table - .get_columns() - .iter() - .any(|column| column.name == "new_name") - ); + let table_columns = table.get_columns().unwrap(); + assert!(table_columns.iter().any(|column| column.name == "new_name")); let index_of_column = table.get_index_of_column(&"new_name".to_string()).unwrap(); assert!(table.columns.stack.len() == 2); assert!(table.columns.stack[0][index_of_column].name == "name"); @@ -255,12 +263,8 @@ mod tests { let table = database.get_table("users"); assert!(table.is_ok()); let table = table.unwrap(); - assert!( - !table - .get_columns() - .iter() - .any(|column| column.name == "age") - ); + let table_columns = table.get_columns().unwrap(); + assert!(!table_columns.iter().any(|column| column.name == "age")); assert!(table.columns.stack.len() == 2); let expected_column_names = vec![ vec![ @@ -336,4 +340,138 @@ mod tests { ]; assert_eq!(expected_row_stacks, table.get_row_stacks_clone()); } + + #[test] + fn alter_table_add_column_works_correctly_with_transaction() { + let mut database = default_database(); + let statement = AlterTableStatement { + table_name: "users".to_string(), + action: AlterTableAction::AddColumn { + column_def: ColumnDefinition { + name: "new_column".to_string(), + data_type: DataType::Integer, + constraints: vec![], + }, + }, + }; + let result = alter_table(&mut database, statement, true); + assert!(result.is_ok()); + let table = database.get_table("users"); + assert!(table.is_ok()); + let table = table.unwrap(); + let table_columns = table.get_columns().unwrap(); + assert!(table_columns.last().unwrap().name == "new_column"); + assert!(table_columns.len() == table[0].len()); + assert!(table.columns.stack.len() == 2); + let expected_column_names = vec![ + vec![ + "id".to_string(), + "name".to_string(), + "age".to_string(), + "money".to_string(), + ], + vec![ + "id".to_string(), + "name".to_string(), + "age".to_string(), + "money".to_string(), + "new_column".to_string(), + ], + ]; + assert_eq!( + expected_column_names, + table + .columns + .stack + .iter() + .map(|column| column + .iter() + .map(|column| column.name.clone()) + .collect::>()) + .collect::>>() + ); + let expected_row_stacks = vec![ + RowStack::new_with_stack(vec![ + Row(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), + Value::Null, + ]), + ]), + RowStack::new_with_stack(vec![ + Row(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), + Value::Null, + ]), + ]), + RowStack::new_with_stack(vec![ + Row(vec![ + Value::Integer(3), + Value::Text("Jim".to_string()), + Value::Integer(35), + Value::Real(3000.0), + ]), + Row(vec![ + Value::Integer(3), + Value::Text("Jim".to_string()), + Value::Integer(35), + Value::Real(3000.0), + Value::Null, + ]), + ]), + RowStack::new_with_stack(vec![ + Row(vec![ + Value::Integer(4), + Value::Null, + Value::Integer(40), + Value::Real(4000.0), + ]), + Row(vec![ + Value::Integer(4), + Value::Null, + Value::Integer(40), + Value::Real(4000.0), + Value::Null, + ]), + ]), + ]; + assert_eq!(expected_row_stacks, table.get_row_stacks_clone()); + } + + #[test] + fn alter_table_rename_table_works_correctly_with_transaction() { + let mut database = default_database(); + let statement = AlterTableStatement { + table_name: "users".to_string(), + action: AlterTableAction::RenameTable { + new_table_name: "new_users".to_string(), + }, + }; + let result = alter_table(&mut database, statement, true); + assert!(result.is_ok()); + let table = database.get_table("new_users"); + assert!(table.is_ok()); + let expected_table_name_stack = vec!["users".to_string(), "new_users".to_string()]; + let table = table.unwrap(); + for (i, name) in expected_table_name_stack.iter().enumerate() { + assert_eq!(*name, table.name.stack[i].clone()); + } + } } diff --git a/src/db/table/operations/create_table/mod.rs b/src/db/table/operations/create_table/mod.rs index d61d419..8336be9 100644 --- a/src/db/table/operations/create_table/mod.rs +++ b/src/db/table/operations/create_table/mod.rs @@ -17,7 +17,7 @@ pub fn create_table( } } let table = Table::new(statement.table_name, statement.columns); - database.tables.insert(table.name.clone(), table); + database.tables.insert(table.name()?.clone(), table); Ok(()) } diff --git a/src/db/table/operations/helpers/common.rs b/src/db/table/operations/helpers/common.rs index 2c6228b..8c6f892 100644 --- a/src/db/table/operations/helpers/common.rs +++ b/src/db/table/operations/helpers/common.rs @@ -10,18 +10,18 @@ use crate::interpreter::ast::{ }; pub fn validate_and_clone_row(table: &Table, row: &Row) -> Result { - if row.len() != table.width() { + if row.len() != table.width()? { return Err(format!("Rows have incorrect width")); } let mut row_values: Row = Row(vec![]); for (i, value) in row.iter().enumerate() { - if value.get_type() != table.get_columns()[i].data_type + if value.get_type() != table.get_columns()?[i].data_type && value.get_type() != DataType::Null { return Err(format!( "Data type mismatch for column {}", - table.get_columns()[i].name + table.get_columns()?[i].name )); } row_values.push(row[i].clone()); @@ -37,13 +37,13 @@ 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_column_names().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 { return Err(format!( "Row does not have the expected number of columns (expected: {}, got: {}", - table.get_columns().len(), + table.get_columns()?.len(), row.len() )); } diff --git a/src/db/table/operations/helpers/where_clause/where_condition.rs b/src/db/table/operations/helpers/where_clause/where_condition.rs index f096765..52d09c7 100644 --- a/src/db/table/operations/helpers/where_clause/where_condition.rs +++ b/src/db/table/operations/helpers/where_clause/where_condition.rs @@ -103,13 +103,14 @@ fn operand_to_value<'a>( match operand { Operand::Value(value) => Ok(value), Operand::Identifier(column) => { - if !table.has_column(column) { + if !table.has_column(column)? { return Err(format!( "Column {} does not exist in table {}", - column, table.name + column, + table.name()? )); } - Ok(table.get_column_from_row(row, column)) + Ok(table.get_column_from_row(row, column)?) } _ => Err(format!("Found invalid operand: {:?}", operand)), } diff --git a/src/db/table/operations/insert/mod.rs b/src/db/table/operations/insert/mod.rs index 944c339..17fe9a9 100644 --- a/src/db/table/operations/insert/mod.rs +++ b/src/db/table/operations/insert/mod.rs @@ -9,7 +9,7 @@ pub fn insert(table: &mut Table, statement: InsertIntoStatement) -> Result Result { let table = database.get_table(&select_statement.table_name)?; let expanded_column_names = - expand_all_column_names(table, &select_statement.column_names); + expand_all_column_names(table, &select_statement.column_names)?; match &column_names { Some(column_names) => { if expanded_column_names.len() != column_names.len() { @@ -118,11 +118,14 @@ pub fn select_statement_stack( } // TODO: add this logic in evaluation too -fn expand_all_column_names(table: &Table, column_names: &Vec) -> Vec { +fn expand_all_column_names( + table: &Table, + column_names: &Vec, +) -> Result, String> { let mut new = vec![]; for column in column_names { if *column == "*".to_string() { - for name in table.get_column_names() { + for name in table.get_column_names()? { if !column_names.contains(name) { new.push(name.clone()); } @@ -131,7 +134,7 @@ fn expand_all_column_names(table: &Table, column_names: &Vec) -> Vec Result<(), String> { + match &statement.statement { + SqlStatement::AlterTable(alter_table) => match alter_table.action { + AlterTableAction::RenameColumn { .. } => { + let table = database.get_table_mut(statement.table_name.as_str())?; + table.rollback_columns(); + } + AlterTableAction::AddColumn { .. } => { + let table = database.get_table_mut(statement.table_name.as_str())?; + table.rollback_columns(); + table.rollback_all_rows(); + } + AlterTableAction::DropColumn { .. } => { + let table = database.get_table_mut(statement.table_name.as_str())?; + table.rollback_columns(); + table.rollback_all_rows(); + } + AlterTableAction::RenameTable { ref new_table_name } => { + // It is now under the new name + let mut table = database + .tables + .remove(new_table_name.as_str()) + .ok_or(format!("Table `{}` does not exist", new_table_name))?; + table.rollback_name(); + database.tables.insert(table.name()?.clone(), table); + } + }, + SqlStatement::Select(_) => {} // These should be kept in the log but obv do nothing. + _ => return Err("UNSUPPORTED".to_string()), + } + return Ok(()); +} diff --git a/tests/crud_test.rs b/tests/crud_test.rs index af894f2..8b12839 100644 --- a/tests/crud_test.rs +++ b/tests/crud_test.rs @@ -74,7 +74,7 @@ fn test_complex_statements_crud() { Row(vec![Value::Text("John".to_string()), Value::Integer(25)]), ]; let expected_second = vec![Row(vec![Value::Integer(80)])]; - test_utils::assert_table_rows_eq_unordered( + test_utils::assert_eq_table_rows_unordered( expected_second, result.pop().unwrap().unwrap().unwrap(), ); diff --git a/tests/set_operators.rs b/tests/set_operators_test.rs similarity index 97% rename from tests/set_operators.rs rename to tests/set_operators_test.rs index e1570d3..261038c 100644 --- a/tests/set_operators.rs +++ b/tests/set_operators_test.rs @@ -22,7 +22,7 @@ fn test_set_operators() { Row(vec![Value::Text("Jack".to_string())]), ]; let row = result.pop().unwrap().unwrap().unwrap(); - test_utils::assert_table_rows_eq_unordered(expected, row); + test_utils::assert_eq_table_rows_unordered(expected, row); assert!( result .into_iter() @@ -97,7 +97,7 @@ fn test_set_operators_with_different_tables_and_clause() { Row(vec![Value::Text("Jim".to_string())]), Row(vec![Value::Text("Jack".to_string())]), ]; - test_utils::assert_table_rows_eq_unordered( + test_utils::assert_eq_table_rows_unordered( expected_first, result.pop().unwrap().unwrap().unwrap(), ); diff --git a/tests/test_utils.rs b/tests/test_utils.rs index edd43c1..936ba97 100644 --- a/tests/test_utils.rs +++ b/tests/test_utils.rs @@ -1,12 +1,9 @@ -#[cfg(test)] use mollycache::db::table::core::row::Row; -#[cfg(test)] use mollycache::interpreter::ast::OrderByDirection; -#[cfg(test)] use std::cmp::Ordering; -#[cfg(test)] -pub fn assert_table_rows_eq_unordered(mut expected: Vec, mut actual: Vec) { +#[allow(dead_code)] // For some reason it can't pick up that this is used in integration tests. I'm prolly doing smth wrong. +pub fn assert_eq_table_rows_unordered(mut expected: Vec, mut actual: Vec) { expected.sort_by(|a, b| { let mut i = 0; while i < a.len() diff --git a/tests/transaction_test.rs b/tests/transaction_test.rs new file mode 100644 index 0000000..2eef1e3 --- /dev/null +++ b/tests/transaction_test.rs @@ -0,0 +1,64 @@ +mod test_utils; + +use mollycache::db::database::Database; +use mollycache::db::table::core::{row::Row, value::Value}; +use mollycache::interpreter::run_sql; + +#[test] +fn test_transaction() { + let mut database = Database::new(); + let sql = " + CREATE TABLE users ( + id INTEGER, + name TEXT + ); + INSERT INTO users (id, name) VALUES (1, 'John'); + SELECT * FROM users; + BEGIN; + ALTER TABLE users ADD COLUMN age INTEGER; + SELECT * FROM users; + ALTER TABLE users RENAME COLUMN age TO new_age; + SELECT new_age FROM users; + ALTER TABLE users DROP COLUMN name; + SELECT * FROM users; + ALTER TABLE users RENAME TO new_users; + SELECT * FROM new_users; + SELECT * FROM users; + ROLLBACK; + SELECT * FROM users; + SELECT * FROM new_users; + "; + let result = run_sql(&mut database, sql); + println!("{:?}", result); + let expected = vec![ + Ok(None), + Ok(None), + Ok(Some(vec![Row(vec![ + Value::Integer(1), + Value::Text("John".to_string()), + ])])), + Ok(None), + Ok(None), + Ok(Some(vec![Row(vec![ + Value::Integer(1), + Value::Text("John".to_string()), + Value::Null, + ])])), + Ok(None), + Ok(Some(vec![Row(vec![Value::Null])])), + Ok(None), + Ok(Some(vec![Row(vec![Value::Integer(1), Value::Null])])), + Ok(None), + Ok(Some(vec![Row(vec![Value::Integer(1), Value::Null])])), + Err("Execution Error with statement starting on line 17 \n Error: Table `users` does not exist".to_string()), + Ok(None), + Ok(Some(vec![Row(vec![ + Value::Integer(1), + Value::Text("John".to_string()), + ])])), + Err("Execution Error with statement starting on line 20 \n Error: Table `new_users` does not exist".to_string()), + ]; + for (i, result) in result.iter().enumerate() { + assert_eq!(expected[i], *result); + } +}