Reorganization
This commit is contained in:
parent
3c57b0eb6c
commit
6b58c3cb9b
8 changed files with 945 additions and 900 deletions
430
minisql/src/interpreter.rs
Normal file
430
minisql/src/interpreter.rs
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
use bimap::BiMap;
|
||||
use crate::type_system::{DbValue, DbType, IndexableDbValue};
|
||||
use crate::base::{TableName, TableSchema, ColumnPosition, ColumnName, DbResult};
|
||||
use crate::table::{Table, Row};
|
||||
use crate::error::Error;
|
||||
use crate::operation::{Operation, Condition, ColumnSelection};
|
||||
use crate::column_index::ColumnIndex;
|
||||
|
||||
|
||||
// Use `TablePosition` as index
|
||||
pub type Tables = Vec<Table>;
|
||||
pub type TablePosition = usize;
|
||||
|
||||
// ==============Interpreter================
|
||||
#[derive(Debug)]
|
||||
pub struct State {
|
||||
table_name_position_mapping: BiMap<TableName, TablePosition>,
|
||||
tables: Tables,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Response {
|
||||
Selected(Vec<Row>),
|
||||
Inserted,
|
||||
Deleted(usize), // how many were deleted
|
||||
TableCreated,
|
||||
IndexCreated,
|
||||
}
|
||||
|
||||
|
||||
impl State {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
table_name_position_mapping: BiMap::new(),
|
||||
tables: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn table_from_name<'b: 'a, 'a>(&'b 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_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 attach_table(&mut self, table_name: TableName, table: Table) {
|
||||
let new_table_position: TablePosition = self.tables.len();
|
||||
self.table_name_position_mapping.insert(table_name, new_table_position);
|
||||
self.tables.push(table);
|
||||
}
|
||||
|
||||
// TODO: Decide if we want for this to return a response (but then you have to deal with lifetimes,
|
||||
// because you'll be forced to put an iterator/slice into the Response data-structure.
|
||||
// Alternative is to pass a row-consumer to the functionas that knows how to communicate with
|
||||
// the client, but the details of communication are hidden behind an interface
|
||||
//
|
||||
// writer: impl SqlResponseConsumer
|
||||
fn interpret(&mut self, operation: Operation) -> DbResult<Response> {
|
||||
// TODO: lock stuff
|
||||
use Operation::*;
|
||||
|
||||
match operation {
|
||||
Select(table_name, column_selection, maybe_condition) => {
|
||||
let table: &Table = self.table_from_name(&table_name)?;
|
||||
Ok(Response::Selected(table.select_where(column_selection, maybe_condition)?))
|
||||
},
|
||||
Insert(table_name, values) => {
|
||||
let table: &mut Table = self.table_from_name_mut(&table_name)?;
|
||||
|
||||
let _ = table.insert(values)?;
|
||||
Ok(Response::Inserted)
|
||||
},
|
||||
Delete(table_name, maybe_condition) => {
|
||||
let table: &mut Table = self.table_from_name_mut(&table_name)?;
|
||||
|
||||
let rows_affected = table.delete_where(maybe_condition)?;
|
||||
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)?;
|
||||
|
||||
let mut index: ColumnIndex = ColumnIndex::new();
|
||||
let _ = index.update_from_table(&table, column_position)?;
|
||||
|
||||
table.attach_index(column_position, index);
|
||||
Ok(Response::IndexCreated)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Give a better name to something that you can respond to with rows
|
||||
trait SqlResponseConsumer {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn users_schema() -> TableSchema {
|
||||
let id: ColumnPosition = 0;
|
||||
let name: ColumnPosition = 1;
|
||||
let age: ColumnPosition = 2;
|
||||
|
||||
TableSchema {
|
||||
table_name: "users".to_string(),
|
||||
primary_key: id,
|
||||
column_name_position_mapping: {
|
||||
let mut mapping: BiMap<ColumnName, ColumnPosition> = BiMap::new();
|
||||
mapping.insert("id".to_string(), id);
|
||||
mapping.insert("name".to_string(), name);
|
||||
mapping.insert("age".to_string(), age);
|
||||
mapping
|
||||
},
|
||||
types: 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.clone(), users_schema)).unwrap();
|
||||
|
||||
assert!(state.tables.len() == 1);
|
||||
let table = &state.tables[0];
|
||||
assert!(table.rows.len() == 0);
|
||||
|
||||
assert!(table.schema.table_name == users);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_empty() {
|
||||
let mut state = State::new();
|
||||
let users_schema = users_schema();
|
||||
let users = users_schema.table_name.clone();
|
||||
|
||||
state.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(_)));
|
||||
let Response::Selected(rows) = response else { todo!() };
|
||||
assert!(rows.len() == 0);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_nonexistant_table() {
|
||||
let mut state = State::new();
|
||||
|
||||
let response: DbResult<Response> = 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 DbValue::*;
|
||||
use IndexableDbValue::*;
|
||||
|
||||
let mut state = State::new();
|
||||
let users_schema = users_schema();
|
||||
let users = users_schema.table_name.clone();
|
||||
|
||||
state.interpret(Operation::CreateTable(users.clone(), users_schema)).unwrap();
|
||||
|
||||
let (id, name, age) = (
|
||||
Indexable(UUID(0)),
|
||||
Indexable(String("Plato".to_string())),
|
||||
Indexable(Int(64))
|
||||
);
|
||||
state.interpret(Operation::Insert(users.clone(), vec![
|
||||
("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();
|
||||
|
||||
assert!(matches!(response, Response::Selected(_)));
|
||||
let Response::Selected(rows) = response else { todo!() };
|
||||
assert!(rows.len() == 1);
|
||||
let row = &rows[0];
|
||||
|
||||
assert!(row.len() == 3);
|
||||
assert!(row[0] == id);
|
||||
assert!(row[1] == name);
|
||||
assert!(row[2] == age);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_select_basic2() {
|
||||
use DbValue::*;
|
||||
use IndexableDbValue::*;
|
||||
use Operation::*;
|
||||
use ColumnSelection::*;
|
||||
use Condition::*;
|
||||
|
||||
let mut state = State::new();
|
||||
let users_schema = users_schema();
|
||||
let users = users_schema.table_name.clone();
|
||||
|
||||
state.interpret(CreateTable(users.clone(), users_schema)).unwrap();
|
||||
|
||||
let (id0, name0, age0) = (
|
||||
Indexable(UUID(0)),
|
||||
Indexable(String("Plato".to_string())),
|
||||
Indexable(Int(64))
|
||||
);
|
||||
state.interpret(Insert(users.clone(), vec![
|
||||
("id".to_string(), id0.clone()),
|
||||
("name".to_string(), name0.clone()),
|
||||
("age".to_string(), age0.clone()),
|
||||
])).unwrap();
|
||||
|
||||
let (id1, name1, age1) = (
|
||||
Indexable(UUID(1)),
|
||||
Indexable(String("Aristotle".to_string())),
|
||||
Indexable(Int(20))
|
||||
);
|
||||
state.interpret(Insert(users.clone(), vec![
|
||||
("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();
|
||||
|
||||
assert!(matches!(response, Response::Selected(_)));
|
||||
let Response::Selected(rows) = response else { todo!() };
|
||||
assert!(rows.len() == 2);
|
||||
let row0 = &rows[0];
|
||||
let row1 = &rows[1];
|
||||
|
||||
assert!(row0.len() == 3);
|
||||
assert!(row0[0] == id0);
|
||||
assert!(row0[1] == name0);
|
||||
assert!(row0[2] == age0);
|
||||
|
||||
assert!(row1.len() == 3);
|
||||
assert!(row1[0] == id1);
|
||||
assert!(row1[1] == name1);
|
||||
assert!(row1[2] == age1);
|
||||
}
|
||||
|
||||
{
|
||||
let response: Response = state.interpret(Select(users.clone(), All, Some(Eq("id".to_string(), id0.clone())))).unwrap();
|
||||
assert!(matches!(response, Response::Selected(_)));
|
||||
let Response::Selected(rows) = response else { todo!() };
|
||||
assert!(rows.len() == 1);
|
||||
let row0 = &rows[0];
|
||||
|
||||
assert!(row0.len() == 3);
|
||||
assert!(row0[0] == id0);
|
||||
assert!(row0[1] == name0);
|
||||
assert!(row0[2] == age0);
|
||||
}
|
||||
|
||||
{
|
||||
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(_)));
|
||||
let Response::Selected(rows) = response else { todo!() };
|
||||
assert!(rows.len() == 1);
|
||||
let row0 = &rows[0];
|
||||
|
||||
assert!(row0.len() == 2);
|
||||
assert!(row0[0] == name0);
|
||||
assert!(row0[1] == id0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete() {
|
||||
use DbValue::*;
|
||||
use IndexableDbValue::*;
|
||||
use Operation::*;
|
||||
use ColumnSelection::*;
|
||||
use Condition::*;
|
||||
|
||||
let mut state = State::new();
|
||||
let users_schema = users_schema();
|
||||
let users = users_schema.table_name.clone();
|
||||
|
||||
state.interpret(CreateTable(users.clone(), users_schema)).unwrap();
|
||||
|
||||
let (id0, name0, age0) = (
|
||||
Indexable(UUID(0)),
|
||||
Indexable(String("Plato".to_string())),
|
||||
Indexable(Int(64))
|
||||
);
|
||||
state.interpret(Insert(users.clone(), vec![
|
||||
("id".to_string(), id0.clone()),
|
||||
("name".to_string(), name0.clone()),
|
||||
("age".to_string(), age0.clone()),
|
||||
])).unwrap();
|
||||
|
||||
let (id1, name1, age1) = (
|
||||
Indexable(UUID(1)),
|
||||
Indexable(String("Aristotle".to_string())),
|
||||
Indexable(Int(20))
|
||||
);
|
||||
state.interpret(Insert(users.clone(), vec![
|
||||
("id".to_string(), id1.clone()),
|
||||
("name".to_string(), name1.clone()),
|
||||
("age".to_string(), age1.clone()),
|
||||
])).unwrap();
|
||||
|
||||
|
||||
let delete_response: Response = state.interpret(Delete(users.clone(), Some(Eq("id".to_string(), id0.clone())))).unwrap();
|
||||
assert!(matches!(delete_response, Response::Deleted(1)));
|
||||
|
||||
let response: Response = state.interpret(Select(users.clone(), All, None)).unwrap();
|
||||
|
||||
assert!(matches!(response, Response::Selected(_)));
|
||||
let Response::Selected(rows) = response else { todo!() };
|
||||
assert!(rows.len() == 1);
|
||||
let row = &rows[0];
|
||||
|
||||
assert!(row.len() == 3);
|
||||
assert!(row[0] == id1);
|
||||
assert!(row[1] == name1);
|
||||
assert!(row[2] == age1);
|
||||
}
|
||||
|
||||
// TODO: Test CreateIndex
|
||||
}
|
||||
|
||||
|
||||
pub fn example() {
|
||||
use DbValue::*;
|
||||
use IndexableDbValue::*;
|
||||
use Operation::*;
|
||||
use ColumnSelection::*;
|
||||
use Condition::*;
|
||||
|
||||
let users_schema = {
|
||||
let id: ColumnPosition = 0;
|
||||
let name: ColumnPosition = 1;
|
||||
let age: ColumnPosition = 2;
|
||||
|
||||
TableSchema {
|
||||
table_name: "users".to_string(),
|
||||
primary_key: id,
|
||||
column_name_position_mapping: {
|
||||
let mut mapping: BiMap<ColumnName, ColumnPosition> = BiMap::new();
|
||||
mapping.insert("id".to_string(), id);
|
||||
mapping.insert("name".to_string(), name);
|
||||
mapping.insert("age".to_string(), age);
|
||||
mapping
|
||||
},
|
||||
types: vec![DbType::UUID, DbType::String, DbType::Int],
|
||||
}
|
||||
};
|
||||
let users = users_schema.table_name.clone();
|
||||
|
||||
|
||||
let mut state = State::new();
|
||||
state.interpret(Operation::CreateTable(users.clone(), users_schema)).unwrap();
|
||||
|
||||
let (id0, name0, age0) = (
|
||||
Indexable(UUID(0)),
|
||||
Indexable(String("Plato".to_string())),
|
||||
Indexable(Int(64))
|
||||
);
|
||||
println!("==INSERT Plato==");
|
||||
state.interpret(Insert(users.clone(), vec![
|
||||
("id".to_string(), id0.clone()),
|
||||
("name".to_string(), name0.clone()),
|
||||
("age".to_string(), 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.clone(), vec![
|
||||
("id".to_string(), id1.clone()),
|
||||
("name".to_string(), name1.clone()),
|
||||
("age".to_string(), age1.clone()),
|
||||
])).unwrap();
|
||||
println!();
|
||||
|
||||
{
|
||||
let response: Response = state.interpret(Operation::Select(users.clone(), ColumnSelection::All, None)).unwrap();
|
||||
println!("==SELECT ALL==");
|
||||
println!("{:?}", response);
|
||||
println!();
|
||||
}
|
||||
{
|
||||
let response: Response = state.interpret(Select(users.clone(), All, Some(Eq("id".to_string(), id0.clone())))).unwrap();
|
||||
println!("==SELECT Plato==");
|
||||
println!("{:?}", response);
|
||||
println!();
|
||||
}
|
||||
|
||||
{
|
||||
let _delete_response: Response = state.interpret(Delete(users.clone(), Some(Eq("id".to_string(), id0.clone())))).unwrap();
|
||||
println!("==DELETE Plato==");
|
||||
let response: Response = state.interpret(Select(users.clone(), Columns(vec!["name".to_string(), "id".to_string()]), None)).unwrap();
|
||||
println!("==SELECT All==");
|
||||
println!("{:?}", response);
|
||||
println!();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue