623 lines
18 KiB
Rust
623 lines
18 KiB
Rust
use crate::schema::{Column, TableName, TableSchema};
|
|
use crate::internals::table::Table;
|
|
use crate::operation::{Operation, Condition};
|
|
use crate::result::DbResult;
|
|
use bimap::BiMap;
|
|
use serde::{Deserialize, Serialize};
|
|
use crate::restricted_row::RestrictedRow;
|
|
|
|
// Use `TablePosition` as index
|
|
pub type Tables = Vec<Table>;
|
|
pub type TablePosition = usize;
|
|
|
|
// ==============Interpreter================
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct State {
|
|
table_name_position_mapping: BiMap<TableName, TablePosition>,
|
|
tables: Tables,
|
|
}
|
|
|
|
// #[derive(Debug)]
|
|
pub enum Response<'a> {
|
|
Selected(&'a TableSchema, Box<dyn Iterator<Item=RestrictedRow> + 'a + Send>),
|
|
Inserted,
|
|
Deleted(usize), // how many were deleted
|
|
TableCreated,
|
|
IndexCreated,
|
|
}
|
|
|
|
pub type DbSchema<'a> = Vec<(TableName, TablePosition, &'a TableSchema)>;
|
|
|
|
impl std::fmt::Debug for Response<'_> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
|
use Response::*;
|
|
match self {
|
|
Selected(_schema, _rows) =>
|
|
// TODO: How can we iterate through the rows without having to take ownership of
|
|
// them?
|
|
f.write_str("Some rows... trust me"),
|
|
Inserted =>
|
|
f.write_str("Inserted"),
|
|
Deleted(usize) =>
|
|
f.write_fmt(format_args!("Deleted({})", usize)),
|
|
TableCreated =>
|
|
f.write_str("TableCreated"),
|
|
IndexCreated =>
|
|
f.write_str("IndexCreated"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl State {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
table_name_position_mapping: BiMap::new(),
|
|
tables: vec![],
|
|
}
|
|
}
|
|
|
|
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 {
|
|
let table_schema = self.tables[table_position].schema();
|
|
schema.push((table_name.clone(), table_position, table_schema));
|
|
}
|
|
schema
|
|
}
|
|
|
|
fn table_at<'a>(&'a self, table_position: TablePosition) -> &'a Table {
|
|
&self.tables[table_position]
|
|
}
|
|
|
|
fn table_at_mut<'a>(&'a mut self, table_position: TablePosition) -> &'a mut Table {
|
|
&mut self.tables[table_position]
|
|
}
|
|
|
|
fn attach_table(&mut self, table: Table) {
|
|
let new_table_position: TablePosition = self.tables.len();
|
|
self.table_name_position_mapping
|
|
.insert(table.schema().table_name().clone(), new_table_position);
|
|
self.tables.push(table);
|
|
}
|
|
|
|
pub fn interpret<'a>(&'a mut self, operation: Operation) -> DbResult<Response<'a>> {
|
|
// TODO: lock stuff
|
|
use Operation::*;
|
|
|
|
match operation {
|
|
Select(table_position, column_selection, maybe_condition) => {
|
|
let table: &Table = self.table_at(table_position);
|
|
|
|
let selected_rows = match maybe_condition {
|
|
None => {
|
|
let rows = table.select_all_rows(column_selection);
|
|
Box::new(rows) as Box<dyn Iterator<Item=RestrictedRow> + 'a + Send>
|
|
},
|
|
|
|
Some(Condition::Eq(eq_column, value)) => {
|
|
let x =
|
|
table.select_rows_where_eq(
|
|
column_selection,
|
|
eq_column,
|
|
value,
|
|
)?;
|
|
Box::new(x) as Box<dyn Iterator<Item=RestrictedRow> + 'a + Send>
|
|
}
|
|
};
|
|
|
|
Ok(Response::Selected(table.schema(), selected_rows))
|
|
},
|
|
Insert(table_position, values) => {
|
|
let table: &mut Table = self.table_at_mut(table_position);
|
|
|
|
let (id, row) = table.schema().row_from_insertion_values(values)?;
|
|
table.insert_row_at(id, row)?;
|
|
Ok(Response::Inserted)
|
|
}
|
|
Delete(table_position, maybe_condition) => {
|
|
let table: &mut Table = self.table_at_mut(table_position);
|
|
|
|
let rows_affected = match maybe_condition {
|
|
None => table.delete_all_rows(),
|
|
Some(Condition::Eq(eq_column, value)) => {
|
|
table.delete_rows_where_eq(eq_column, value)?
|
|
}
|
|
};
|
|
|
|
Ok(Response::Deleted(rows_affected))
|
|
}
|
|
CreateTable(table_schema) => {
|
|
let table = Table::new(table_schema);
|
|
self.attach_table(table);
|
|
|
|
Ok(Response::TableCreated)
|
|
}
|
|
CreateIndex(table_position, column) => {
|
|
let table: &mut Table = self.table_at_mut(table_position);
|
|
table.attach_index(column)?;
|
|
Ok(Response::IndexCreated)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::schema::Column;
|
|
use std::collections::HashSet;
|
|
use crate::type_system::{DbType, IndexableValue, Value};
|
|
use crate::operation::Operation;
|
|
|
|
fn users_schema() -> TableSchema {
|
|
TableSchema::new(
|
|
"users".to_string(),
|
|
"id".to_string(),
|
|
vec!(
|
|
"id".to_string(),
|
|
"name".to_string(),
|
|
"age".to_string(),
|
|
),
|
|
vec![DbType::Uuid, DbType::String, DbType::Int],
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn test_table_creation() {
|
|
let mut state = State::new();
|
|
let users_schema = users_schema();
|
|
let users = users_schema.table_name().clone();
|
|
|
|
state
|
|
.interpret(Operation::CreateTable(users_schema))
|
|
.unwrap();
|
|
|
|
assert!(state.tables.len() == 1);
|
|
let table = &state.tables[0];
|
|
assert!(table.rows().len() == 0);
|
|
|
|
assert!(table.table_name() == &users);
|
|
}
|
|
|
|
#[test]
|
|
fn test_select_empty() {
|
|
let mut state = State::new();
|
|
let users_schema = users_schema();
|
|
let users_position = 0;
|
|
|
|
state
|
|
.interpret(Operation::CreateTable(users_schema.clone()))
|
|
.unwrap();
|
|
let response: Response = state
|
|
.interpret(Operation::Select(users_position, users_schema.all_selection(), None))
|
|
.unwrap();
|
|
assert!(matches!(response, Response::Selected(_, _)));
|
|
let Response::Selected(_schema, rows) = response else {
|
|
panic!()
|
|
};
|
|
let rows: Vec<_> = rows.collect();
|
|
assert!(rows.len() == 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_insert_select_basic1() {
|
|
use IndexableValue::*;
|
|
use Value::*;
|
|
|
|
let mut state = State::new();
|
|
let users_schema = users_schema();
|
|
let users = 0;
|
|
|
|
|
|
state
|
|
.interpret(Operation::CreateTable(users_schema.clone()))
|
|
.unwrap();
|
|
|
|
let (id, name, age) = (
|
|
Indexable(Uuid(0)),
|
|
Indexable(String("Plato".to_string())),
|
|
Indexable(Int(64)),
|
|
);
|
|
state
|
|
.interpret(Operation::Insert(
|
|
users,
|
|
vec![
|
|
id.clone(),
|
|
name.clone(),
|
|
age.clone(),
|
|
],
|
|
))
|
|
.unwrap();
|
|
|
|
let response: Response = state
|
|
.interpret(Operation::Select(users, users_schema.all_selection(), None))
|
|
.unwrap();
|
|
|
|
assert!(matches!(response, Response::Selected(_, _)));
|
|
let Response::Selected(_schema, rows) = response else {
|
|
panic!()
|
|
};
|
|
let rows: Vec<_> = rows.collect();
|
|
assert!(rows.len() == 1);
|
|
let row = &rows[0];
|
|
|
|
assert!(row.len() == 3);
|
|
assert!(row[0].1 == id);
|
|
assert!(row[1].1 == name);
|
|
assert!(row[2].1 == age);
|
|
}
|
|
|
|
#[test]
|
|
fn test_insert_select_basic2() {
|
|
use Condition::*;
|
|
use IndexableValue::*;
|
|
use Operation::*;
|
|
use Value::*;
|
|
|
|
let mut state = State::new();
|
|
let users_schema = users_schema();
|
|
let users_position: TablePosition = 0;
|
|
|
|
let id_column: Column = 0;
|
|
let name_column: Column = 1;
|
|
|
|
state
|
|
.interpret(CreateTable(users_schema.clone()))
|
|
.unwrap();
|
|
|
|
let (id0, name0, age0) = (
|
|
Indexable(Uuid(0)),
|
|
Indexable(String("Plato".to_string())),
|
|
Indexable(Int(64)),
|
|
);
|
|
state
|
|
.interpret(Insert(
|
|
users_position,
|
|
vec![
|
|
id0.clone(),
|
|
name0.clone(),
|
|
age0.clone(),
|
|
],
|
|
))
|
|
.unwrap();
|
|
|
|
let (id1, name1, age1) = (
|
|
Indexable(Uuid(1)),
|
|
Indexable(String("Aristotle".to_string())),
|
|
Indexable(Int(20)),
|
|
);
|
|
state
|
|
.interpret(Insert(
|
|
users_position,
|
|
vec![
|
|
id1.clone(),
|
|
name1.clone(),
|
|
age1.clone(),
|
|
],
|
|
))
|
|
.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 {
|
|
panic!()
|
|
};
|
|
|
|
let rows: Vec<_> = rows.collect();
|
|
assert!(rows.len() == 2);
|
|
let row0 = &rows[0];
|
|
let row1 = &rows[1];
|
|
|
|
assert!(row0.len() == 3);
|
|
assert!(row0[0].1 == id0);
|
|
assert!(row0[1].1 == name0);
|
|
assert!(row0[2].1 == age0);
|
|
|
|
assert!(row1.len() == 3);
|
|
assert!(row1[0].1 == id1);
|
|
assert!(row1[1].1 == name1);
|
|
assert!(row1[2].1 == age1);
|
|
}
|
|
|
|
{
|
|
let response: Response = state
|
|
.interpret(Select(
|
|
users_position,
|
|
users_schema.all_selection(),
|
|
Some(Eq(id_column, id0.clone())),
|
|
))
|
|
.unwrap();
|
|
assert!(matches!(response, Response::Selected(_, _)));
|
|
let Response::Selected(_, rows) = response else {
|
|
panic!()
|
|
};
|
|
let rows: Vec<_> = rows.collect();
|
|
assert!(rows.len() == 1);
|
|
let row0 = &rows[0];
|
|
|
|
assert!(row0.len() == 3);
|
|
assert!(row0[0].1 == id0);
|
|
assert!(row0[1].1 == name0);
|
|
assert!(row0[2].1 == age0);
|
|
}
|
|
|
|
{
|
|
let response: Response = state
|
|
.interpret(Select(
|
|
users_position,
|
|
vec![name_column, id_column],
|
|
Some(Eq(id_column, id0.clone())),
|
|
))
|
|
.unwrap();
|
|
assert!(matches!(response, Response::Selected(_, _)));
|
|
let Response::Selected(_, rows) = response else {
|
|
panic!()
|
|
};
|
|
let rows: Vec<_> = rows.collect();
|
|
assert!(rows.len() == 1);
|
|
let row0 = &rows[0];
|
|
|
|
assert!(row0.len() == 2);
|
|
assert!(row0[0].1 == name0);
|
|
assert!(row0[1].1 == id0);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_delete() {
|
|
use Condition::*;
|
|
use IndexableValue::*;
|
|
use Operation::*;
|
|
use Value::*;
|
|
|
|
let mut state = State::new();
|
|
let users_schema = users_schema();
|
|
let users_position: TablePosition = 0;
|
|
|
|
let id_column: Column = 0;
|
|
|
|
state
|
|
.interpret(CreateTable(users_schema.clone()))
|
|
.unwrap();
|
|
|
|
let (id0, name0, age0) = (
|
|
Indexable(Uuid(0)),
|
|
Indexable(String("Plato".to_string())),
|
|
Indexable(Int(64)),
|
|
);
|
|
state
|
|
.interpret(Insert(
|
|
users_position,
|
|
vec![
|
|
id0.clone(),
|
|
name0.clone(),
|
|
age0.clone(),
|
|
],
|
|
))
|
|
.unwrap();
|
|
|
|
let (id1, name1, age1) = (
|
|
Indexable(Uuid(1)),
|
|
Indexable(String("Aristotle".to_string())),
|
|
Indexable(Int(20)),
|
|
);
|
|
state
|
|
.interpret(Insert(
|
|
users_position,
|
|
vec![
|
|
id1.clone(),
|
|
name1.clone(),
|
|
age1.clone(),
|
|
],
|
|
))
|
|
.unwrap();
|
|
|
|
{
|
|
let delete_response: Response = state
|
|
.interpret(Delete(
|
|
users_position,
|
|
Some(Eq(id_column, id0.clone())),
|
|
))
|
|
.unwrap();
|
|
assert!(matches!(delete_response, Response::Deleted(1)));
|
|
}
|
|
|
|
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 {
|
|
panic!()
|
|
};
|
|
let rows: Vec<_> = rows.collect();
|
|
assert!(rows.len() == 1);
|
|
let row = &rows[0];
|
|
|
|
assert!(row.len() == 3);
|
|
assert!(row[0].1 == id1);
|
|
assert!(row[1].1 == name1);
|
|
assert!(row[2].1 == age1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_index() {
|
|
use IndexableValue::*;
|
|
use Operation::*;
|
|
use Value::*;
|
|
|
|
let mut state = State::new();
|
|
let users_schema = users_schema();
|
|
let users_position: TablePosition = 0;
|
|
|
|
let name_column: Column = 1;
|
|
|
|
state
|
|
.interpret(CreateTable(users_schema.clone()))
|
|
.unwrap();
|
|
|
|
state
|
|
.interpret(CreateIndex(users_position, name_column))
|
|
.unwrap();
|
|
|
|
let (id0, name0, age0) = (
|
|
Indexable(Uuid(0)),
|
|
Indexable(String("Plato".to_string())),
|
|
Indexable(Int(64)),
|
|
);
|
|
state
|
|
.interpret(Insert(
|
|
users_position,
|
|
vec![
|
|
id0.clone(),
|
|
name0.clone(),
|
|
age0.clone(),
|
|
],
|
|
))
|
|
.unwrap();
|
|
|
|
let (id1, name1, age1) = (
|
|
Indexable(Uuid(1)),
|
|
Indexable(String("Aristotle".to_string())),
|
|
Indexable(Int(20)),
|
|
);
|
|
state
|
|
.interpret(Insert(
|
|
users_position,
|
|
vec![
|
|
id1.clone(),
|
|
name1.clone(),
|
|
age1.clone(),
|
|
],
|
|
))
|
|
.unwrap();
|
|
|
|
assert!(state.tables.len() == 1);
|
|
let table = &state.tables[0];
|
|
assert!(table.rows().len() == 2);
|
|
|
|
let user: Column = 1;
|
|
assert!(table.indexes().contains_key(&user));
|
|
|
|
let index = table.indexes().get(&user).unwrap();
|
|
|
|
let plato_id = 0;
|
|
let aristotle_id = 1;
|
|
|
|
let plato_ids = index.get(&String("Plato".to_string())).cloned().unwrap_or(HashSet::new());
|
|
assert!(plato_ids.contains(&plato_id));
|
|
assert!(!plato_ids.contains(&aristotle_id));
|
|
assert!(plato_ids.len() == 1);
|
|
}
|
|
}
|
|
|
|
pub fn example() {
|
|
use crate::type_system::{IndexableValue, Value, DbType};
|
|
use Condition::*;
|
|
use IndexableValue::*;
|
|
use Operation::*;
|
|
use Value::*;
|
|
|
|
let id_column: Column = 0;
|
|
let name_column: Column = 1;
|
|
// let age_column: ColumnPosition = 2;
|
|
|
|
let users_schema: TableSchema = {
|
|
TableSchema::new(
|
|
"users".to_string(),
|
|
"id".to_string(),
|
|
vec!(
|
|
"id".to_string(), // 0
|
|
"name".to_string(), // 1
|
|
"age".to_string(), // 2
|
|
),
|
|
vec![DbType::Uuid, DbType::String, DbType::Int],
|
|
)
|
|
};
|
|
let users_position: TablePosition = 0;
|
|
|
|
let mut state = State::new();
|
|
state
|
|
.interpret(Operation::CreateTable(users_schema.clone()))
|
|
.unwrap();
|
|
|
|
let (id0, name0, age0) = (
|
|
Indexable(Uuid(0)),
|
|
Indexable(String("Plato".to_string())),
|
|
Indexable(Int(64)),
|
|
);
|
|
println!("==INSERT Plato==");
|
|
state
|
|
.interpret(Insert(
|
|
users_position,
|
|
vec![
|
|
id0.clone(),
|
|
name0.clone(),
|
|
age0.clone(),
|
|
],
|
|
))
|
|
.unwrap();
|
|
|
|
let (id1, name1, age1) = (
|
|
Indexable(Uuid(1)),
|
|
Indexable(String("Aristotle".to_string())),
|
|
Indexable(Int(20)),
|
|
);
|
|
println!("==INSERT Aristotle==");
|
|
state
|
|
.interpret(Insert(
|
|
users_position,
|
|
vec![
|
|
id1.clone(),
|
|
name1.clone(),
|
|
age1.clone(),
|
|
],
|
|
))
|
|
.unwrap();
|
|
println!();
|
|
|
|
{
|
|
let response: Response = state
|
|
.interpret(Operation::Select(users_position, users_schema.all_selection(), None))
|
|
.unwrap();
|
|
println!("==SELECT ALL==");
|
|
println!("{:?}", response);
|
|
println!();
|
|
}
|
|
{
|
|
let response: Response = state
|
|
.interpret(Select(
|
|
users_position,
|
|
users_schema.all_selection(),
|
|
Some(Eq(id_column, id0.clone())),
|
|
))
|
|
.unwrap();
|
|
println!("==SELECT Plato==");
|
|
println!("{:?}", response);
|
|
println!();
|
|
}
|
|
|
|
{
|
|
{
|
|
// TODO: Why do I have to write these braces explicitely? Why doesn't Rust compiler
|
|
// "infer" them?
|
|
let _delete_response: Response = state
|
|
.interpret(Delete(
|
|
users_position,
|
|
Some(Eq(id_column, id0.clone())),
|
|
))
|
|
.unwrap();
|
|
println!("==DELETE Plato==");
|
|
}
|
|
let response: Response = state
|
|
.interpret(Select(
|
|
users_position,
|
|
vec![name_column, id_column],
|
|
None,
|
|
))
|
|
.unwrap();
|
|
println!("==SELECT All==");
|
|
println!("{:?}", response);
|
|
println!();
|
|
}
|
|
}
|