Attempt to introduce basic types
This commit is contained in:
parent
ded7faf505
commit
c333849cc2
2 changed files with 217 additions and 4 deletions
217
src/main.rs
217
src/main.rs
|
|
@ -1,5 +1,222 @@
|
|||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
// ==============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.
|
||||
enum Operation {
|
||||
Select(TableName, ColumnSelection, Option<Condition>),
|
||||
Insert(TableName, InsertionValues),
|
||||
Delete(TableName, Option<Condition>),
|
||||
// Update(...),
|
||||
//
|
||||
CreateTable(TableName, TableSchema),
|
||||
CreateIndex(TableName, ColumnName), // TODO: Is this sufficient?
|
||||
// DropTable(TableName),
|
||||
}
|
||||
|
||||
type InsertionValues = Vec<(ColumnName, DbValue)>;
|
||||
|
||||
enum ColumnSelection {
|
||||
All,
|
||||
Columns(Vec<ColumnName>),
|
||||
}
|
||||
|
||||
enum Condition {
|
||||
// And(Box<Condition>, Box<Condition>),
|
||||
// Or(Box<Condition>, Box<Condition>),
|
||||
// Not(Box<Condition>),
|
||||
|
||||
Eq(ColumnName, DbValue),
|
||||
// LessOrEqual(ColumnName, DbValue),
|
||||
// Less(ColumnName, DbValue),
|
||||
|
||||
// StringCondition(StringCondition),
|
||||
}
|
||||
|
||||
// enum StringCondition {
|
||||
// Prefix(ColumnName, String),
|
||||
// Substring(ColumnName, String),
|
||||
// }
|
||||
|
||||
|
||||
// ==============Values and Types================
|
||||
type UUID = u64;
|
||||
|
||||
// TODO: What about nulls? I would rather not have that as in SQL, it sucks.
|
||||
// I would rather have non-nullable values by default,
|
||||
// and something like an explicit Option type for nulls.
|
||||
enum DbValue {
|
||||
String(String),
|
||||
Int(u64),
|
||||
Number(f64),
|
||||
UUID(UUID),
|
||||
}
|
||||
|
||||
// TODO: Can this be autogenerated from the values?
|
||||
enum DbType {
|
||||
String,
|
||||
Int,
|
||||
Number,
|
||||
UUID,
|
||||
}
|
||||
|
||||
impl DbValue {
|
||||
// TODO: Can this be autogenerated?
|
||||
fn to_type(self) -> DbType {
|
||||
match self {
|
||||
Self::String(_) => DbType::String,
|
||||
Self::Int(_) => DbType::Int,
|
||||
Self::Number(_) => DbType::Number,
|
||||
Self::UUID(_) => DbType::UUID,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ==============Tables================
|
||||
// table-metadata and data
|
||||
|
||||
type TableName = String;
|
||||
type TablePosition = u32;
|
||||
|
||||
struct Table {
|
||||
schema: TableSchema,
|
||||
rows: Rows,
|
||||
indexes:
|
||||
HashMap<ColumnPosition, ColumnIndex> // TODO: Consider generalizing `ColumnPosition` to something that would also apply to a pair of `ColumnNames` etc
|
||||
}
|
||||
|
||||
// TODO: Is this really indexed by DbValues?
|
||||
// Maybe we should have a separate index type for each type of value we're indexing over
|
||||
struct ColumnIndex {
|
||||
index: BTreeMap<DbValue, UUID>
|
||||
}
|
||||
|
||||
// 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.
|
||||
struct TableSchema {
|
||||
columns: HashMap<ColumnName, (DbType, ColumnPosition)>
|
||||
}
|
||||
|
||||
// TODO
|
||||
fn column_position(table_meta: TableSchema, column_name: ColumnName) -> Option<ColumnPosition> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// Use `TablePosition` as index
|
||||
type Tables = Vec<Table>;
|
||||
|
||||
|
||||
type ColumnName = String;
|
||||
type ColumnPosition = u32;
|
||||
|
||||
// Use `ColumnPosition` as index
|
||||
type Row = Vec<DbValue>;
|
||||
|
||||
type Rows =
|
||||
BTreeMap<UUID, Row>;
|
||||
|
||||
// ==============Interpreter================
|
||||
struct State {
|
||||
table_positions: HashMap<TableName, TablePosition>,
|
||||
tables: Vec<Table>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn table_from_name<'b: 'a, 'a>(&'b self, table_name: TableName) -> Option<&'a Table> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn attach_table(&mut self, table: Table) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Give a better name to something that you can respond to with rows
|
||||
trait SqlConsumer {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
// TODO: This should return a reference to the table
|
||||
// 'tables_life contains 'table_life
|
||||
fn get_table<'tables_life: 'table_life, 'table_life>(tables: &'tables_life Tables, table_name: &TableName) -> &'table_life Table {
|
||||
// let table_position:
|
||||
todo!()
|
||||
}
|
||||
|
||||
// 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
|
||||
fn interpret(table_name: TableName, operation: Operation, state: &mut State, consumer: impl SqlConsumer) -> () {
|
||||
// TODO: lock stuff
|
||||
use Operation::*;
|
||||
|
||||
match operation {
|
||||
Select(table_name, column_selection, maybe_condition) => {
|
||||
let table: &Table = todo!();
|
||||
table.select_where(column_selection, maybe_condition, consumer)
|
||||
},
|
||||
Insert(table_name, values) => {
|
||||
let table: &mut Table = todo!();
|
||||
|
||||
table.insert(values)
|
||||
},
|
||||
Delete(table_name, maybe_condition) => {
|
||||
let table: &mut Table = todo!();
|
||||
|
||||
table.delete_where(maybe_condition)
|
||||
},
|
||||
CreateTable(table_name, table_schema) => {
|
||||
let table = Table::new(table_name, table_schema);
|
||||
state.attach_table(table);
|
||||
todo!()
|
||||
},
|
||||
CreateIndex(table_name, column_name) => {
|
||||
let table: &mut Table = todo!();
|
||||
|
||||
let index: ColumnIndex = ColumnIndex::new(table, column_name);
|
||||
table.attach_index(index);
|
||||
}, // TODO: Is this sufficient?
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
impl ColumnIndex {
|
||||
fn new(table: &Table, column_name: ColumnName) -> ColumnIndex {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Table {
|
||||
fn new(table_name: TableName, table_schema: TableSchema) -> Table {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn attach_index(&mut self, column_index: ColumnIndex) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn select_where(&self, column_selection: ColumnSelection, maybe_condition: Option<Condition>, consumer: impl SqlConsumer) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn insert(&mut self, values: InsertionValues) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn delete_where(&mut self, maybe_condition: Option<Condition>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
// enum Response {
|
||||
// Selected(impl Iter<???>), // TODO: How to do this? Some reference to an iterator somehow... slice..?
|
||||
// Inserted(???),
|
||||
// Deleted(usize), // how many were deleted
|
||||
// }
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue