From 9771a89716193ae377202952105118579a938ae3 Mon Sep 17 00:00:00 2001 From: Yuriy Dupyn <2153100+omedusyo@users.noreply.github.com> Date: Sat, 27 Jan 2024 21:47:33 +0100 Subject: [PATCH] Use RawQuerySyntax for parsing --- minisql/src/interpreter.rs | 16 +++++++++------- parser/src/core.rs | 13 +++++-------- parser/src/parsing/common.rs | 7 +++++-- parser/src/parsing/create.rs | 13 +++++++------ parser/src/parsing/delete.rs | 10 +++++----- parser/src/parsing/index.rs | 24 ++++++++++++------------ parser/src/parsing/insert.rs | 18 ++++++++++-------- parser/src/parsing/select.rs | 16 ++++++++-------- parser/src/validation.rs | 4 +--- server/src/main.rs | 6 +++--- 10 files changed, 65 insertions(+), 62 deletions(-) diff --git a/minisql/src/interpreter.rs b/minisql/src/interpreter.rs index 7907ec9..70393cc 100644 --- a/minisql/src/interpreter.rs +++ b/minisql/src/interpreter.rs @@ -2,7 +2,7 @@ use crate::error::Error; use crate::internals::row::{ColumnPosition, Row}; use crate::schema::{TableName, TableSchema}; use crate::internals::table::Table; -use crate::operation::{ColumnSelection, Condition, Operation, OperationForInterpreter, ConditionForInterpreter, ColumnSelectionForInterpreter}; +use crate::operation::{ColumnSelection, Condition, Operation, OperationForInterpreter, ConditionForInterpreter}; use crate::result::DbResult; use crate::type_system::{DbType, IndexableValue, Value}; use bimap::BiMap; @@ -27,6 +27,8 @@ pub enum Response<'a> { 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::*; @@ -56,13 +58,13 @@ impl State { } /// TODO: return a reference to avoid allocations - pub fn metadata<'a>(&'a self) -> Vec<(String, &'a TableSchema)> { - let mut m = Vec::new(); - for (name, pos) in &self.table_name_position_mapping { - let table_schema = self.tables.get(*pos).unwrap().schema(); - m.push((name.clone(), table_schema)); + 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)); } - m + schema } // TODO: Get rid of this diff --git a/parser/src/core.rs b/parser/src/core.rs index 258c928..f173f38 100644 --- a/parser/src/core.rs +++ b/parser/src/core.rs @@ -1,4 +1,4 @@ -use minisql::{operation::Operation, schema::TableSchema}; +use minisql::{operation::OperationForInterpreter, interpreter::DbSchema}; use crate::syntax::RawQuerySyntax; use nom::{branch::alt, multi::many0, IResult}; use thiserror::Error; @@ -13,7 +13,7 @@ pub enum Error { ValidationError(#[from] ValidationError) } -pub fn parse_statement<'a>(input: &'a str) -> IResult<&str, Operation> { +pub fn parse_statement<'a>(input: &'a str) -> IResult<&str, RawQuerySyntax> { alt(( parse_insert, parse_create, @@ -25,20 +25,17 @@ pub fn parse_statement<'a>(input: &'a str) -> IResult<&str, Operation> { ))(input) } -pub fn parse_statements<'a>(input: &'a str) -> IResult<&str, Vec> { +pub fn parse_statements<'a>(input: &'a str) -> IResult<&str, Vec> { many0(parse_statement)(input) } -pub fn parse_and_validate(query: String, db_metadata: &Vec<(String, &TableSchema)>) -> Result { +pub fn parse_and_validate(query: String, db_schema: &DbSchema) -> Result { let (_, op) = parse_statement(query.as_str()) .map_err(|err| { Error::ParsingError(err.to_string()) })?; - // TODO - // validate_operation(&op, db_metadata)?; - todo!(); - Ok(op) + Ok(validate_operation(op, db_schema)?) } // #[test] diff --git a/parser/src/parsing/common.rs b/parser/src/parsing/common.rs index f71d587..6787105 100644 --- a/parser/src/parsing/common.rs +++ b/parser/src/parsing/common.rs @@ -6,8 +6,9 @@ use nom::{ bytes::complete::tag, IResult, branch::alt, }; -use minisql::{operation::Condition, type_system::DbType}; +use minisql::type_system::DbType; +use crate::syntax::Condition; use super::literal::parse_db_value; pub fn parse_table_name(input: &str) -> IResult<&str, &str> { @@ -67,7 +68,9 @@ fn parse_equality(input: &str) -> IResult<&str, Condition> { #[cfg(test)] mod tests { - use minisql::{operation::Condition, type_system::DbType}; + use minisql::type_system::DbType; + + use crate::syntax::Condition; use crate::parsing::common::{parse_db_type, parse_equality}; #[test] diff --git a/parser/src/parsing/create.rs b/parser/src/parsing/create.rs index 94e3538..e950f98 100644 --- a/parser/src/parsing/create.rs +++ b/parser/src/parsing/create.rs @@ -1,4 +1,4 @@ -use minisql::{operation::Operation, schema::{ColumnName, TableSchema}, type_system::DbType}; +use minisql::{schema::{ColumnName, TableSchema}, type_system::DbType}; use nom::{ bytes::complete::tag, character::complete::{char, multispace0, multispace1}, @@ -8,8 +8,9 @@ use nom::{ }; use super::common::{parse_table_name, parse_identifier, parse_db_type}; +use crate::syntax::RawQuerySyntax; -pub fn parse_create(input: &str) -> IResult<&str, Operation> { +pub fn parse_create(input: &str) -> IResult<&str, RawQuerySyntax> { let (input, _) = tag("CREATE")(input)?; let (input, _) = multispace1(input)?; let (input, _) = tag("TABLE")(input)?; @@ -41,7 +42,7 @@ pub fn parse_create(input: &str) -> IResult<&str, Operation> { ); Ok(( input, - Operation::CreateTable(table_name.to_string(), schema), + RawQuerySyntax::CreateTable(table_name.to_string(), schema), )) } @@ -68,8 +69,8 @@ pub fn parse_column_definition(input: &str) -> IResult<&str, (ColumnName, DbType #[cfg(test)] mod tests { - use minisql::operation::Operation; use crate::parsing::create::parse_create; + use crate::syntax::RawQuerySyntax; #[test] fn test_parse_create_no_spaces() { @@ -94,9 +95,9 @@ mod tests { #[test] fn test_parse_create() { let (_, create) = parse_create("CREATE TABLE \"Table1\"( id UUID , column1 INT );").expect("should parse"); - assert!(matches!(create, Operation::CreateTable(_ ,_))); + assert!(matches!(create, RawQuerySyntax::CreateTable(_ ,_))); match create { - Operation::CreateTable(name, schema) => { + RawQuerySyntax::CreateTable(name, schema) => { assert_eq!(name, "Table1"); assert_eq!(schema.number_of_columns(), 2); assert_eq!(schema.column_position_from_column_name(&"id".to_string()).unwrap(), 0); diff --git a/parser/src/parsing/delete.rs b/parser/src/parsing/delete.rs index af71cec..4dc07d2 100644 --- a/parser/src/parsing/delete.rs +++ b/parser/src/parsing/delete.rs @@ -1,13 +1,13 @@ -use minisql::operation::Operation; use nom::{ bytes::complete::tag, character::complete::{char, multispace0, multispace1}, IResult, }; +use crate::syntax::RawQuerySyntax; use super::common::{parse_table_name, parse_condition}; -pub fn parse_delete(input: &str) -> IResult<&str, Operation> { +pub fn parse_delete(input: &str) -> IResult<&str, RawQuerySyntax> { let (input, _) = tag("DELETE")(input)?; let (input, _) = multispace1(input)?; let (input, _) = tag("FROM")(input)?; @@ -19,19 +19,19 @@ pub fn parse_delete(input: &str) -> IResult<&str, Operation> { let (input, _) = char(';')(input)?; Ok(( input, - Operation::Delete(table_name.to_string(), condition), + RawQuerySyntax::Delete(table_name.to_string(), condition), )) } #[cfg(test)] mod tests { - use minisql::operation::Operation; + use crate::syntax::RawQuerySyntax; use crate::parsing::delete::parse_delete; #[test] fn test_parse_delete() { let (_, operation) = parse_delete("DELETE FROM \"T1\" WHERE id = 1 ;").expect("should parse"); - assert!(matches!(operation, Operation::Delete(_, _))) + assert!(matches!(operation, RawQuerySyntax::Delete(_, _))) } // TODO: add test with condition diff --git a/parser/src/parsing/index.rs b/parser/src/parsing/index.rs index 3130a6b..68233ba 100644 --- a/parser/src/parsing/index.rs +++ b/parser/src/parsing/index.rs @@ -1,4 +1,4 @@ -use minisql::operation::Operation; +use crate::syntax::RawQuerySyntax; use nom::{ bytes::complete::tag, character::complete::{char, multispace0, multispace1}, @@ -7,7 +7,7 @@ use nom::{ use super::common::{parse_identifier, parse_table_name}; -pub fn parse_create_index(input: &str) -> IResult<&str, Operation> { +pub fn parse_create_index(input: &str) -> IResult<&str, RawQuerySyntax> { let (input, _) = tag("CREATE")(input)?; let unique = |input| -> IResult<&str, bool> { let (input, _) = multispace1(input)?; @@ -31,23 +31,23 @@ pub fn parse_create_index(input: &str) -> IResult<&str, Operation> { let (input, _) = char(')')(input)?; let (input, _) = multispace0(input)?; let (input, _) = char(';')(input)?; - let operation = Operation::CreateIndex(table_name.to_string(), column_name.to_string()); + let operation = RawQuerySyntax::CreateIndex(table_name.to_string(), column_name.to_string()); Ok((input, operation)) } #[cfg(test)] mod tests { - use minisql::operation::Operation; + use crate::syntax::RawQuerySyntax; use crate::parsing::index::parse_create_index; #[test] fn test_create_index() { - let (_, operation) = parse_create_index("CREATE UNIQUE INDEX idxcontactsemail ON \"contacts\" (email);").expect("should parse"); - assert!(matches!(operation, Operation::CreateIndex(_, _))); - match operation { - Operation::CreateIndex(table_name, column_name) => { + let (_, syntax) = parse_create_index("CREATE UNIQUE INDEX idxcontactsemail ON \"contacts\" (email);").expect("should parse"); + assert!(matches!(syntax, RawQuerySyntax::CreateIndex(_, _))); + match syntax { + RawQuerySyntax::CreateIndex(table_name, column_name) => { assert_eq!(table_name, "contacts"); assert_eq!(column_name, "email"); } @@ -57,10 +57,10 @@ mod tests { #[test] fn test_create_index_with_spaces() { - let (_, operation) = parse_create_index("CREATE UNIQUE INDEX idxcontactsemail ON \"contacts\" ( email ) ;").expect("should parse"); - assert!(matches!(operation, Operation::CreateIndex(_, _))); - match operation { - Operation::CreateIndex(table_name, column_name) => { + let (_, syntax) = parse_create_index("CREATE UNIQUE INDEX idxcontactsemail ON \"contacts\" ( email ) ;").expect("should parse"); + assert!(matches!(syntax, RawQuerySyntax::CreateIndex(_, _))); + match syntax { + RawQuerySyntax::CreateIndex(table_name, column_name) => { assert_eq!(table_name, "contacts"); assert_eq!(column_name, "email"); } diff --git a/parser/src/parsing/insert.rs b/parser/src/parsing/insert.rs index 365be68..5f1f64a 100644 --- a/parser/src/parsing/insert.rs +++ b/parser/src/parsing/insert.rs @@ -1,5 +1,6 @@ use super::{literal::parse_db_value, common::{parse_table_name, parse_identifier}}; -use minisql::{operation::Operation, type_system::Value}; +use crate::syntax::RawQuerySyntax; +use minisql::type_system::Value; use nom::{ bytes::complete::tag, character::complete::{multispace0, multispace1, char}, @@ -9,7 +10,7 @@ use nom::{ IResult, }; -pub fn parse_insert(input: &str) -> IResult<&str, Operation> { +pub fn parse_insert(input: &str) -> IResult<&str, RawQuerySyntax> { let (input, _) = tag("INSERT")(input)?; let (input, _) = multispace1(input)?; let (input, _) = tag("INTO")(input)?; @@ -33,7 +34,7 @@ pub fn parse_insert(input: &str) -> IResult<&str, Operation> { let (input, _) = char(';')(input)?; Ok(( input, - Operation::Insert(table_name.to_string(), column_names.into_iter().zip(values).collect()), + RawQuerySyntax::Insert(table_name.to_string(), column_names.into_iter().zip(values).collect()), )) } @@ -48,17 +49,18 @@ pub fn parse_values(input: &str) -> IResult<&str, Vec> { #[cfg(test)] mod tests { - use minisql::{operation::Operation, type_system::{IndexableValue, Value}}; + use minisql::type_system::{IndexableValue, Value}; + use crate::syntax::RawQuerySyntax; use super::parse_insert; #[test] fn test_parse_insert() { let sql = "INSERT INTO \"MyTable\" (id, data) VALUES(1, \"Text\");"; - let operation = parse_insert(sql).expect("should parse"); - match operation { - ("", Operation::Insert(table_name, insertion_values)) => { + let syntax = parse_insert(sql).expect("should parse"); + match syntax { + ("", RawQuerySyntax::Insert(table_name, insertion_values)) => { assert_eq!(table_name, "MyTable"); assert_eq!( insertion_values, @@ -78,7 +80,7 @@ mod tests { let sql = "INSERT INTO \"MyTable\" ( id, data ) VALUES ( 1, \"Text\" ) ;"; let operation = parse_insert(sql).expect("should parse"); match operation { - ("", Operation::Insert(table_name, insertion_values)) => { + ("", RawQuerySyntax::Insert(table_name, insertion_values)) => { assert_eq!(table_name, "MyTable"); assert_eq!(insertion_values, vec![ diff --git a/parser/src/parsing/select.rs b/parser/src/parsing/select.rs index c3c4292..e7a09a8 100644 --- a/parser/src/parsing/select.rs +++ b/parser/src/parsing/select.rs @@ -1,5 +1,5 @@ use super::common::{parse_table_name, parse_column_name, parse_condition}; -use minisql::operation::{ColumnSelection, Operation}; +use crate::syntax::{ColumnSelection, RawQuerySyntax}; use nom::{ branch::alt, bytes::complete::tag, @@ -11,7 +11,7 @@ use nom::{ IResult, }; -pub fn parse_select(input: &str) -> IResult<&str, Operation> { +pub fn parse_select(input: &str) -> IResult<&str, RawQuerySyntax> { let (input, _) = tag("SELECT")(input)?; let (input, _) = multispace1(input)?; @@ -27,7 +27,7 @@ pub fn parse_select(input: &str) -> IResult<&str, Operation> { let (input, _) = tag(";")(input)?; Ok(( input, - Operation::Select(table_name.to_string(), column_selection, condition), + RawQuerySyntax::Select(table_name.to_string(), column_selection, condition), )) } @@ -44,7 +44,7 @@ pub fn try_parse_column_selection(input: &str) -> IResult<&str, ColumnSelection> #[cfg(test)] mod tests { - use minisql::operation::{ColumnSelection, Operation}; + use crate::syntax::{ColumnSelection, RawQuerySyntax}; use crate::parsing::{common::{parse_column_name, parse_table_name}, select::parse_select}; @@ -53,7 +53,7 @@ mod tests { let sql = "SELECT * FROM \"MyTable\";"; let operation = parse_select(sql).expect("should parse"); match operation { - ("", Operation::Select(table_name, column_selection, maybe_condition)) => { + ("", RawQuerySyntax::Select(table_name, column_selection, maybe_condition)) => { assert_eq!(table_name, "MyTable"); assert!(matches!(column_selection, ColumnSelection::All)); assert!(matches!(maybe_condition, None)); @@ -80,7 +80,7 @@ mod tests { let sql = "SELECT name , email FROM \"AddressBook\" ;"; let operation = parse_select(sql).expect("should parse"); match operation { - ("", Operation::Select(table_name, column_selection, maybe_condition)) => { + ("", RawQuerySyntax::Select(table_name, column_selection, maybe_condition)) => { assert_eq!(table_name, "AddressBook"); assert!(matches!(column_selection, ColumnSelection::Columns(_))); match column_selection { @@ -102,11 +102,11 @@ mod tests { #[test] fn test_parse_select_where() { - use minisql::operation::Condition; + use crate::syntax::Condition; let sql = "SELECT * FROM \"AddressBook\" WHERE id = 5 ;"; let operation = parse_select(sql).expect("should parse"); match operation { - ("", Operation::Select(table_name, column_selection, maybe_condition)) => { + ("", RawQuerySyntax::Select(table_name, column_selection, maybe_condition)) => { assert_eq!(table_name, "AddressBook"); assert!(matches!(column_selection, ColumnSelection::All)); assert!(matches!(maybe_condition, Some(Condition::Eq(_, _)))); diff --git a/parser/src/validation.rs b/parser/src/validation.rs index 017491b..9e6a79f 100644 --- a/parser/src/validation.rs +++ b/parser/src/validation.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use thiserror::Error; use crate::syntax::{ColumnSelection, Condition, InsertionValues, RawQuerySyntax}; -use minisql::{operation::{ColumnSelectionForInterpreter, ConditionForInterpreter, InsertionValuesForInterpreter, OperationForInterpreter}, type_system::Value, schema::{TableSchema, ColumnName, TableName}, type_system::DbType, interpreter::TablePosition}; +use minisql::{operation::{ColumnSelectionForInterpreter, ConditionForInterpreter, InsertionValuesForInterpreter, OperationForInterpreter}, type_system::Value, schema::{TableSchema, ColumnName, TableName}, type_system::DbType, interpreter::{TablePosition, DbSchema}}; #[derive(Debug, Error)] pub enum ValidationError { @@ -25,8 +25,6 @@ pub enum ValidationError { RequiredColumnsAreMissing(Vec) } -pub type DbSchema<'a> = Vec<(TableName, TablePosition, &'a TableSchema)>; - /// Validates and converts the raw syntax into a proper interpreter operation based on db schema. pub fn validate_operation(query: RawQuerySyntax, db_schema: &DbSchema) -> Result { match query { diff --git a/server/src/main.rs b/server/src/main.rs index d256374..fcbb6bf 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -53,10 +53,10 @@ async fn handle_stream(mut stream: TcpStream) -> anyhow::Result<()> { } FrontendMessage::Query(data) => { println!("Received Query: {:?}", data); - let metadata = state.metadata(); - match parse_and_validate(data.query.as_str().to_string(), &metadata) { + let db_schema = state.db_schema(); + match parse_and_validate(data.query.as_str().to_string(), &db_schema) { Ok(operation) => { - match state.interpret(operation) { + match state.interpret_for_interpreter(operation) { Ok(_) => { send_query_response(&mut writer).await?; }