From c0a472567d92965edfe9d0010aeb5fb4445b29a1 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Fri, 29 Aug 2025 23:20:27 -0400 Subject: [PATCH 1/4] Move table definition into db/ --- src/cli/ast/common.rs | 3 ++- src/cli/ast/create_statement.rs | 5 +++-- src/cli/ast/insert_statement.rs | 3 ++- src/cli/ast/mod.rs | 5 +++-- src/cli/ast/select_statement.rs | 2 +- src/cli/mod.rs | 1 - src/db/mod.rs | 2 ++ src/{cli => db}/table.rs | 1 - src/main.rs | 1 + 9 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 src/db/mod.rs rename src/{cli => db}/table.rs (97%) diff --git a/src/cli/ast/common.rs b/src/cli/ast/common.rs index 48f1739..ab9356d 100644 --- a/src/cli/ast/common.rs +++ b/src/cli/ast/common.rs @@ -1,4 +1,5 @@ -use crate::cli::{ast::parser::Parser, table::Value, tokenizer::token::TokenTypes}; +use crate::cli::{ast::parser::Parser, tokenizer::token::TokenTypes}; +use crate::db::table::Value; use hex::decode; // Returns an error if the current token does not match the given token type diff --git a/src/cli/ast/create_statement.rs b/src/cli/ast/create_statement.rs index 2514abf..983ae16 100644 --- a/src/cli/ast/create_statement.rs +++ b/src/cli/ast/create_statement.rs @@ -1,4 +1,5 @@ -use crate::cli::{ast::{parser::Parser, CreateTableStatement, SqlStatement::{self, CreateTable}, common::expect_token_type}, table::{ColumnDefinition, DataType}, tokenizer::token::TokenTypes}; +use crate::cli::{ast::{parser::Parser, CreateTableStatement, SqlStatement::{self, CreateTable}, common::expect_token_type}, tokenizer::token::TokenTypes}; +use crate::db::table::{ColumnDefinition, DataType}; pub fn build(parser: &mut Parser) -> Result { parser.advance()?; @@ -36,7 +37,7 @@ fn table_statement(parser: &mut Parser) -> Result { } fn column_definitions(parser: &mut Parser) -> Result, String> { - let mut columns: Vec = vec![]; + let mut columns: Vec = vec![]; expect_token_type(parser, TokenTypes::LeftParen)?; parser.advance()?; diff --git a/src/cli/ast/insert_statement.rs b/src/cli/ast/insert_statement.rs index 6f0adb6..e4edebd 100644 --- a/src/cli/ast/insert_statement.rs +++ b/src/cli/ast/insert_statement.rs @@ -1,4 +1,5 @@ -use crate::cli::{ast::{parser::Parser, common::token_to_value, common::expect_token_type, InsertIntoStatement, SqlStatement::{self, InsertInto}}, table::Value, tokenizer::token::TokenTypes}; +use crate::cli::{ast::{parser::Parser, common::token_to_value, common::expect_token_type, InsertIntoStatement, SqlStatement::{self, InsertInto}}, tokenizer::token::TokenTypes}; +use crate::db::table::Value; pub fn build(parser: &mut Parser) -> Result { parser.advance()?; diff --git a/src/cli/ast/mod.rs b/src/cli/ast/mod.rs index 6e76401..bfacf90 100644 --- a/src/cli/ast/mod.rs +++ b/src/cli/ast/mod.rs @@ -1,4 +1,5 @@ -use crate::cli::{self, table::{ColumnDefinition, Value}, tokenizer::token::TokenTypes}; +use crate::cli::tokenizer::{scanner::Token, token::TokenTypes}; +use crate::db::table::{ColumnDefinition, Value}; mod common; mod create_statement; @@ -98,7 +99,7 @@ impl StatementBuilder for DefaultStatementBuilder { } } -pub fn generate(tokens: Vec) -> Vec> { +pub fn generate(tokens: Vec) -> Vec> { let mut results: Vec> = vec![]; let mut parser = parser::Parser::new(tokens); let builder : &dyn StatementBuilder = &DefaultStatementBuilder; diff --git a/src/cli/ast/select_statement.rs b/src/cli/ast/select_statement.rs index e992222..930e730 100644 --- a/src/cli/ast/select_statement.rs +++ b/src/cli/ast/select_statement.rs @@ -153,7 +153,7 @@ fn get_limit(parser: &mut Parser) -> Result, String> { mod tests { use super::*; use crate::cli::tokenizer::scanner::Token; - use crate::cli::table::Value; + use crate::db::table::Value; fn token(tt: TokenTypes, val: &'static str) -> Token<'static> { Token { diff --git a/src/cli/mod.rs b/src/cli/mod.rs index d168cd1..75e3356 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,6 +1,5 @@ use std::io; mod ast; -mod table; mod tokenizer; pub fn cli() { diff --git a/src/db/mod.rs b/src/db/mod.rs new file mode 100644 index 0000000..a995cb1 --- /dev/null +++ b/src/db/mod.rs @@ -0,0 +1,2 @@ + +pub mod table; \ No newline at end of file diff --git a/src/cli/table.rs b/src/db/table.rs similarity index 97% rename from src/cli/table.rs rename to src/db/table.rs index c91d190..10a0449 100644 --- a/src/cli/table.rs +++ b/src/db/table.rs @@ -12,7 +12,6 @@ pub struct _Table { name: String, columns: Vec, rows: Vec<_Row>, - length: usize, } #[derive(Debug, PartialEq)] diff --git a/src/main.rs b/src/main.rs index dacf017..674b10d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod cli; +mod db; fn main() { cli::cli(); From 90926521287dc1f13b8e48d244f9c3df09bfdd03 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Sat, 30 Aug 2025 00:18:38 -0400 Subject: [PATCH 2/4] Implement create table and implement insert row --- src/cli/mod.rs | 17 ++++++-- src/db/database.rs | 60 ++++++++++++++++++++++++++ src/db/mod.rs | 4 +- src/db/table.rs | 102 +++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 169 insertions(+), 14 deletions(-) create mode 100644 src/db/database.rs diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 75e3356..cea82f8 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,11 +1,13 @@ use std::io; -mod ast; +use crate::db; +pub mod ast; mod tokenizer; pub fn cli() { clear_screen(); println!("Welcome to the MollyDB CLI"); let mut line_count = 1; + let mut database = db::database::Database::new(); loop { print!("({:03}) > ", line_count); @@ -30,8 +32,17 @@ pub fn cli() { let tokens = tokenizer::tokenize(input); println!("{:?}", tokens); let ast = ast::generate(tokens); - for result in ast { - println!("{:?}", result); + for sql_statement in ast { + println!("{:?}", sql_statement); + match sql_statement { + Ok(statement) => { + let result = database.execute(statement); + println!("{:?}", result); + }, + Err(error) => { + println!("Error: {}", error); + }, + } } } } diff --git a/src/db/database.rs b/src/db/database.rs new file mode 100644 index 0000000..0560c18 --- /dev/null +++ b/src/db/database.rs @@ -0,0 +1,60 @@ +use crate::db::table::Table; +use crate::cli::ast::{SqlStatement, CreateTableStatement, InsertIntoStatement, SelectStatement}; +use std::collections::HashMap; + +pub struct Database { + tables: HashMap, +} + +impl Database { + pub fn new() -> Self { + Self { + tables: HashMap::new(), + } + } + + pub fn execute(&mut self, sql_statement: SqlStatement) -> Result<(), String> { + return match sql_statement { + SqlStatement::CreateTable(statement) => self.create_table(statement), + SqlStatement::InsertInto(statement) => self.insert_into_table(statement), + SqlStatement::Select(statement) => self.select_from_table(statement), + } + } + + fn create_table(&mut self, statement: CreateTableStatement) -> Result<(), String> { + if self.has_table(&statement.table_name) { + return Err(format!("Table {} already exists", statement.table_name)); + } + let table_name = statement.table_name; + self.tables.insert(table_name.clone(), Table::new(table_name, statement.columns)); + Ok(()) + } + + fn insert_into_table(&mut self, statement: InsertIntoStatement) -> Result<(), String> { + let table = self.get_table_mut(&statement.table_name)?; + table.insert(statement)?; + Ok(()) + } + + fn select_from_table(&mut self, _statement: SelectStatement) -> Result<(), String> { + todo!() + } + + fn has_table(&self, table_name: &str) -> bool { + self.tables.contains_key(table_name) + } + + fn _get_table(&self, table_name: &str) -> Result<&Table, String> { + if !self.has_table(table_name) { + return Err(format!("Table {} does not exist", table_name)); + } + Ok(self.tables.get(table_name).unwrap()) + } + + fn get_table_mut(&mut self, table_name: &str) -> Result<&mut Table, String> { + if !self.has_table(table_name) { + return Err(format!("Table {} does not exist", table_name)); + } + Ok(self.tables.get_mut(table_name).unwrap()) + } +} \ No newline at end of file diff --git a/src/db/mod.rs b/src/db/mod.rs index a995cb1..b66a9b6 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,2 +1,2 @@ - -pub mod table; \ No newline at end of file +pub mod database; +pub mod table; diff --git a/src/db/table.rs b/src/db/table.rs index 10a0449..611d411 100644 --- a/src/db/table.rs +++ b/src/db/table.rs @@ -1,3 +1,4 @@ +use crate::cli::ast::{InsertIntoStatement, SelectStatement}; #[derive(Debug, PartialEq)] pub enum DataType { @@ -8,11 +9,7 @@ pub enum DataType { Null, } -pub struct _Table { - name: String, - columns: Vec, - rows: Vec<_Row>, -} + #[derive(Debug, PartialEq)] pub struct ColumnDefinition { @@ -26,10 +23,6 @@ pub struct ColumnConstraint { pub constraint_type: String, } -struct _Row { - primary_key: usize, - values: Vec, -} #[derive(Debug, PartialEq)] pub enum Value { @@ -39,3 +32,94 @@ pub enum Value { Blob(Vec), Null } + +impl Value { + pub fn get_type(&self) -> DataType { + match self { + Value::Integer(_) => DataType::Integer, + Value::Real(_) => DataType::Real, + Value::Text(_) => DataType::Text, + Value::Blob(_) => DataType::Blob, + Value::Null => DataType::Null, + } + } + + pub fn clone(&self) -> Value { + match self { + Value::Integer(value) => Value::Integer(*value), + Value::Real(value) => Value::Real(*value), + Value::Text(value) => Value::Text(value.clone()), + Value::Blob(value) => Value::Blob(value.clone()), + Value::Null => Value::Null, + } + } +} + +pub struct Table { + name: String, + columns: Vec, + rows: Vec>, +} + +impl Table { + pub fn new(name: String, columns: Vec) -> Self { + Self { + name, + columns, + rows: vec![], + } + } + + pub fn insert(&mut self, statement: InsertIntoStatement) -> Result<(), String> { + // Validate columns + if let Some(columns) = statement.columns { + if columns.len() != self.columns.len() { + return Err(format!("Columns have incorrect width")); + } + for (i, column) in columns.iter().enumerate() { + if column != &self.columns[i].name { + return Err(format!("Column mismatch")); + } + } + } + + let mut rows: Vec> = vec![]; + // Validate row inserts + for row in statement.values { + if row.len() != self.width() { + return Err(format!("Rows have incorrect width")); + } + let row_values = self.validate_and_clone_row(row)?; + rows.push(row_values); + } + + // Insert rows + for row in rows { + self.rows.push(row); + } + return Ok(()); + } + + pub fn select(&self, statement: SelectStatement) { + todo!() + } + + fn width(&self) -> usize { + self.columns.len() + } + + fn validate_and_clone_row(&self, row: Vec) -> Result, String> { + if row.len() != self.width() { + return Err(format!("Rows have incorrect width")); + } + + let mut row_values: Vec = vec![]; + for (i, value) in row.iter().enumerate() { + if value.get_type() != self.columns[i].data_type && value.get_type() != DataType::Null { + return Err(format!("Data type mismatch for column {}", self.columns[i].name)); + } + row_values.push(row[i].clone()); + } + return Ok(row_values); + } +} \ No newline at end of file From 55b90283b1d2db5abb0523bb50f410268c51a53a Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Sat, 30 Aug 2025 21:35:05 -0400 Subject: [PATCH 3/4] First attempt at getting select working with Where clause, Inserting into table is currently broken --- src/cli/ast/select_statement.rs | 1 - src/cli/mod.rs | 14 ++++- src/db/database.rs | 27 +++++++--- src/db/table.rs | 93 +++++++++++++++++++++++++++++---- 4 files changed, 116 insertions(+), 19 deletions(-) diff --git a/src/cli/ast/select_statement.rs b/src/cli/ast/select_statement.rs index 930e730..548e065 100644 --- a/src/cli/ast/select_statement.rs +++ b/src/cli/ast/select_statement.rs @@ -41,7 +41,6 @@ fn get_table_name(parser: &mut Parser) -> Result { Ok(result) } - fn get_where_clause(parser: &mut Parser) -> Result, String> { if expect_token_type(parser, TokenTypes::Where).is_err() { return Ok(None); diff --git a/src/cli/mod.rs b/src/cli/mod.rs index cea82f8..aefe93b 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -37,7 +37,19 @@ pub fn cli() { match sql_statement { Ok(statement) => { let result = database.execute(statement); - println!("{:?}", result); + if let Ok(result) = result { + if let Some(rows) = result { + for row in rows { + println!("{:?}", row); + } + } + else { + println!("Executed Successfully"); + } + } + else { + println!("Error: {}", result.unwrap_err()); + } }, Err(error) => { println!("Error: {}", error); diff --git a/src/db/database.rs b/src/db/database.rs index 0560c18..f13e221 100644 --- a/src/db/database.rs +++ b/src/db/database.rs @@ -1,4 +1,4 @@ -use crate::db::table::Table; +use crate::db::table::{Table, Value}; use crate::cli::ast::{SqlStatement, CreateTableStatement, InsertIntoStatement, SelectStatement}; use std::collections::HashMap; @@ -13,11 +13,20 @@ impl Database { } } - pub fn execute(&mut self, sql_statement: SqlStatement) -> Result<(), String> { + pub fn execute(&mut self, sql_statement: SqlStatement) -> Result>>, String> { return match sql_statement { - SqlStatement::CreateTable(statement) => self.create_table(statement), - SqlStatement::InsertInto(statement) => self.insert_into_table(statement), - SqlStatement::Select(statement) => self.select_from_table(statement), + SqlStatement::CreateTable(statement) => { + self.create_table(statement)?; + Ok(None) + }, + SqlStatement::InsertInto(statement) => { + self.insert_into_table(statement)?; + Ok(None) + }, + SqlStatement::Select(statement) => { + let rows = self.select_from_table(statement)?; + Ok(Some(rows)) + }, } } @@ -36,15 +45,17 @@ impl Database { Ok(()) } - fn select_from_table(&mut self, _statement: SelectStatement) -> Result<(), String> { - todo!() + fn select_from_table(&mut self, statement: SelectStatement) -> Result>, String> { + let table = self.get_table(&statement.table_name)?; + let rows = table.select(statement)?; + Ok(rows) } fn has_table(&self, table_name: &str) -> bool { self.tables.contains_key(table_name) } - fn _get_table(&self, table_name: &str) -> Result<&Table, String> { + fn get_table(&self, table_name: &str) -> Result<&Table, String> { if !self.has_table(table_name) { return Err(format!("Table {} does not exist", table_name)); } diff --git a/src/db/table.rs b/src/db/table.rs index 611d411..c82adfc 100644 --- a/src/db/table.rs +++ b/src/db/table.rs @@ -1,4 +1,4 @@ -use crate::cli::ast::{InsertIntoStatement, SelectStatement}; +use crate::cli::ast::{InsertIntoStatement, Operator, SelectStatement, SelectStatementColumns, WhereClause}; #[derive(Debug, PartialEq)] pub enum DataType { @@ -24,7 +24,7 @@ pub struct ColumnConstraint { } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, PartialOrd)] pub enum Value { Integer(i64), Real(f64), @@ -56,15 +56,15 @@ impl Value { } pub struct Table { - name: String, + _name: String, columns: Vec, rows: Vec>, } impl Table { - pub fn new(name: String, columns: Vec) -> Self { + pub fn new(_name: String, columns: Vec) -> Self { Self { - name, + _name, columns, rows: vec![], } @@ -89,7 +89,7 @@ impl Table { if row.len() != self.width() { return Err(format!("Rows have incorrect width")); } - let row_values = self.validate_and_clone_row(row)?; + let row_values = self.validate_and_clone_row(&row)?; rows.push(row_values); } @@ -100,15 +100,90 @@ impl Table { return Ok(()); } - pub fn select(&self, statement: SelectStatement) { - todo!() + pub fn select(&self, statement: SelectStatement) -> Result>, String> { + let mut rows: Vec> = vec![]; + if let Some(where_clause) = statement.where_clause { + for row in self.rows.iter() { + if self.matches_where_clause(&row, &where_clause) { + rows.push(self.get_columns_from_row(&row, &statement.columns)?); + } + } + } else { + todo!() + } + return Ok(rows); + } + + fn matches_where_clause(&self, row: &Vec, where_clause: &WhereClause) -> bool { + let column_value = self.get_column_from_row(row, &where_clause.column); + if column_value.get_type() != where_clause.value.get_type() { + return false; + } + + match where_clause.operator { + Operator::Equals => { + return *column_value == where_clause.value; + }, + Operator::NotEquals => { + return *column_value != where_clause.value; + }, + _ => { + match column_value.get_type() { + DataType::Integer | DataType::Real => { + match where_clause.operator { + Operator::LessThan => { + return *column_value < where_clause.value; + }, + Operator::GreaterThan => { + return *column_value > where_clause.value; + }, + Operator::LessEquals => { + return *column_value <= where_clause.value; + }, + Operator::GreaterEquals => { + return *column_value >= where_clause.value; + }, + _ => { + return false; + }, + } + }, + _ => { + return false; + }, + } + } + } + } + + fn get_column_from_row<'a>(&self, row: &'a Vec, column: &String) -> &'a Value { + for (i, value) in row.iter().enumerate() { + if self.columns[i].name == *column { + return &value; + } + } + return &Value::Null; + } + + fn get_columns_from_row(&self, row: &Vec, columns: &SelectStatementColumns) -> Result, String> { + let mut row_values: Vec = vec![]; + if *columns == SelectStatementColumns::All { + return Ok(self.validate_and_clone_row(row)?); + } else { + for (i, column) in self.columns.iter().enumerate() { + if self.columns.contains(column) { + row_values.push(row[i].clone()); + } + } + } + return Ok(row_values); } fn width(&self) -> usize { self.columns.len() } - fn validate_and_clone_row(&self, row: Vec) -> Result, String> { + fn validate_and_clone_row(&self, row: &Vec) -> Result, String> { if row.len() != self.width() { return Err(format!("Rows have incorrect width")); } From 815b1cf8c13cf473740f4c42e69fe5a901cd0df0 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Sun, 31 Aug 2025 12:31:04 -0400 Subject: [PATCH 4/4] Basic statements are working correctly, CREATE TABLE, INSERT INTO, and SELECT are functioning as intended --- src/cli/mod.rs | 4 ++-- src/db/table.rs | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index aefe93b..a5ef2e4 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -37,8 +37,8 @@ pub fn cli() { match sql_statement { Ok(statement) => { let result = database.execute(statement); - if let Ok(result) = result { - if let Some(rows) = result { + if let Ok(values) = result { + if let Some(rows) = values { for row in rows { println!("{:?}", row); } diff --git a/src/db/table.rs b/src/db/table.rs index c82adfc..f7250df 100644 --- a/src/db/table.rs +++ b/src/db/table.rs @@ -109,7 +109,9 @@ impl Table { } } } else { - todo!() + for row in self.rows.iter() { + rows.push(self.get_columns_from_row(&row, &statement.columns)?); + } } return Ok(rows); }