diff --git a/minisql/src/error.rs b/minisql/src/error.rs index 6000c0e..6660f9d 100644 --- a/minisql/src/error.rs +++ b/minisql/src/error.rs @@ -1,17 +1,11 @@ use crate::internals::row::ColumnPosition; use crate::schema::{ColumnName, TableName}; -use crate::operation::InsertionValues; use crate::type_system::{DbType, Uuid, Value}; #[derive(Debug)] pub enum Error { - TableDoesNotExist(TableName), - ColumnDoesNotExist(TableName, ColumnName), ColumnPositionDoesNotExist(TableName, ColumnPosition), ValueDoesNotMatchExpectedType(TableName, ColumnName, DbType, Value), AttemptingToInsertAlreadyPresentId(TableName, Uuid), - MissingTypeAnnotationOfColumn(TableName, ColumnPosition), - MissingColumnInInsertValues(TableName, ColumnName, InsertionValues), - MismatchBetweenInsertValuesAndColumns(TableName, InsertionValues), AttemptToIndexNonIndexableColumn(TableName, ColumnName), } diff --git a/minisql/src/internals/row.rs b/minisql/src/internals/row.rs index c0c81c0..508ca40 100644 --- a/minisql/src/internals/row.rs +++ b/minisql/src/internals/row.rs @@ -1,5 +1,5 @@ use crate::type_system::Value; -use crate::operation::InsertionValuesForInterpreter; +use crate::operation::InsertionValues; use std::ops::{Index, IndexMut}; use std::slice::SliceIndex; @@ -43,7 +43,7 @@ impl Row { Row(vec![]) } - pub fn new_from_insertion_values(insertion_values: InsertionValuesForInterpreter) -> Self { + pub fn new_from_insertion_values(insertion_values: InsertionValues) -> Self { Row(insertion_values) } diff --git a/minisql/src/internals/table.rs b/minisql/src/internals/table.rs index 2b5a5bc..ac6ca5a 100644 --- a/minisql/src/internals/table.rs +++ b/minisql/src/internals/table.rs @@ -206,6 +206,7 @@ impl Table { match value { IndexableValue::Uuid(id) => Ok(Some(HashSet::from([*id]))), _ => { + // TODO: This validation step is not really necessary. let column_name: ColumnName = self .schema .column_name_from_column_position(column_position)?; @@ -222,8 +223,8 @@ impl Table { match self.indexes.get(&column_position) { Some(index) => { // Note that we are cloning the ids here! This can be very wasteful in some cases. - // It would be possible to just return a reference, - // but this seems fairly non-trivial. + // Theoretically it would be possible to return a reference, + // but after attempting to do this it seems very non-trivial. let ids = index.get(value).cloned(); Ok(ids) } diff --git a/minisql/src/interpreter.rs b/minisql/src/interpreter.rs index 70393cc..b96dc40 100644 --- a/minisql/src/interpreter.rs +++ b/minisql/src/interpreter.rs @@ -1,10 +1,8 @@ -use crate::error::Error; -use crate::internals::row::{ColumnPosition, Row}; +use crate::internals::row::Row; use crate::schema::{TableName, TableSchema}; use crate::internals::table::Table; -use crate::operation::{ColumnSelection, Condition, Operation, OperationForInterpreter, ConditionForInterpreter}; +use crate::operation::{Operation, Condition}; use crate::result::DbResult; -use crate::type_system::{DbType, IndexableValue, Value}; use bimap::BiMap; // Use `TablePosition` as index @@ -57,7 +55,6 @@ impl State { } } - /// TODO: return a reference to avoid allocations pub fn db_schema<'a>(&'a self) -> DbSchema { let mut schema: DbSchema = Vec::new(); for (table_name, &table_position) in &self.table_name_position_mapping { @@ -67,34 +64,10 @@ impl State { schema } - // TODO: Get rid of this - fn table_from_name<'a>(&'a self, table_name: &TableName) -> DbResult<&'a Table> { - match self.table_name_position_mapping.get_by_left(table_name) { - Some(table_position) => { - let table = &self.tables[*table_position]; - Ok(table) - } - None => Err(Error::TableDoesNotExist(table_name.clone())), - } - } - fn table_at<'a>(&'a self, table_position: TablePosition) -> &'a Table { &self.tables[table_position] } - fn table_from_name_mut<'b: 'a, 'a>( - &'b mut self, - table_name: &TableName, - ) -> DbResult<&'a mut Table> { - match self.table_name_position_mapping.get_by_left(table_name) { - Some(table_position) => { - let table = &mut self.tables[*table_position]; - Ok(table) - } - None => Err(Error::TableDoesNotExist(table_name.clone())), - } - } - fn table_at_mut<'a>(&'a mut self, table_position: TablePosition) -> &'a mut Table { &mut self.tables[table_position] } @@ -106,9 +79,9 @@ impl State { self.tables.push(table); } - pub fn interpret_for_interpreter<'a>(&'a mut self, operation: OperationForInterpreter) -> DbResult> { + pub fn interpret<'a>(&'a mut self, operation: Operation) -> DbResult> { // TODO: lock stuff - use OperationForInterpreter::*; + use Operation::*; match operation { Select(table_position, column_selection, maybe_condition) => { @@ -120,7 +93,7 @@ impl State { Box::new(x) as Box + 'a + Send> }, - Some(ConditionForInterpreter::Eq(eq_column, value)) => { + Some(Condition::Eq(eq_column, value)) => { let x = table.select_rows_where_eq( column_selection, @@ -136,7 +109,7 @@ impl State { Insert(table_position, values) => { let table: &mut Table = self.table_at_mut(table_position); - let (id, row) = table.schema().row_from_insertion_values_for_interpreter(values)?; + let (id, row) = table.schema().row_from_insertion_values(values)?; table.insert_row_at(id, row)?; Ok(Response::Inserted) } @@ -145,7 +118,7 @@ impl State { let rows_affected = match maybe_condition { None => table.delete_all_rows(), - Some(ConditionForInterpreter::Eq(eq_column, value)) => { + Some(Condition::Eq(eq_column, value)) => { table.delete_rows_where_eq(eq_column, value)? } }; @@ -165,85 +138,15 @@ impl State { } } } - - pub fn interpret<'a>(&'a mut self, operation: Operation) -> DbResult> { - // TODO: lock stuff - use Operation::*; - - match operation { - Select(table_name, column_selection, maybe_condition) => { - let table: &Table = self.table_from_name(&table_name)?; - - let selected_column_positions: Vec = table - .schema() - .column_positions_from_column_selection(&column_selection)?; - let selected_rows = match maybe_condition { - None => { - let x = table.select_all_rows(selected_column_positions); - Box::new(x) as Box + 'a + Send> - }, - - Some(Condition::Eq(eq_column_name, value)) => { - let eq_column_position = table - .schema() - .column_position_from_column_name(&eq_column_name)?; - let x = - table.select_rows_where_eq( - selected_column_positions, - eq_column_position, - value, - )?; - Box::new(x) as Box + 'a + Send> - } - }; - - Ok(Response::Selected(selected_rows)) - } - Insert(table_name, values) => { - let table: &mut Table = self.table_from_name_mut(&table_name)?; - - let (id, row) = table.schema().row_from_insertion_values(values)?; - table.insert_row_at(id, row)?; - Ok(Response::Inserted) - } - Delete(table_name, maybe_condition) => { - let table: &mut Table = self.table_from_name_mut(&table_name)?; - - let rows_affected = match maybe_condition { - None => table.delete_all_rows(), - Some(Condition::Eq(eq_column_name, value)) => { - let eq_column_position = table - .schema() - .column_position_from_column_name(&eq_column_name)?; - table.delete_rows_where_eq(eq_column_position, value)? - } - }; - - Ok(Response::Deleted(rows_affected)) - } - CreateTable(table_name, table_schema) => { - let table = Table::new(table_schema); - self.attach_table(table_name, table); - - Ok(Response::TableCreated) - } - CreateIndex(table_name, column_name) => { - let table: &mut Table = self.table_from_name_mut(&table_name)?; - let column_position: ColumnPosition = table - .schema() - .column_position_from_column_name(&column_name)?; - - table.attach_index(column_position)?; - Ok(Response::IndexCreated) - } - } - } } #[cfg(test)] mod tests { use super::*; + use crate::internals::row::ColumnPosition; use std::collections::HashSet; + use crate::type_system::{DbType, IndexableValue, Value}; + use crate::operation::Operation; fn users_schema() -> TableSchema { let id: ColumnPosition = 0; @@ -284,12 +187,13 @@ mod tests { let mut state = State::new(); let users_schema = users_schema(); let users = users_schema.table_name().clone(); + let users_position = 0; state - .interpret(Operation::CreateTable(users.clone(), users_schema)) + .interpret(Operation::CreateTable(users, users_schema.clone())) .unwrap(); let response: Response = state - .interpret(Operation::Select(users.clone(), ColumnSelection::All, None)) + .interpret(Operation::Select(users_position, users_schema.all_selection(), None)) .unwrap(); assert!(matches!(response, Response::Selected(_))); let Response::Selected(rows) = response else { @@ -299,18 +203,6 @@ mod tests { assert!(rows.len() == 0); } - #[test] - fn test_select_nonexistant_table() { - let mut state = State::new(); - - let response: DbResult = state.interpret(Operation::Select( - "table_that_doesnt_exist".to_string(), - ColumnSelection::All, - None, - )); - assert!(matches!(response, Err(Error::TableDoesNotExist(_)))); - } - #[test] fn test_insert_select_basic1() { use IndexableValue::*; @@ -318,10 +210,11 @@ mod tests { let mut state = State::new(); let users_schema = users_schema(); - let users = users_schema.table_name().clone(); + let users = 0; + state - .interpret(Operation::CreateTable(users.clone(), users_schema)) + .interpret(Operation::CreateTable("users".to_string(), users_schema.clone())) .unwrap(); let (id, name, age) = ( @@ -331,17 +224,17 @@ mod tests { ); state .interpret(Operation::Insert( - users.clone(), + users, vec![ - ("id".to_string(), id.clone()), - ("name".to_string(), name.clone()), - ("age".to_string(), age.clone()), + id.clone(), + name.clone(), + age.clone(), ], )) .unwrap(); let response: Response = state - .interpret(Operation::Select(users.clone(), ColumnSelection::All, None)) + .interpret(Operation::Select(users, users_schema.all_selection(), None)) .unwrap(); assert!(matches!(response, Response::Selected(_))); @@ -351,7 +244,7 @@ mod tests { let rows: Vec<_> = rows.collect(); assert!(rows.len() == 1); let row = &rows[0]; - + assert!(row.len() == 3); assert!(row[0] == id); assert!(row[1] == name); @@ -360,7 +253,6 @@ mod tests { #[test] fn test_insert_select_basic2() { - use ColumnSelection::*; use Condition::*; use IndexableValue::*; use Operation::*; @@ -368,10 +260,13 @@ mod tests { let mut state = State::new(); let users_schema = users_schema(); - let users = users_schema.table_name().clone(); + let users_position: TablePosition = 0; + + let id_column: ColumnPosition = 0; + let name_column: ColumnPosition = 1; state - .interpret(CreateTable(users.clone(), users_schema)) + .interpret(CreateTable(users_schema.table_name().clone(), users_schema.clone())) .unwrap(); let (id0, name0, age0) = ( @@ -381,11 +276,11 @@ mod tests { ); state .interpret(Insert( - users.clone(), + users_position, vec![ - ("id".to_string(), id0.clone()), - ("name".to_string(), name0.clone()), - ("age".to_string(), age0.clone()), + id0.clone(), + name0.clone(), + age0.clone(), ], )) .unwrap(); @@ -397,17 +292,17 @@ mod tests { ); state .interpret(Insert( - users.clone(), + users_position, vec![ - ("id".to_string(), id1.clone()), - ("name".to_string(), name1.clone()), - ("age".to_string(), age1.clone()), + id1.clone(), + name1.clone(), + age1.clone(), ], )) .unwrap(); { - let response: Response = state.interpret(Select(users.clone(), All, None)).unwrap(); + let response: Response = state.interpret(Select(users_position, users_schema.all_selection(), None)).unwrap(); assert!(matches!(response, Response::Selected(_))); let Response::Selected(rows) = response else { @@ -432,9 +327,9 @@ mod tests { { let response: Response = state .interpret(Select( - users.clone(), - All, - Some(Eq("id".to_string(), id0.clone())), + users_position, + users_schema.all_selection(), + Some(Eq(id_column, id0.clone())), )) .unwrap(); assert!(matches!(response, Response::Selected(_))); @@ -454,9 +349,9 @@ mod tests { { let response: Response = state .interpret(Select( - users.clone(), - Columns(vec!["name".to_string(), "id".to_string()]), - Some(Eq("id".to_string(), id0.clone())), + users_position, + vec![name_column, id_column], + Some(Eq(id_column, id0.clone())), )) .unwrap(); assert!(matches!(response, Response::Selected(_))); @@ -475,7 +370,6 @@ mod tests { #[test] fn test_delete() { - use ColumnSelection::*; use Condition::*; use IndexableValue::*; use Operation::*; @@ -483,10 +377,12 @@ mod tests { let mut state = State::new(); let users_schema = users_schema(); - let users = users_schema.table_name().clone(); + let users_position: TablePosition = 0; + + let id_column: ColumnPosition = 0; state - .interpret(CreateTable(users.clone(), users_schema)) + .interpret(CreateTable(users_schema.table_name().clone(), users_schema.clone())) .unwrap(); let (id0, name0, age0) = ( @@ -496,11 +392,11 @@ mod tests { ); state .interpret(Insert( - users.clone(), + users_position, vec![ - ("id".to_string(), id0.clone()), - ("name".to_string(), name0.clone()), - ("age".to_string(), age0.clone()), + id0.clone(), + name0.clone(), + age0.clone(), ], )) .unwrap(); @@ -512,11 +408,11 @@ mod tests { ); state .interpret(Insert( - users.clone(), + users_position, vec![ - ("id".to_string(), id1.clone()), - ("name".to_string(), name1.clone()), - ("age".to_string(), age1.clone()), + id1.clone(), + name1.clone(), + age1.clone(), ], )) .unwrap(); @@ -524,14 +420,14 @@ mod tests { { let delete_response: Response = state .interpret(Delete( - users.clone(), - Some(Eq("id".to_string(), id0.clone())), + users_position, + Some(Eq(id_column, id0.clone())), )) .unwrap(); assert!(matches!(delete_response, Response::Deleted(1))); } - let response: Response = state.interpret(Select(users.clone(), All, None)).unwrap(); + let response: Response = state.interpret(Select(users_position, users_schema.all_selection(), None)).unwrap(); assert!(matches!(response, Response::Selected(_))); let Response::Selected(rows) = response else { @@ -555,14 +451,16 @@ mod tests { let mut state = State::new(); let users_schema = users_schema(); - let users = users_schema.table_name().clone(); + let users_position: TablePosition = 0; + + let name_column: ColumnPosition = 1; state - .interpret(CreateTable(users.clone(), users_schema)) + .interpret(CreateTable(users_schema.table_name().clone(), users_schema.clone())) .unwrap(); state - .interpret(CreateIndex(users.clone(), "name".to_string())) + .interpret(CreateIndex(users_position, name_column)) .unwrap(); let (id0, name0, age0) = ( @@ -572,11 +470,11 @@ mod tests { ); state .interpret(Insert( - users.clone(), + users_position, vec![ - ("id".to_string(), id0.clone()), - ("name".to_string(), name0.clone()), - ("age".to_string(), age0.clone()), + id0.clone(), + name0.clone(), + age0.clone(), ], )) .unwrap(); @@ -588,11 +486,11 @@ mod tests { ); state .interpret(Insert( - users.clone(), + users_position, vec![ - ("id".to_string(), id1.clone()), - ("name".to_string(), name1.clone()), - ("age".to_string(), age1.clone()), + id1.clone(), + name1.clone(), + age1.clone(), ], )) .unwrap(); @@ -617,33 +515,35 @@ mod tests { } pub fn example() { - use ColumnSelection::*; + use crate::type_system::{IndexableValue, Value, DbType}; + use crate::internals::row::ColumnPosition; use Condition::*; use IndexableValue::*; use Operation::*; use Value::*; - let users_schema: TableSchema = { - let id: ColumnPosition = 0; - let name: ColumnPosition = 1; - let age: ColumnPosition = 2; + let id_column: ColumnPosition = 0; + let name_column: ColumnPosition = 1; + let age_column: ColumnPosition = 2; + let users_schema: TableSchema = { TableSchema::new( "users".to_string(), - id, + id_column, vec!( - ("id".to_string(), id), - ("name".to_string(), name), - ("age".to_string(), age), + ("id".to_string(), id_column), + ("name".to_string(), name_column), + ("age".to_string(), age_column), ), vec![DbType::Uuid, DbType::String, DbType::Int], ) }; + let users_position: TablePosition = 0; let users = users_schema.table_name().clone(); let mut state = State::new(); state - .interpret(Operation::CreateTable(users.clone(), users_schema)) + .interpret(Operation::CreateTable(users, users_schema.clone())) .unwrap(); let (id0, name0, age0) = ( @@ -654,11 +554,11 @@ pub fn example() { println!("==INSERT Plato=="); state .interpret(Insert( - users.clone(), + users_position, vec![ - ("id".to_string(), id0.clone()), - ("name".to_string(), name0.clone()), - ("age".to_string(), age0.clone()), + id0.clone(), + name0.clone(), + age0.clone(), ], )) .unwrap(); @@ -671,11 +571,11 @@ pub fn example() { println!("==INSERT Aristotle=="); state .interpret(Insert( - users.clone(), + users_position, vec![ - ("id".to_string(), id1.clone()), - ("name".to_string(), name1.clone()), - ("age".to_string(), age1.clone()), + id1.clone(), + name1.clone(), + age1.clone(), ], )) .unwrap(); @@ -683,7 +583,7 @@ pub fn example() { { let response: Response = state - .interpret(Operation::Select(users.clone(), ColumnSelection::All, None)) + .interpret(Operation::Select(users_position, users_schema.all_selection(), None)) .unwrap(); println!("==SELECT ALL=="); println!("{:?}", response); @@ -692,9 +592,9 @@ pub fn example() { { let response: Response = state .interpret(Select( - users.clone(), - All, - Some(Eq("id".to_string(), id0.clone())), + users_position, + users_schema.all_selection(), + Some(Eq(id_column, id0.clone())), )) .unwrap(); println!("==SELECT Plato=="); @@ -708,16 +608,16 @@ pub fn example() { // "infer" them? let _delete_response: Response = state .interpret(Delete( - users.clone(), - Some(Eq("id".to_string(), id0.clone())), + users_position, + Some(Eq(id_column, id0.clone())), )) .unwrap(); println!("==DELETE Plato=="); } let response: Response = state .interpret(Select( - users.clone(), - Columns(vec!["name".to_string(), "id".to_string()]), + users_position, + vec![name_column, id_column], None, )) .unwrap(); diff --git a/minisql/src/operation.rs b/minisql/src/operation.rs index 332aff0..6bbf918 100644 --- a/minisql/src/operation.rs +++ b/minisql/src/operation.rs @@ -1,58 +1,21 @@ -use crate::schema::{ColumnName, TableName, TableSchema}; +use crate::schema::{TableName, TableSchema}; use crate::type_system::Value; use crate::internals::row::ColumnPosition; use crate::interpreter::TablePosition; -// ==============SQL operations================ -// TODO: Note that every operation has a table name. -// Perhaps consider factoring the table name out -// and think of the operations as operating on a unique table. -// TODO: `TableName` should be replaced by `TablePosition` +// Validated operation. Constructed by validation crate. pub enum Operation { - Select(TableName, ColumnSelection, Option), - Insert(TableName, InsertionValues), - Delete(TableName, Option), - // Update(...), - CreateTable(TableName, TableSchema), - CreateIndex(TableName, ColumnName), - // DropTable(TableName), -} - -pub type InsertionValues = Vec<(ColumnName, Value)>; - -pub enum ColumnSelection { - All, - Columns(Vec), -} - -pub enum Condition { - // And(Box, Box), - // Or(Box, Box), - // Not(Box), - Eq(ColumnName, Value), - // LessOrEqual(ColumnName, DbValue), - // Less(ColumnName, DbValue), - - // StringCondition(StringCondition), -} - -// enum StringCondition { -// Prefix(ColumnName, String), -// Substring(ColumnName, String), -// } - -pub enum OperationForInterpreter { - Select(TablePosition, ColumnSelectionForInterpreter, Option), - Insert(TablePosition, InsertionValuesForInterpreter), - Delete(TablePosition, Option), + Select(TablePosition, ColumnSelection, Option), + Insert(TablePosition, InsertionValues), + Delete(TablePosition, Option), CreateTable(TableName, TableSchema), CreateIndex(TablePosition, ColumnPosition), } -pub type InsertionValuesForInterpreter = Vec; +pub type InsertionValues = Vec; -pub type ColumnSelectionForInterpreter = Vec; +pub type ColumnSelection = Vec; -pub enum ConditionForInterpreter { +pub enum Condition { Eq(ColumnPosition, Value), } diff --git a/minisql/src/schema.rs b/minisql/src/schema.rs index 0d44b76..97cd87b 100644 --- a/minisql/src/schema.rs +++ b/minisql/src/schema.rs @@ -1,14 +1,13 @@ use crate::error::Error; use crate::internals::row::{ColumnPosition, Row}; -use crate::operation::{ColumnSelection, InsertionValues, InsertionValuesForInterpreter, ColumnSelectionForInterpreter}; +use crate::operation::{InsertionValues, ColumnSelection}; use crate::result::DbResult; use crate::type_system::{DbType, IndexableValue, Uuid, Value}; use bimap::BiMap; -use std::collections::HashMap; // Note that it is nice to split metadata from the data because // then you can give the metadata to the parser without giving it the data. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TableSchema { table_name: TableName, // used for descriptive errors primary_key: ColumnPosition, @@ -48,12 +47,13 @@ impl TableSchema { self.column_name_position_mapping.get_by_left(column_name).copied() } - pub fn all_selection(&self) -> ColumnSelectionForInterpreter { - self.column_name_position_mapping.iter().map(|(_, column)| *column).collect() + pub fn all_selection(&self) -> ColumnSelection { + let mut selection: ColumnSelection = self.column_name_position_mapping.iter().map(|(_, column)| *column).collect(); + selection.sort(); + selection } - // TODO: Rename to get_column - pub fn get_column0(&self, column_name: &ColumnName) -> Option<(ColumnPosition, DbType)> { + pub fn get_column(&self, column_name: &ColumnName) -> Option<(ColumnPosition, DbType)> { let column = self.get_column_position(column_name)?; Some((column, self.column_type(column))) } @@ -63,48 +63,10 @@ impl TableSchema { self.types.get(position).copied() } - // TODO: Get rid of this after validation is merged - fn get_column(&self, column_name: &ColumnName) -> DbResult<(DbType, ColumnPosition)> { - match self.column_name_position_mapping.get_by_left(column_name) { - Some(column_position) => match self.types.get(*column_position) { - Some(type_) => Ok((*type_, *column_position)), - None => Err(Error::MissingTypeAnnotationOfColumn( - self.table_name.clone(), - *column_position, - )), - }, - None => Err(Error::ColumnDoesNotExist( - self.table_name.clone(), - column_name.clone(), - )), - } - } - - // TODO: Get rid of this after validation is merged - pub fn column_position_from_column_name( - &self, - column_name: &ColumnName, - ) -> DbResult { - self.get_column(column_name) - .map(|(_, column_position)| column_position) - } - pub fn is_primary(&self, column_position: ColumnPosition) -> bool { self.primary_key == column_position } - fn column_positions_from_column_names( - &self, - column_names: &[ColumnName], - ) -> DbResult> { - let mut positions: Vec = Vec::with_capacity(column_names.len()); - for column_name in column_names { - let column_position = self.column_position_from_column_name(column_name)?; - positions.push(column_position) - } - Ok(positions) - } - pub fn column_name_from_column_position( &self, column_position: ColumnPosition, @@ -121,34 +83,13 @@ impl TableSchema { } } - pub fn column_positions_from_column_selection( - &self, - column_selection: &ColumnSelection, - ) -> DbResult> { - match column_selection { - ColumnSelection::All => { - let mut column_positions: Vec = self - .column_name_position_mapping - .iter() - .map(|(_, column_position)| *column_position) - .collect(); - column_positions.sort(); - Ok(column_positions) - } - - ColumnSelection::Columns(column_names) => { - self.column_positions_from_column_names(column_names) - } - } - } - pub fn number_of_columns(&self) -> usize { self.column_name_position_mapping.len() } - pub fn row_from_insertion_values_for_interpreter( + pub fn row_from_insertion_values( &self, - insertion_values: InsertionValuesForInterpreter, + insertion_values: InsertionValues, ) -> DbResult<(Uuid, Row)> { let row: Row = Row::new_from_insertion_values(insertion_values); @@ -160,51 +101,4 @@ impl TableSchema { Ok((id, row)) } - - pub fn row_from_insertion_values( - &self, - insertion_values: InsertionValues, - ) -> DbResult<(Uuid, Row)> { - // TODO: There should be proper validation of the insertion_values. - // And it shouldn't really be done here. - // - // In the below we don't check for duplicate column names - // - let number_of_columns = self.number_of_columns(); - if number_of_columns != insertion_values.len() { - return Err(Error::MismatchBetweenInsertValuesAndColumns( - self.table_name.clone(), - insertion_values, - )); - } - - let mut row: Row = Row::with_number_of_columns(number_of_columns); - - let mut values: HashMap = HashMap::new(); - for (column_name, db_value) in &insertion_values { - values.insert(column_name.clone(), db_value.clone()); - } - - for column_position in 0..number_of_columns { - let column_name: ColumnName = self.column_name_from_column_position(column_position)?; - match values.get(&column_name) { - Some(db_value) => row.push(db_value.clone()), - None => { - return Err(Error::MissingColumnInInsertValues( - self.table_name.clone(), - column_name, - insertion_values, - )) - } - } - } - - let id: Uuid = match row.get(self.primary_key) { - Some(Value::Indexable(IndexableValue::Uuid(id))) => *id, - Some(_) => unreachable!(), - None => unreachable!(), - }; - - Ok((id, row)) - } } diff --git a/parser/src/core.rs b/parser/src/core.rs index f173f38..2cd2432 100644 --- a/parser/src/core.rs +++ b/parser/src/core.rs @@ -1,4 +1,4 @@ -use minisql::{operation::OperationForInterpreter, interpreter::DbSchema}; +use minisql::{operation::Operation, interpreter::DbSchema}; use crate::syntax::RawQuerySyntax; use nom::{branch::alt, multi::many0, IResult}; use thiserror::Error; @@ -29,7 +29,7 @@ pub fn parse_statements<'a>(input: &'a str) -> IResult<&str, Vec many0(parse_statement)(input) } -pub fn parse_and_validate(query: String, db_schema: &DbSchema) -> Result { +pub fn parse_and_validate(query: String, db_schema: &DbSchema) -> Result { let (_, op) = parse_statement(query.as_str()) .map_err(|err| { Error::ParsingError(err.to_string()) diff --git a/parser/src/parsing/create.rs b/parser/src/parsing/create.rs index e950f98..c82df31 100644 --- a/parser/src/parsing/create.rs +++ b/parser/src/parsing/create.rs @@ -100,8 +100,8 @@ mod tests { RawQuerySyntax::CreateTable(name, schema) => { assert_eq!(name, "Table1"); assert_eq!(schema.number_of_columns(), 2); - assert_eq!(schema.column_position_from_column_name(&"id".to_string()).unwrap(), 0); - assert_eq!(schema.column_position_from_column_name(&"column1".to_string()).unwrap(), 1); + assert_eq!(schema.get_column_position(&"id".to_string()).unwrap(), 0); + assert_eq!(schema.get_column_position(&"column1".to_string()).unwrap(), 1); } _ => {} } diff --git a/parser/src/validation.rs b/parser/src/validation.rs index 9e6a79f..0f60f00 100644 --- a/parser/src/validation.rs +++ b/parser/src/validation.rs @@ -2,8 +2,10 @@ use std::collections::HashSet; use std::collections::HashMap; use thiserror::Error; -use crate::syntax::{ColumnSelection, Condition, InsertionValues, RawQuerySyntax}; -use minisql::{operation::{ColumnSelectionForInterpreter, ConditionForInterpreter, InsertionValuesForInterpreter, OperationForInterpreter}, type_system::Value, schema::{TableSchema, ColumnName, TableName}, type_system::DbType, interpreter::{TablePosition, DbSchema}}; +use crate::syntax; +use crate::syntax::RawQuerySyntax; +use minisql::operation; +use minisql::{operation::Operation, type_system::Value, schema::{TableSchema, ColumnName, TableName}, type_system::DbType, interpreter::{TablePosition, DbSchema}}; #[derive(Debug, Error)] pub enum ValidationError { @@ -26,7 +28,7 @@ pub enum ValidationError { } /// Validates and converts the raw syntax into a proper interpreter operation based on db schema. -pub fn validate_operation(query: RawQuerySyntax, db_schema: &DbSchema) -> Result { +pub fn validate_operation(query: RawQuerySyntax, db_schema: &DbSchema) -> Result { match query { RawQuerySyntax::Select(table_name, column_selection, condition) => { validate_select(table_name, column_selection, condition, db_schema) @@ -52,7 +54,7 @@ fn validate_table_exists<'a>(db_schema: &DbSchema<'a>, table_name: &'a TableName .map(|(_, table_position, table_schema)| (*table_position, *table_schema)) } -pub fn validate_create(table_name: TableName, table_schema: TableSchema, db_schema: &DbSchema) -> Result { +pub fn validate_create(table_name: TableName, table_schema: TableSchema, db_schema: &DbSchema) -> Result { if let Some(_) = get_table_schema(db_schema, &table_name) { return Err(ValidationError::TableAlreadyExists(table_name.to_string())); } @@ -64,13 +66,13 @@ pub fn validate_create(table_name: TableName, table_schema: TableSchema, db_sche )?; // TODO: Ensure it has a primary key?? - Ok(OperationForInterpreter::CreateTable(table_name, table_schema)) + Ok(Operation::CreateTable(table_name, table_schema)) } -pub fn validate_select(table_name: TableName, column_selection: ColumnSelection, condition: Option, db_schema: &DbSchema) -> Result { +pub fn validate_select(table_name: TableName, column_selection: syntax::ColumnSelection, condition: Option, db_schema: &DbSchema) -> Result { let (table_position, schema) = validate_table_exists(db_schema, &table_name)?; match column_selection { - ColumnSelection::Columns(columns) => { + syntax::ColumnSelection::Columns(columns) => { let non_existant_columns: Vec = columns.iter().filter_map(|column| if schema.does_column_exist(&column) { @@ -81,20 +83,20 @@ pub fn validate_select(table_name: TableName, column_selection: ColumnSelection, if non_existant_columns.len() > 0 { Err(ValidationError::ColumnsDoNotExist(non_existant_columns)) } else { - let selection: ColumnSelectionForInterpreter = + let selection: operation::ColumnSelection = columns.iter().filter_map(|column_name| schema.get_column_position(column_name)).collect(); let validated_condition = validate_condition(condition, schema)?; - Ok(OperationForInterpreter::Select(table_position, selection, validated_condition)) + Ok(Operation::Select(table_position, selection, validated_condition)) } } - ColumnSelection::All => { + syntax::ColumnSelection::All => { let validated_condition = validate_condition(condition, schema)?; - Ok(OperationForInterpreter::Select(table_position, schema.all_selection(), validated_condition)) + Ok(Operation::Select(table_position, schema.all_selection(), validated_condition)) } } } -pub fn validate_insert(table_name: TableName, insertion_values: InsertionValues, db_schema: &DbSchema) -> Result { +pub fn validate_insert(table_name: TableName, insertion_values: syntax::InsertionValues, db_schema: &DbSchema) -> Result { let (table_position, schema) = validate_table_exists(db_schema, &table_name)?; // Check for duplicate columns in insertion_values. @@ -120,7 +122,7 @@ pub fn validate_insert(table_name: TableName, insertion_values: InsertionValues, // Check types and prepare for creation of InsertionValues for the interpreter let mut values_map: HashMap<_, Value> = HashMap::new(); for (column_name, value) in insertion_values { - let (column, expected_type) = schema.get_column0(&column_name).ok_or(ValidationError::ColumnsDoNotExist(vec![column_name.to_string()]))?; // By the previous validation steps this is never gonna trigger an error. + let (column, expected_type) = schema.get_column(&column_name).ok_or(ValidationError::ColumnsDoNotExist(vec![column_name.to_string()]))?; // By the previous validation steps this is never gonna trigger an error. let value_type = value.to_type(); if value_type != expected_type { return Err(ValidationError::TypeMismatch { column_name: column_name.to_string(), received_type: value_type, expected_type }); @@ -129,26 +131,26 @@ pub fn validate_insert(table_name: TableName, insertion_values: InsertionValues, } // These are values ordered by the column position - let values: InsertionValuesForInterpreter = values_map.into_values().collect(); + let values: operation::InsertionValues = values_map.into_values().collect(); - Ok(OperationForInterpreter::Insert(table_position, values)) + Ok(Operation::Insert(table_position, values)) } -pub fn validate_delete(table_name: TableName, condition: Option, db_schema: &DbSchema) -> Result { +pub fn validate_delete(table_name: TableName, condition: Option, db_schema: &DbSchema) -> Result { let (table_position, schema) = validate_table_exists(db_schema, &table_name)?; let validated_condition = validate_condition(condition, schema)?; - Ok(OperationForInterpreter::Delete(table_position, validated_condition)) + Ok(Operation::Delete(table_position, validated_condition)) } -fn validate_condition(condition: Option, schema: &TableSchema) -> Result, ValidationError> { +fn validate_condition(condition: Option, schema: &TableSchema) -> Result, ValidationError> { match condition { Some(condition) => { match condition { - Condition::Eq(column_name, value) => { - let (column, expected_type) = schema.get_column0(&column_name).ok_or(ValidationError::ColumnsDoNotExist(vec![column_name.to_string()]))?; + syntax::Condition::Eq(column_name, value) => { + let (column, expected_type) = schema.get_column(&column_name).ok_or(ValidationError::ColumnsDoNotExist(vec![column_name.to_string()]))?; let value_type: DbType = value.to_type(); if expected_type.eq(&value_type) { - Ok(Some(ConditionForInterpreter::Eq(column, value))) + Ok(Some(operation::Condition::Eq(column, value))) } else { return Err(ValidationError::TypeMismatch { column_name: column_name.to_string(), received_type: value_type, expected_type }); } @@ -159,27 +161,27 @@ fn validate_condition(condition: Option, schema: &TableSchema) -> Res } } -fn validate_create_index(table_name: TableName, column_name: ColumnName, db_schema: &DbSchema) -> Result { +fn validate_create_index(table_name: TableName, column_name: ColumnName, db_schema: &DbSchema) -> Result { // TODO: You should disallow indexing of Number columns. let (table_position, schema) = validate_table_exists(db_schema, &table_name)?; schema .get_column_position(&column_name) .map_or_else( || Err(ValidationError::ColumnsDoNotExist(vec![column_name.to_string()])), - |column| Ok(OperationForInterpreter::CreateIndex(table_position, column)) + |column| Ok(Operation::CreateIndex(table_position, column)) ) } // ===Helpers=== -fn find_first_duplicate(xs: &[A]) -> Option<&A> -where A: Eq + std::hash::Hash +fn find_first_duplicate(ts: &[T]) -> Option<&T> +where T: Eq + std::hash::Hash { - let mut already_seen_elements: HashSet<&A> = HashSet::new(); - for x in xs { - if already_seen_elements.contains(x) { - return Some(x); + let mut already_seen_elements: HashSet<&T> = HashSet::new(); + for t in ts { + if already_seen_elements.contains(t) { + return Some(t); } else { - already_seen_elements.insert(&x); + already_seen_elements.insert(&t); } } None diff --git a/server/src/main.rs b/server/src/main.rs index fcbb6bf..add7b2b 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -56,7 +56,7 @@ async fn handle_stream(mut stream: TcpStream) -> anyhow::Result<()> { let db_schema = state.db_schema(); match parse_and_validate(data.query.as_str().to_string(), &db_schema) { Ok(operation) => { - match state.interpret_for_interpreter(operation) { + match state.interpret(operation) { Ok(_) => { send_query_response(&mut writer).await?; }