From e0876bb0f164d9293809abac9fe1a4cffcf4428f Mon Sep 17 00:00:00 2001 From: Yuriy Dupyn <2153100+omedusyo@users.noreply.github.com> Date: Mon, 11 Dec 2023 20:07:04 +0100 Subject: [PATCH] First attempt at UPDATE --- Cargo.lock | 9 ++++ Cargo.toml | 1 + src/main.rs | 123 ++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 124 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e87788c..7254440 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + [[package]] name = "minisql" version = "0.1.0" +dependencies = [ + "bimap", +] diff --git a/Cargo.toml b/Cargo.toml index df68143..1a108f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bimap = "0.6.3" diff --git a/src/main.rs b/src/main.rs index da02d7b..91035a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use std::collections::{BTreeMap, HashMap, HashSet}; +use bimap::BiMap; // ==============SQL operations================ // TODO: Note that every operation has a table name. @@ -112,7 +113,8 @@ struct ColumnIndex { struct TableSchema { table_name: TableName, // used for descriptive errors primary_key: ColumnPosition, - columns: HashMap + column_name_position_mapping: BiMap, + types: Vec, } // TODO @@ -219,8 +221,17 @@ impl ColumnIndex { impl TableSchema { fn get_column(&self, column_name: &ColumnName) -> DbResult<(DbType, ColumnPosition)> { - match self.columns.get(column_name) { - Some((type_, column_position)) => Ok((*type_, *column_position)), + 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())) } } @@ -238,10 +249,17 @@ impl TableSchema { Ok(positions) } + fn column_name_from_column_position(&self, column_position: ColumnPosition) -> DbResult { + match self.column_name_position_mapping.get_by_right(&column_position) { + Some(column_name) => Ok(column_name.clone()), + None => Err(Error::ColumnPositionDoesNotExist(self.table_name.clone(), column_position)) + } + } + fn column_positions_from_column_selection(&self, column_selection: &ColumnSelection) -> DbResult> { match column_selection { ColumnSelection::All => { - let mut column_positions: Vec = self.columns.values().map(|(_, column_position)| *column_position).collect(); + let mut column_positions: Vec = self.column_name_position_mapping.iter().map(|(_, column_position)| *column_position).collect(); column_positions.sort(); Ok(column_positions) }, @@ -252,6 +270,57 @@ impl TableSchema { } } + fn number_of_columns(&self) -> usize { + self.column_name_position_mapping.len() + } + + 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: Vec = Vec::with_capacity(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 = match row.get(self.primary_key) { + Some(val) => { + match val { + DbValue::Indexable(IndexableDbValue::UUID(id)) => { + id + }, + _ => + unreachable!() + } + }, + None => + unreachable!() + }; + + Ok((*id, row)) + } + } impl Table { @@ -324,10 +393,25 @@ impl Table { } } - fn insert(&mut self, values: InsertionValues) { - // 1. You need to update indices - // 2. you simply insert the data - todo!() + fn insert(&mut self, values: InsertionValues) -> DbResult<()> { + let (id, row) = self.schema.row_from_insertion_values(values)?; + + if self.rows.get(&id).is_some() { + return Err(Error::AttemptingToInsertAlreadyPresentId(self.schema.table_name.clone(), id)) + } + + for (column_position, column_index) in &mut self.indexes { + match row.get(*column_position) { + Some(DbValue::Indexable(val)) => { + column_index.add(val.clone(), id) + }, + Some(_) => {}, + None => return Err(Error::ColumnPositionDoesNotExist(self.schema.table_name.clone(), *column_position)) + } + } + + let _ = self.rows.insert(id, row); + Ok(()) } fn delete_where(&mut self, maybe_condition: Option) { @@ -344,6 +428,21 @@ impl ColumnIndex { None => HashSet::new(), } } + + fn add(&mut self, value: IndexableDbValue, id: UUID) { + match self.index.get_mut(&value) { + Some(ids) => { + ids.insert(id); + }, + None => { + self.index.insert(value, HashSet::from([id])); + } + } + } + + fn remove(&mut self, id: UUID) { + todo!() + } } enum Response { @@ -354,9 +453,15 @@ enum Response { type DbResult = Result; +// #[derive(Debug)] enum Error { ColumnDoesNotExist(TableName, ColumnName), - ValueDoesNotMatchExpectedType(TableName, ColumnName, DbType, DbValue) + ColumnPositionDoesNotExist(TableName, ColumnPosition), + ValueDoesNotMatchExpectedType(TableName, ColumnName, DbType, DbValue), + AttemptingToInsertAlreadyPresentId(TableName, UUID), + MissingTypeAnnotationOfColumn(TableName, ColumnPosition), + MissingColumnInInsertValues(TableName, ColumnName, InsertionValues), + MismatchBetweenInsertValuesAndColumns(TableName, InsertionValues), } fn main() {