format & clippy

This commit is contained in:
Yuriy Dupyn 2023-12-29 07:58:35 +01:00
parent 4f2c864d7a
commit 25b26acde0
10 changed files with 444 additions and 271 deletions

View file

@ -1,7 +1,7 @@
use crate::internals::schema::{ColumnName, TableName};
use crate::internals::row::ColumnPosition; use crate::internals::row::ColumnPosition;
use crate::type_system::{DbType, Value, UUID}; use crate::internals::schema::{ColumnName, TableName};
use crate::operation::InsertionValues; use crate::operation::InsertionValues;
use crate::type_system::{DbType, Uuid, Value};
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -9,7 +9,7 @@ pub enum Error {
ColumnDoesNotExist(TableName, ColumnName), ColumnDoesNotExist(TableName, ColumnName),
ColumnPositionDoesNotExist(TableName, ColumnPosition), ColumnPositionDoesNotExist(TableName, ColumnPosition),
ValueDoesNotMatchExpectedType(TableName, ColumnName, DbType, Value), ValueDoesNotMatchExpectedType(TableName, ColumnName, DbType, Value),
AttemptingToInsertAlreadyPresentId(TableName, UUID), AttemptingToInsertAlreadyPresentId(TableName, Uuid),
MissingTypeAnnotationOfColumn(TableName, ColumnPosition), MissingTypeAnnotationOfColumn(TableName, ColumnPosition),
MissingColumnInInsertValues(TableName, ColumnName, InsertionValues), MissingColumnInInsertValues(TableName, ColumnName, InsertionValues),
MismatchBetweenInsertValuesAndColumns(TableName, InsertionValues), MismatchBetweenInsertValuesAndColumns(TableName, InsertionValues),

View file

@ -1,9 +1,9 @@
use crate::type_system::{IndexableValue, Uuid};
use std::collections::{BTreeMap, HashSet}; use std::collections::{BTreeMap, HashSet};
use crate::type_system::{UUID, IndexableValue};
#[derive(Debug)] #[derive(Debug)]
pub struct ColumnIndex { pub struct ColumnIndex {
index: BTreeMap<IndexableValue, HashSet<UUID>> index: BTreeMap<IndexableValue, HashSet<Uuid>>,
} }
impl ColumnIndex { impl ColumnIndex {
@ -12,34 +12,30 @@ impl ColumnIndex {
Self { index } Self { index }
} }
pub fn get(&self, value: &IndexableValue) -> HashSet<UUID> { pub fn get(&self, value: &IndexableValue) -> HashSet<Uuid> {
match self.index.get(value) { match self.index.get(value) {
Some(set) => set.clone(), Some(set) => set.clone(),
None => HashSet::new(), None => HashSet::new(),
} }
} }
pub fn add(&mut self, value: IndexableValue, id: UUID) { pub fn add(&mut self, value: IndexableValue, id: Uuid) {
match self.index.get_mut(&value) { match self.index.get_mut(&value) {
Some(ids) => { Some(ids) => {
ids.insert(id); ids.insert(id);
}, }
None => { None => {
self.index.insert(value, HashSet::from([id])); self.index.insert(value, HashSet::from([id]));
} }
} }
} }
pub fn remove(&mut self, value: &IndexableValue, id_to_be_removed: UUID) -> bool { pub fn remove(&mut self, value: &IndexableValue, id_to_be_removed: Uuid) -> bool {
match self.index.get_mut(value) { match self.index.get_mut(value) {
Some(ids) => { Some(ids) => {
let was_present = ids.remove(&id_to_be_removed); ids.remove(&id_to_be_removed) // true iff was present
was_present
},
None => {
false
} }
None => false,
} }
} }
} }

View file

@ -1,4 +1,4 @@
pub mod table;
pub mod row;
pub mod column_index; pub mod column_index;
pub mod row;
pub mod schema; pub mod schema;
pub mod table;

View file

@ -1,6 +1,6 @@
use std::slice::SliceIndex;
use std::ops::{Index, IndexMut};
use crate::type_system::Value; use crate::type_system::Value;
use std::ops::{Index, IndexMut};
use std::slice::SliceIndex;
pub type ColumnPosition = usize; pub type ColumnPosition = usize;
@ -28,7 +28,7 @@ where
} }
impl FromIterator<Value> for Row { impl FromIterator<Value> for Row {
fn from_iter<I: IntoIterator<Item=Value>>(iter: I) -> Self { fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
let mut v = vec![]; let mut v = vec![];
for x in iter { for x in iter {
v.push(x) v.push(x)
@ -62,11 +62,8 @@ impl Row {
// If the index from `columns` is non-existant in `row`, it will just ignore it. // If the index from `columns` is non-existant in `row`, it will just ignore it.
let mut subrow: Row = Row::new(); let mut subrow: Row = Row::new();
for column_position in columns { for column_position in columns {
match self.get(*column_position) { if let Some(value) = self.get(*column_position) {
Some(value) => { subrow.0.push(value.clone())
subrow.0.push(value.clone())
},
None => {}
} }
} }
subrow subrow

View file

@ -1,10 +1,10 @@
use std::collections::HashMap;
use bimap::BiMap;
use crate::result::DbResult;
use crate::operation::{InsertionValues, ColumnSelection};
use crate::internals::row::{Row, ColumnPosition};
use crate::type_system::{DbType, Value, IndexableValue, UUID};
use crate::error::Error; use crate::error::Error;
use crate::internals::row::{ColumnPosition, Row};
use crate::operation::{ColumnSelection, InsertionValues};
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 // 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. // then you can give the metadata to the parser without giving it the data.
@ -22,29 +22,36 @@ pub type ColumnName = String;
impl TableSchema { impl TableSchema {
fn get_column(&self, column_name: &ColumnName) -> DbResult<(DbType, ColumnPosition)> { fn get_column(&self, column_name: &ColumnName) -> DbResult<(DbType, ColumnPosition)> {
match self.column_name_position_mapping.get_by_left(column_name) { match self.column_name_position_mapping.get_by_left(column_name) {
Some(column_position) => { Some(column_position) => match self.types.get(*column_position) {
match self.types.get(*column_position) { Some(type_) => Ok((*type_, *column_position)),
Some(type_) => { None => Err(Error::MissingTypeAnnotationOfColumn(
Ok((*type_, *column_position)) self.table_name.clone(),
}, *column_position,
None => { )),
Err(Error::MissingTypeAnnotationOfColumn(self.table_name.clone(), *column_position))
}
}
}, },
None => Err(Error::ColumnDoesNotExist(self.table_name.clone(), column_name.clone())) None => Err(Error::ColumnDoesNotExist(
self.table_name.clone(),
column_name.clone(),
)),
} }
} }
pub fn column_position_from_column_name(&self, column_name: &ColumnName) -> DbResult<ColumnPosition> { pub fn column_position_from_column_name(
self.get_column(column_name).map(|(_, column_position)| column_position) &self,
column_name: &ColumnName,
) -> DbResult<ColumnPosition> {
self.get_column(column_name)
.map(|(_, column_position)| column_position)
} }
pub fn is_primary(&self, column_position: ColumnPosition) -> bool { pub fn is_primary(&self, column_position: ColumnPosition) -> bool {
self.primary_key == column_position self.primary_key == column_position
} }
fn column_positions_from_column_names(&self, column_names: &[ColumnName]) -> DbResult<Vec<ColumnPosition>> { fn column_positions_from_column_names(
&self,
column_names: &[ColumnName],
) -> DbResult<Vec<ColumnPosition>> {
let mut positions: Vec<ColumnPosition> = Vec::with_capacity(column_names.len()); let mut positions: Vec<ColumnPosition> = Vec::with_capacity(column_names.len());
for column_name in column_names { for column_name in column_names {
let column_position = self.column_position_from_column_name(column_name)?; let column_position = self.column_position_from_column_name(column_name)?;
@ -53,24 +60,40 @@ impl TableSchema {
Ok(positions) Ok(positions)
} }
pub fn column_name_from_column_position(&self, column_position: ColumnPosition) -> DbResult<ColumnName> { pub fn column_name_from_column_position(
match self.column_name_position_mapping.get_by_right(&column_position) { &self,
column_position: ColumnPosition,
) -> DbResult<ColumnName> {
match self
.column_name_position_mapping
.get_by_right(&column_position)
{
Some(column_name) => Ok(column_name.clone()), Some(column_name) => Ok(column_name.clone()),
None => Err(Error::ColumnPositionDoesNotExist(self.table_name.clone(), column_position)) None => Err(Error::ColumnPositionDoesNotExist(
self.table_name.clone(),
column_position,
)),
} }
} }
pub fn column_positions_from_column_selection(&self, column_selection: &ColumnSelection) -> DbResult<Vec<ColumnPosition>> { pub fn column_positions_from_column_selection(
&self,
column_selection: &ColumnSelection,
) -> DbResult<Vec<ColumnPosition>> {
match column_selection { match column_selection {
ColumnSelection::All => { ColumnSelection::All => {
let mut column_positions: Vec<ColumnPosition> = self.column_name_position_mapping.iter().map(|(_, column_position)| *column_position).collect(); let mut column_positions: Vec<ColumnPosition> = self
.column_name_position_mapping
.iter()
.map(|(_, column_position)| *column_position)
.collect();
column_positions.sort(); column_positions.sort();
Ok(column_positions) Ok(column_positions)
}, }
ColumnSelection::Columns(column_names) => { ColumnSelection::Columns(column_names) => {
self.column_positions_from_column_names(column_names) self.column_positions_from_column_names(column_names)
}, }
} }
} }
@ -78,7 +101,10 @@ impl TableSchema {
self.column_name_position_mapping.len() self.column_name_position_mapping.len()
} }
pub fn row_from_insertion_values(&self, insertion_values: InsertionValues) -> DbResult<(UUID, Row)> { pub fn row_from_insertion_values(
&self,
insertion_values: InsertionValues,
) -> DbResult<(Uuid, Row)> {
// TODO: There should be proper validation of the insertion_values. // TODO: There should be proper validation of the insertion_values.
// And it shouldn't really be done here. // And it shouldn't really be done here.
// //
@ -86,7 +112,10 @@ impl TableSchema {
// //
let number_of_columns = self.number_of_columns(); let number_of_columns = self.number_of_columns();
if number_of_columns != insertion_values.len() { if number_of_columns != insertion_values.len() {
return Err(Error::MismatchBetweenInsertValuesAndColumns(self.table_name.clone(), insertion_values)) return Err(Error::MismatchBetweenInsertValuesAndColumns(
self.table_name.clone(),
insertion_values,
));
} }
let mut row: Row = Row::with_number_of_columns(number_of_columns); let mut row: Row = Row::with_number_of_columns(number_of_columns);
@ -99,27 +128,21 @@ impl TableSchema {
for column_position in 0..number_of_columns { for column_position in 0..number_of_columns {
let column_name: ColumnName = self.column_name_from_column_position(column_position)?; let column_name: ColumnName = self.column_name_from_column_position(column_position)?;
match values.get(&column_name) { match values.get(&column_name) {
Some(db_value) => { Some(db_value) => row.push(db_value.clone()),
row.push(db_value.clone())
},
None => { None => {
return Err(Error::MissingColumnInInsertValues(self.table_name.clone(), column_name, insertion_values)) return Err(Error::MissingColumnInInsertValues(
self.table_name.clone(),
column_name,
insertion_values,
))
} }
} }
} }
let id: UUID = match row.get(self.primary_key) { let id: Uuid = match row.get(self.primary_key) {
Some(val) => { Some(Value::Indexable(IndexableValue::Uuid(id))) => *id,
match val { Some(_) => unreachable!(),
Value::Indexable(IndexableValue::UUID(id)) => { None => unreachable!(),
*id
},
_ =>
unreachable!()
}
},
None =>
unreachable!()
}; };
Ok((id, row)) Ok((id, row))

View file

@ -1,25 +1,21 @@
use std::collections::{BTreeMap, HashMap, HashSet}; use std::collections::{BTreeMap, HashMap, HashSet};
use crate::result::DbResult;
use crate::type_system::{UUID, Value, IndexableValue};
use crate::error::Error; use crate::error::Error;
use crate::internals::schema::{TableSchema, ColumnName};
use crate::internals::row::{Row, ColumnPosition};
use crate::internals::column_index::ColumnIndex; use crate::internals::column_index::ColumnIndex;
use crate::internals::row::{ColumnPosition, Row};
use crate::internals::schema::{ColumnName, TableSchema};
use crate::result::DbResult;
use crate::type_system::{IndexableValue, Uuid, Value};
#[derive(Debug)] #[derive(Debug)]
pub struct Table { pub struct Table {
pub schema: TableSchema, pub schema: TableSchema,
pub rows: Rows, // TODO: Consider wrapping this in a lock. Also consider if we need to have the pub rows: Rows, // TODO: Consider wrapping this in a lock. Also consider if we need to have the
// same lock for both rows and indexes // same lock for both rows and indexes
pub indexes: pub indexes: HashMap<ColumnPosition, ColumnIndex>,
HashMap<ColumnPosition, ColumnIndex>
} }
pub type Rows = pub type Rows = BTreeMap<Uuid, Row>;
BTreeMap<UUID, Row>;
impl Table { impl Table {
pub fn new(table_schema: TableSchema) -> Self { pub fn new(table_schema: TableSchema) -> Self {
@ -31,11 +27,11 @@ impl Table {
} }
// ======Selection====== // ======Selection======
fn get_row_by_id(&self, id: UUID) -> Option<Row> { fn get_row_by_id(&self, id: Uuid) -> Option<Row> {
self.rows.get(&id).cloned() self.rows.get(&id).cloned()
} }
fn get_rows_by_ids(&self, ids: HashSet<UUID>) -> Vec<Row> { fn get_rows_by_ids(&self, ids: HashSet<Uuid>) -> Vec<Row> {
ids.into_iter() ids.into_iter()
.filter_map(|id| self.get_row_by_id(id)) .filter_map(|id| self.get_row_by_id(id))
.collect() .collect()
@ -43,44 +39,71 @@ impl Table {
fn get_rows_by_value(&self, column_position: ColumnPosition, value: &Value) -> Vec<Row> { fn get_rows_by_value(&self, column_position: ColumnPosition, value: &Value) -> Vec<Row> {
// brute-force search // brute-force search
self.rows.values() self.rows
.filter_map(|row| if row.get(column_position) == Some(value) { Some(row.clone()) } else { None }) .values()
.filter_map(|row| {
if row.get(column_position) == Some(value) {
Some(row.clone())
} else {
None
}
})
.collect() .collect()
} }
pub fn select_all_rows(&self, selected_column_positions: &Vec<ColumnPosition>) -> Vec<Row> { pub fn select_all_rows(&self, selected_column_positions: &Vec<ColumnPosition>) -> Vec<Row> {
self.rows.values().map(|row| row.restrict_columns(&selected_column_positions)).collect() self.rows
.values()
.map(|row| row.restrict_columns(selected_column_positions))
.collect()
} }
pub fn select_rows_where_eq(&self, selected_column_positions: &Vec<ColumnPosition>, column_position: ColumnPosition, value: Value) -> DbResult<Vec<Row>> { pub fn select_rows_where_eq(
&self,
selected_column_positions: &Vec<ColumnPosition>,
column_position: ColumnPosition,
value: Value,
) -> DbResult<Vec<Row>> {
match value { match value {
Value::Indexable(value) => { Value::Indexable(value) => match self.fetch_ids_from_index(column_position, &value)? {
match self.fetch_ids_from_index(column_position, &value)? { Some(ids) => Ok(self
Some(ids) => .get_rows_by_ids(ids)
Ok(self.get_rows_by_ids(ids).iter().map(|row| row.restrict_columns(&selected_column_positions)).collect()), .iter()
None => .map(|row| row.restrict_columns(selected_column_positions))
Ok(self.get_rows_by_value(column_position, &Value::Indexable(value)).iter().map(|row| row.restrict_columns(&selected_column_positions)).collect()) .collect()),
} None => Ok(self
.get_rows_by_value(column_position, &Value::Indexable(value))
.iter()
.map(|row| row.restrict_columns(selected_column_positions))
.collect()),
}, },
_ => { _ => Ok(self
Ok(self.get_rows_by_value(column_position, &value).iter().map(|row| row.restrict_columns(&selected_column_positions)).collect()) .get_rows_by_value(column_position, &value)
} .iter()
.map(|row| row.restrict_columns(selected_column_positions))
.collect()),
} }
} }
// ======Insertion====== // ======Insertion======
pub fn insert_row_at(&mut self, id: UUID, row: Row) -> DbResult<()> { pub fn insert_row_at(&mut self, id: Uuid, row: Row) -> DbResult<()> {
if self.rows.get(&id).is_some() { if self.rows.get(&id).is_some() {
return Err(Error::AttemptingToInsertAlreadyPresentId(self.schema.table_name.clone(), id)) return Err(Error::AttemptingToInsertAlreadyPresentId(
self.schema.table_name.clone(),
id,
));
} }
for (column_position, column_index) in &mut self.indexes { for (column_position, column_index) in &mut self.indexes {
match row.get(*column_position) { match row.get(*column_position) {
Some(Value::Indexable(val)) => { Some(Value::Indexable(val)) => column_index.add(val.clone(), id),
column_index.add(val.clone(), id) Some(_) => {}
}, None => {
Some(_) => {}, return Err(Error::ColumnPositionDoesNotExist(
None => return Err(Error::ColumnPositionDoesNotExist(self.schema.table_name.clone(), *column_position)) self.schema.table_name.clone(),
*column_position,
))
}
} }
} }
@ -89,7 +112,7 @@ impl Table {
} }
// ======Deletion====== // ======Deletion======
fn delete_row_by_id(&mut self, id: UUID) -> usize { fn delete_row_by_id(&mut self, id: Uuid) -> usize {
match self.rows.remove(&id) { match self.rows.remove(&id) {
Some(row) => { Some(row) => {
for (column_position, column_index) in &mut self.indexes { for (column_position, column_index) in &mut self.indexes {
@ -98,12 +121,12 @@ impl Table {
}; };
} }
1 1
}, }
None => 0 None => 0,
} }
} }
fn delete_rows_by_ids(&mut self, ids: HashSet<UUID>) -> usize { fn delete_rows_by_ids(&mut self, ids: HashSet<Uuid>) -> usize {
let mut total_count = 0; let mut total_count = 0;
for id in ids { for id in ids {
total_count += self.delete_row_by_id(id) total_count += self.delete_row_by_id(id)
@ -112,8 +135,16 @@ impl Table {
} }
fn delete_rows_by_value(&mut self, column_position: ColumnPosition, value: &Value) -> usize { fn delete_rows_by_value(&mut self, column_position: ColumnPosition, value: &Value) -> usize {
let matched_ids: HashSet<UUID> = self.rows.iter() let matched_ids: HashSet<Uuid> = self
.filter_map(|(id, row)| if row.get(column_position) == Some(value) { Some(*id) } else { None }) .rows
.iter()
.filter_map(|(id, row)| {
if row.get(column_position) == Some(value) {
Some(*id)
} else {
None
}
})
.collect(); .collect();
self.delete_rows_by_ids(matched_ids) self.delete_rows_by_ids(matched_ids)
} }
@ -125,18 +156,17 @@ impl Table {
number_of_rows number_of_rows
} }
pub fn delete_rows_where_eq(&mut self, column_position: ColumnPosition, value: Value) -> DbResult<usize> { pub fn delete_rows_where_eq(
&mut self,
column_position: ColumnPosition,
value: Value,
) -> DbResult<usize> {
match value { match value {
Value::Indexable(value) => { Value::Indexable(value) => match self.fetch_ids_from_index(column_position, &value)? {
match self.fetch_ids_from_index(column_position, &value)? { Some(ids) => Ok(self.delete_rows_by_ids(ids)),
Some(ids) => None => Ok(self.delete_rows_by_value(column_position, &Value::Indexable(value))),
Ok(self.delete_rows_by_ids(ids)),
None =>
Ok(self.delete_rows_by_value(column_position, &Value::Indexable(value)))
}
}, },
_ => _ => Ok(self.delete_rows_by_value(column_position, &value)),
Ok(self.delete_rows_by_value(column_position, &value))
} }
} }
@ -145,15 +175,25 @@ impl Table {
self.indexes.insert(column_position, column_index); self.indexes.insert(column_position, column_index);
} }
fn fetch_ids_from_index(&self, column_position: ColumnPosition, value: &IndexableValue) -> DbResult<Option<HashSet<UUID>>> { fn fetch_ids_from_index(
&self,
column_position: ColumnPosition,
value: &IndexableValue,
) -> DbResult<Option<HashSet<Uuid>>> {
if self.schema.is_primary(column_position) { if self.schema.is_primary(column_position) {
match value { match value {
IndexableValue::UUID(id) => IndexableValue::Uuid(id) => Ok(Some(HashSet::from([*id]))),
Ok(Some(HashSet::from([*id]))),
_ => { _ => {
let column_name: ColumnName = self.schema.column_name_from_column_position(column_position)?; let column_name: ColumnName = self
.schema
.column_name_from_column_position(column_position)?;
let type_ = self.schema.types[column_position]; let type_ = self.schema.types[column_position];
Err(Error::ValueDoesNotMatchExpectedType(self.schema.table_name.clone(), column_name, type_, Value::Indexable(value.clone()))) Err(Error::ValueDoesNotMatchExpectedType(
self.schema.table_name.clone(),
column_name,
type_,
Value::Indexable(value.clone()),
))
} }
} }
} else { } else {
@ -161,10 +201,8 @@ impl Table {
Some(index) => { Some(index) => {
let ids = index.get(value); let ids = index.get(value);
Ok(Some(ids)) Ok(Some(ids))
},
None => {
Ok(None)
} }
None => Ok(None),
} }
} }
} }

View file

@ -1,13 +1,12 @@
use bimap::BiMap;
use crate::result::DbResult;
use crate::type_system::{Value, DbType, IndexableValue};
use crate::internals::schema::{TableName, TableSchema, ColumnName};
use crate::internals::table::Table;
use crate::internals::row::{Row, ColumnPosition};
use crate::error::Error; use crate::error::Error;
use crate::operation::{Operation, Condition, ColumnSelection};
use crate::internals::column_index::ColumnIndex; use crate::internals::column_index::ColumnIndex;
use crate::internals::row::{ColumnPosition, Row};
use crate::internals::schema::{ColumnName, TableName, TableSchema};
use crate::internals::table::Table;
use crate::operation::{ColumnSelection, Condition, Operation};
use crate::result::DbResult;
use crate::type_system::{DbType, IndexableValue, Value};
use bimap::BiMap;
// Use `TablePosition` as index // Use `TablePosition` as index
pub type Tables = Vec<Table>; pub type Tables = Vec<Table>;
@ -27,8 +26,7 @@ pub enum Response {
Deleted(usize), // how many were deleted Deleted(usize), // how many were deleted
TableCreated, TableCreated,
IndexCreated, IndexCreated,
} }
impl State { impl State {
fn new() -> Self { fn new() -> Self {
@ -43,24 +41,28 @@ impl State {
Some(table_position) => { Some(table_position) => {
let table = &self.tables[*table_position]; let table = &self.tables[*table_position];
Ok(table) Ok(table)
}, }
None => Err(Error::TableDoesNotExist(table_name.clone())) None => Err(Error::TableDoesNotExist(table_name.clone())),
} }
} }
fn table_from_name_mut<'b: 'a, 'a>(&'b mut self, table_name: &TableName) -> DbResult<&'a mut Table> { 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) { match self.table_name_position_mapping.get_by_left(table_name) {
Some(table_position) => { Some(table_position) => {
let table = &mut self.tables[*table_position]; let table = &mut self.tables[*table_position];
Ok(table) Ok(table)
}, }
None => Err(Error::TableDoesNotExist(table_name.clone())) None => Err(Error::TableDoesNotExist(table_name.clone())),
} }
} }
fn attach_table(&mut self, table_name: TableName, table: Table) { fn attach_table(&mut self, table_name: TableName, table: Table) {
let new_table_position: TablePosition = self.tables.len(); let new_table_position: TablePosition = self.tables.len();
self.table_name_position_mapping.insert(table_name, new_table_position); self.table_name_position_mapping
.insert(table_name, new_table_position);
self.tables.push(table); self.tables.push(table);
} }
@ -78,72 +80,94 @@ impl State {
Select(table_name, column_selection, maybe_condition) => { Select(table_name, column_selection, maybe_condition) => {
let table: &Table = self.table_from_name(&table_name)?; let table: &Table = self.table_from_name(&table_name)?;
let selected_column_positions: Vec<ColumnPosition> = table.schema.column_positions_from_column_selection(&column_selection)?; let selected_column_positions: Vec<ColumnPosition> = table
.schema
.column_positions_from_column_selection(&column_selection)?;
let selected_rows = match maybe_condition { let selected_rows = match maybe_condition {
None => table.select_all_rows(&selected_column_positions), None => table.select_all_rows(&selected_column_positions),
Some(Condition::Eq(eq_column_name, value)) => { Some(Condition::Eq(eq_column_name, value)) => {
let eq_column_position = table.schema.column_position_from_column_name(&eq_column_name)?; let eq_column_position = table
table.select_rows_where_eq(&selected_column_positions, eq_column_position, value)? .schema
.column_position_from_column_name(&eq_column_name)?;
table.select_rows_where_eq(
&selected_column_positions,
eq_column_position,
value,
)?
} }
}; };
Ok(Response::Selected(selected_rows)) Ok(Response::Selected(selected_rows))
}, }
Insert(table_name, values) => { Insert(table_name, values) => {
let table: &mut Table = self.table_from_name_mut(&table_name)?; let table: &mut Table = self.table_from_name_mut(&table_name)?;
let (id, row) = table.schema.row_from_insertion_values(values)?; let (id, row) = table.schema.row_from_insertion_values(values)?;
let _ = table.insert_row_at(id, row)?; table.insert_row_at(id, row)?;
Ok(Response::Inserted) Ok(Response::Inserted)
}, }
Delete(table_name, maybe_condition) => { Delete(table_name, maybe_condition) => {
let table: &mut Table = self.table_from_name_mut(&table_name)?; let table: &mut Table = self.table_from_name_mut(&table_name)?;
let rows_affected = match maybe_condition { let rows_affected = match maybe_condition {
None => table.delete_all_rows(), None => table.delete_all_rows(),
Some(Condition::Eq(eq_column_name, value)) => { Some(Condition::Eq(eq_column_name, value)) => {
let eq_column_position = table.schema.column_position_from_column_name(&eq_column_name)?; let eq_column_position = table
.schema
.column_position_from_column_name(&eq_column_name)?;
table.delete_rows_where_eq(eq_column_position, value)? table.delete_rows_where_eq(eq_column_position, value)?
} }
}; };
Ok(Response::Deleted(rows_affected)) Ok(Response::Deleted(rows_affected))
}, }
CreateTable(table_name, table_schema) => { CreateTable(table_name, table_schema) => {
let table = Table::new(table_schema); let table = Table::new(table_schema);
self.attach_table(table_name, table); self.attach_table(table_name, table);
Ok(Response::TableCreated) Ok(Response::TableCreated)
}, }
CreateIndex(table_name, column_name) => { CreateIndex(table_name, column_name) => {
let table: &mut Table = self.table_from_name_mut(&table_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)?; let column_position: ColumnPosition = table
.schema
.column_position_from_column_name(&column_name)?;
let mut index: ColumnIndex = ColumnIndex::new(); let mut index: ColumnIndex = ColumnIndex::new();
let _ = update_index_from_table(&mut index, &table, column_position)?; update_index_from_table(&mut index, table, column_position)?;
table.attach_index(column_position, index); table.attach_index(column_position, index);
Ok(Response::IndexCreated) Ok(Response::IndexCreated)
}, }
} }
} }
} }
// Should be used in the case when an indexed is created after the table has existed for a // Should be used in the case when an indexed is created after the table has existed for a
// while. In such a case you need to build the index from the already existing rows. // while. In such a case you need to build the index from the already existing rows.
fn update_index_from_table(column_index: &mut ColumnIndex, table: &Table, column_position: ColumnPosition) -> DbResult<()> { fn update_index_from_table(
column_index: &mut ColumnIndex,
table: &Table,
column_position: ColumnPosition,
) -> DbResult<()> {
for (id, row) in &table.rows { for (id, row) in &table.rows {
let value = match row.get(column_position) { let value = match row.get(column_position) {
Some(Value::Indexable(value)) => { Some(Value::Indexable(value)) => value.clone(),
value.clone()
},
Some(_) => { Some(_) => {
let column_name: ColumnName = table.schema.column_name_from_column_position(column_position)?; let column_name: ColumnName = table
return Err(Error::AttemptToIndexNonIndexableColumn(table.schema.table_name.to_string(), column_name)) .schema
}, .column_name_from_column_position(column_position)?;
return Err(Error::AttemptToIndexNonIndexableColumn(
table.schema.table_name.to_string(),
column_name,
));
}
None => { None => {
return Err(Error::ColumnPositionDoesNotExist(table.schema.table_name.to_string(), column_position)) return Err(Error::ColumnPositionDoesNotExist(
table.schema.table_name.to_string(),
column_position,
))
} }
}; };
column_index.add(value, *id) column_index.add(value, *id)
@ -153,10 +177,9 @@ fn update_index_from_table(column_index: &mut ColumnIndex, table: &Table, column
// TODO: Give a better name to something that you can respond to with rows // TODO: Give a better name to something that you can respond to with rows
trait SqlResponseConsumer { trait SqlResponseConsumer {
// TODO: // TODO:
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -176,7 +199,7 @@ mod tests {
mapping.insert("age".to_string(), age); mapping.insert("age".to_string(), age);
mapping mapping
}, },
types: vec![DbType::UUID, DbType::String, DbType::Int], types: vec![DbType::Uuid, DbType::String, DbType::Int],
} }
} }
@ -186,7 +209,9 @@ mod tests {
let users_schema = users_schema(); let users_schema = users_schema();
let users = users_schema.table_name.clone(); let users = users_schema.table_name.clone();
state.interpret(Operation::CreateTable(users.clone(), users_schema)).unwrap(); state
.interpret(Operation::CreateTable(users.clone(), users_schema))
.unwrap();
assert!(state.tables.len() == 1); assert!(state.tables.len() == 1);
let table = &state.tables[0]; let table = &state.tables[0];
@ -201,48 +226,68 @@ mod tests {
let users_schema = users_schema(); let users_schema = users_schema();
let users = users_schema.table_name.clone(); let users = users_schema.table_name.clone();
state.interpret(Operation::CreateTable(users.clone(), users_schema)).unwrap(); state
let response: Response = state.interpret(Operation::Select(users.clone(), ColumnSelection::All, None)).unwrap(); .interpret(Operation::CreateTable(users.clone(), users_schema))
.unwrap();
let response: Response = state
.interpret(Operation::Select(users.clone(), ColumnSelection::All, None))
.unwrap();
assert!(matches!(response, Response::Selected(_))); assert!(matches!(response, Response::Selected(_)));
let Response::Selected(rows) = response else { todo!() }; let Response::Selected(rows) = response else {
todo!()
};
assert!(rows.len() == 0); assert!(rows.len() == 0);
} }
#[test] #[test]
fn test_select_nonexistant_table() { fn test_select_nonexistant_table() {
let mut state = State::new(); let mut state = State::new();
let response: DbResult<Response> = state.interpret(Operation::Select("table_that_doesnt_exist".to_string(), ColumnSelection::All, None)); let response: DbResult<Response> = state.interpret(Operation::Select(
"table_that_doesnt_exist".to_string(),
ColumnSelection::All,
None,
));
assert!(matches!(response, Err(Error::TableDoesNotExist(_)))); assert!(matches!(response, Err(Error::TableDoesNotExist(_))));
} }
#[test] #[test]
fn test_insert_select_basic1() { fn test_insert_select_basic1() {
use Value::*;
use IndexableValue::*; use IndexableValue::*;
use Value::*;
let mut state = State::new(); let mut state = State::new();
let users_schema = users_schema(); let users_schema = users_schema();
let users = users_schema.table_name.clone(); let users = users_schema.table_name.clone();
state.interpret(Operation::CreateTable(users.clone(), users_schema)).unwrap(); state
.interpret(Operation::CreateTable(users.clone(), users_schema))
.unwrap();
let (id, name, age) = ( let (id, name, age) = (
Indexable(UUID(0)), Indexable(Uuid(0)),
Indexable(String("Plato".to_string())), Indexable(String("Plato".to_string())),
Indexable(Int(64)) Indexable(Int(64)),
); );
state.interpret(Operation::Insert(users.clone(), vec![ state
("id".to_string(), id.clone()), .interpret(Operation::Insert(
("name".to_string(), name.clone()), users.clone(),
("age".to_string(), age.clone()), vec![
])).unwrap(); ("id".to_string(), id.clone()),
("name".to_string(), name.clone()),
("age".to_string(), age.clone()),
],
))
.unwrap();
let response: Response = state.interpret(Operation::Select(users.clone(), ColumnSelection::All, None)).unwrap(); let response: Response = state
.interpret(Operation::Select(users.clone(), ColumnSelection::All, None))
.unwrap();
assert!(matches!(response, Response::Selected(_))); assert!(matches!(response, Response::Selected(_)));
let Response::Selected(rows) = response else { todo!() }; let Response::Selected(rows) = response else {
todo!()
};
assert!(rows.len() == 1); assert!(rows.len() == 1);
let row = &rows[0]; let row = &rows[0];
@ -254,45 +299,59 @@ mod tests {
#[test] #[test]
fn test_insert_select_basic2() { fn test_insert_select_basic2() {
use Value::*;
use IndexableValue::*;
use Operation::*;
use ColumnSelection::*; use ColumnSelection::*;
use Condition::*; use Condition::*;
use IndexableValue::*;
use Operation::*;
use Value::*;
let mut state = State::new(); let mut state = State::new();
let users_schema = users_schema(); let users_schema = users_schema();
let users = users_schema.table_name.clone(); let users = users_schema.table_name.clone();
state.interpret(CreateTable(users.clone(), users_schema)).unwrap(); state
.interpret(CreateTable(users.clone(), users_schema))
.unwrap();
let (id0, name0, age0) = ( let (id0, name0, age0) = (
Indexable(UUID(0)), Indexable(Uuid(0)),
Indexable(String("Plato".to_string())), Indexable(String("Plato".to_string())),
Indexable(Int(64)) Indexable(Int(64)),
); );
state.interpret(Insert(users.clone(), vec![ state
("id".to_string(), id0.clone()), .interpret(Insert(
("name".to_string(), name0.clone()), users.clone(),
("age".to_string(), age0.clone()), vec![
])).unwrap(); ("id".to_string(), id0.clone()),
("name".to_string(), name0.clone()),
("age".to_string(), age0.clone()),
],
))
.unwrap();
let (id1, name1, age1) = ( let (id1, name1, age1) = (
Indexable(UUID(1)), Indexable(Uuid(1)),
Indexable(String("Aristotle".to_string())), Indexable(String("Aristotle".to_string())),
Indexable(Int(20)) Indexable(Int(20)),
); );
state.interpret(Insert(users.clone(), vec![ state
("id".to_string(), id1.clone()), .interpret(Insert(
("name".to_string(), name1.clone()), users.clone(),
("age".to_string(), age1.clone()), vec![
])).unwrap(); ("id".to_string(), id1.clone()),
("name".to_string(), name1.clone()),
("age".to_string(), age1.clone()),
],
))
.unwrap();
{ {
let response: Response = state.interpret(Select(users.clone(), All, None)).unwrap(); let response: Response = state.interpret(Select(users.clone(), All, None)).unwrap();
assert!(matches!(response, Response::Selected(_))); assert!(matches!(response, Response::Selected(_)));
let Response::Selected(rows) = response else { todo!() }; let Response::Selected(rows) = response else {
todo!()
};
assert!(rows.len() == 2); assert!(rows.len() == 2);
let row0 = &rows[0]; let row0 = &rows[0];
let row1 = &rows[1]; let row1 = &rows[1];
@ -309,9 +368,17 @@ mod tests {
} }
{ {
let response: Response = state.interpret(Select(users.clone(), All, Some(Eq("id".to_string(), id0.clone())))).unwrap(); let response: Response = state
.interpret(Select(
users.clone(),
All,
Some(Eq("id".to_string(), id0.clone())),
))
.unwrap();
assert!(matches!(response, Response::Selected(_))); assert!(matches!(response, Response::Selected(_)));
let Response::Selected(rows) = response else { todo!() }; let Response::Selected(rows) = response else {
todo!()
};
assert!(rows.len() == 1); assert!(rows.len() == 1);
let row0 = &rows[0]; let row0 = &rows[0];
@ -322,9 +389,17 @@ 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())))).unwrap(); let response: Response = state
.interpret(Select(
users.clone(),
Columns(vec!["name".to_string(), "id".to_string()]),
Some(Eq("id".to_string(), id0.clone())),
))
.unwrap();
assert!(matches!(response, Response::Selected(_))); assert!(matches!(response, Response::Selected(_)));
let Response::Selected(rows) = response else { todo!() }; let Response::Selected(rows) = response else {
todo!()
};
assert!(rows.len() == 1); assert!(rows.len() == 1);
let row0 = &rows[0]; let row0 = &rows[0];
@ -336,48 +411,66 @@ mod tests {
#[test] #[test]
fn test_delete() { fn test_delete() {
use Value::*;
use IndexableValue::*;
use Operation::*;
use ColumnSelection::*; use ColumnSelection::*;
use Condition::*; use Condition::*;
use IndexableValue::*;
use Operation::*;
use Value::*;
let mut state = State::new(); let mut state = State::new();
let users_schema = users_schema(); let users_schema = users_schema();
let users = users_schema.table_name.clone(); let users = users_schema.table_name.clone();
state.interpret(CreateTable(users.clone(), users_schema)).unwrap(); state
.interpret(CreateTable(users.clone(), users_schema))
.unwrap();
let (id0, name0, age0) = ( let (id0, name0, age0) = (
Indexable(UUID(0)), Indexable(Uuid(0)),
Indexable(String("Plato".to_string())), Indexable(String("Plato".to_string())),
Indexable(Int(64)) Indexable(Int(64)),
); );
state.interpret(Insert(users.clone(), vec![ state
("id".to_string(), id0.clone()), .interpret(Insert(
("name".to_string(), name0.clone()), users.clone(),
("age".to_string(), age0.clone()), vec![
])).unwrap(); ("id".to_string(), id0.clone()),
("name".to_string(), name0.clone()),
("age".to_string(), age0.clone()),
],
))
.unwrap();
let (id1, name1, age1) = ( let (id1, name1, age1) = (
Indexable(UUID(1)), Indexable(Uuid(1)),
Indexable(String("Aristotle".to_string())), Indexable(String("Aristotle".to_string())),
Indexable(Int(20)) Indexable(Int(20)),
); );
state.interpret(Insert(users.clone(), vec![ state
("id".to_string(), id1.clone()), .interpret(Insert(
("name".to_string(), name1.clone()), users.clone(),
("age".to_string(), age1.clone()), vec![
])).unwrap(); ("id".to_string(), id1.clone()),
("name".to_string(), name1.clone()),
("age".to_string(), age1.clone()),
],
))
.unwrap();
let delete_response: Response = state
let delete_response: Response = state.interpret(Delete(users.clone(), Some(Eq("id".to_string(), id0.clone())))).unwrap(); .interpret(Delete(
users.clone(),
Some(Eq("id".to_string(), id0.clone())),
))
.unwrap();
assert!(matches!(delete_response, Response::Deleted(1))); 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.clone(), All, None)).unwrap();
assert!(matches!(response, Response::Selected(_))); assert!(matches!(response, Response::Selected(_)));
let Response::Selected(rows) = response else { todo!() }; let Response::Selected(rows) = response else {
todo!()
};
assert!(rows.len() == 1); assert!(rows.len() == 1);
let row = &rows[0]; let row = &rows[0];
@ -390,13 +483,12 @@ mod tests {
// TODO: Test CreateIndex // TODO: Test CreateIndex
} }
pub fn example() { pub fn example() {
use Value::*;
use IndexableValue::*;
use Operation::*;
use ColumnSelection::*; use ColumnSelection::*;
use Condition::*; use Condition::*;
use IndexableValue::*;
use Operation::*;
use Value::*;
let users_schema = { let users_schema = {
let id: ColumnPosition = 0; let id: ColumnPosition = 0;
@ -413,60 +505,89 @@ pub fn example() {
mapping.insert("age".to_string(), age); mapping.insert("age".to_string(), age);
mapping mapping
}, },
types: vec![DbType::UUID, DbType::String, DbType::Int], types: vec![DbType::Uuid, DbType::String, DbType::Int],
} }
}; };
let users = users_schema.table_name.clone(); let users = users_schema.table_name.clone();
let mut state = State::new(); let mut state = State::new();
state.interpret(Operation::CreateTable(users.clone(), users_schema)).unwrap(); state
.interpret(Operation::CreateTable(users.clone(), users_schema))
.unwrap();
let (id0, name0, age0) = ( let (id0, name0, age0) = (
Indexable(UUID(0)), Indexable(Uuid(0)),
Indexable(String("Plato".to_string())), Indexable(String("Plato".to_string())),
Indexable(Int(64)) Indexable(Int(64)),
); );
println!("==INSERT Plato=="); println!("==INSERT Plato==");
state.interpret(Insert(users.clone(), vec![ state
("id".to_string(), id0.clone()), .interpret(Insert(
("name".to_string(), name0.clone()), users.clone(),
("age".to_string(), age0.clone()), vec![
])).unwrap(); ("id".to_string(), id0.clone()),
("name".to_string(), name0.clone()),
("age".to_string(), age0.clone()),
],
))
.unwrap();
let (id1, name1, age1) = ( let (id1, name1, age1) = (
Indexable(UUID(1)), Indexable(Uuid(1)),
Indexable(String("Aristotle".to_string())), Indexable(String("Aristotle".to_string())),
Indexable(Int(20)) Indexable(Int(20)),
); );
println!("==INSERT Aristotle=="); println!("==INSERT Aristotle==");
state.interpret(Insert(users.clone(), vec![ state
("id".to_string(), id1.clone()), .interpret(Insert(
("name".to_string(), name1.clone()), users.clone(),
("age".to_string(), age1.clone()), vec![
])).unwrap(); ("id".to_string(), id1.clone()),
("name".to_string(), name1.clone()),
("age".to_string(), age1.clone()),
],
))
.unwrap();
println!(); println!();
{ {
let response: Response = state.interpret(Operation::Select(users.clone(), ColumnSelection::All, None)).unwrap(); let response: Response = state
.interpret(Operation::Select(users.clone(), ColumnSelection::All, None))
.unwrap();
println!("==SELECT ALL=="); println!("==SELECT ALL==");
println!("{:?}", response); println!("{:?}", response);
println!(); println!();
} }
{ {
let response: Response = state.interpret(Select(users.clone(), All, Some(Eq("id".to_string(), id0.clone())))).unwrap(); let response: Response = state
.interpret(Select(
users.clone(),
All,
Some(Eq("id".to_string(), id0.clone())),
))
.unwrap();
println!("==SELECT Plato=="); println!("==SELECT Plato==");
println!("{:?}", response); println!("{:?}", response);
println!(); println!();
} }
{ {
let _delete_response: Response = state.interpret(Delete(users.clone(), Some(Eq("id".to_string(), id0.clone())))).unwrap(); let _delete_response: Response = state
.interpret(Delete(
users.clone(),
Some(Eq("id".to_string(), id0.clone())),
))
.unwrap();
println!("==DELETE Plato=="); println!("==DELETE Plato==");
let response: Response = state.interpret(Select(users.clone(), Columns(vec!["name".to_string(), "id".to_string()]), None)).unwrap(); let response: Response = state
.interpret(Select(
users.clone(),
Columns(vec!["name".to_string(), "id".to_string()]),
None,
))
.unwrap();
println!("==SELECT All=="); println!("==SELECT All==");
println!("{:?}", response); println!("{:?}", response);
println!(); println!();
} }
} }

View file

@ -1,8 +1,8 @@
mod result;
mod internals;
mod operation;
mod interpreter;
mod error; mod error;
mod internals;
mod interpreter;
mod operation;
mod result;
mod type_system; mod type_system;
fn main() { fn main() {

View file

@ -1,5 +1,5 @@
use crate::internals::schema::{ColumnName, TableName, TableSchema};
use crate::type_system::Value; use crate::type_system::Value;
use crate::internals::schema::{TableSchema, TableName, ColumnName};
// ==============SQL operations================ // ==============SQL operations================
// TODO: Note that every operation has a table name. // TODO: Note that every operation has a table name.
@ -27,7 +27,6 @@ pub enum Condition {
// And(Box<Condition>, Box<Condition>), // And(Box<Condition>, Box<Condition>),
// Or(Box<Condition>, Box<Condition>), // Or(Box<Condition>, Box<Condition>),
// Not(Box<Condition>), // Not(Box<Condition>),
Eq(ColumnName, Value), Eq(ColumnName, Value),
// LessOrEqual(ColumnName, DbValue), // LessOrEqual(ColumnName, DbValue),
// Less(ColumnName, DbValue), // Less(ColumnName, DbValue),

View file

@ -4,11 +4,11 @@ pub enum DbType {
String, String,
Int, Int,
Number, Number,
UUID, Uuid,
} }
// ==============Values================ // ==============Values================
pub type UUID = u64; pub type Uuid = u64;
// TODO: What about nulls? I would rather not have that in SQL, it sucks. // TODO: What about nulls? I would rather not have that in SQL, it sucks.
// I would rather have non-nullable values by default, // I would rather have non-nullable values by default,
@ -16,7 +16,7 @@ pub type UUID = u64;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Value { pub enum Value {
Number(f64), // TODO: Can't put floats as keys in maps, since they don't implement Eq. What to Number(f64), // TODO: Can't put floats as keys in maps, since they don't implement Eq. What to
// do? // do?
Indexable(IndexableValue), Indexable(IndexableValue),
} }
@ -24,20 +24,19 @@ pub enum Value {
pub enum IndexableValue { pub enum IndexableValue {
String(String), String(String),
Int(u64), Int(u64),
UUID(UUID), Uuid(Uuid),
// TODO: what about null? // TODO: what about null?
} }
impl Value { impl Value {
pub fn to_type(self) -> DbType { pub fn to_type(&self) -> DbType {
match self { match self {
Self::Number(_) => DbType::Number, Self::Number(_) => DbType::Number,
Self::Indexable(val) => Self::Indexable(val) => match val {
match val { IndexableValue::String(_) => DbType::String,
IndexableValue::String(_) => DbType::String, IndexableValue::Int(_) => DbType::Int,
IndexableValue::Int(_) => DbType::Int, IndexableValue::Uuid(_) => DbType::Uuid,
IndexableValue::UUID(_) => DbType::UUID, },
}
} }
} }
} }