From e1afc938098ad46771a8dc1e282afc3554ebf315 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Wed, 3 Sep 2025 11:11:32 -0400 Subject: [PATCH 1/6] Pull Where Clause generation into a helper for building trees --- src/cli/ast/common.rs | 36 +---------------------------- src/cli/ast/delete_statement.rs | 3 ++- src/cli/ast/helpers/mod.rs | 1 + src/cli/ast/helpers/where_clause.rs | 35 ++++++++++++++++++++++++++++ src/cli/ast/mod.rs | 1 + src/cli/ast/select_statement.rs | 3 ++- src/cli/ast/update_statement.rs | 3 ++- 7 files changed, 44 insertions(+), 38 deletions(-) create mode 100644 src/cli/ast/helpers/mod.rs create mode 100644 src/cli/ast/helpers/where_clause.rs diff --git a/src/cli/ast/common.rs b/src/cli/ast/common.rs index bbc36e8..36f4c1a 100644 --- a/src/cli/ast/common.rs +++ b/src/cli/ast/common.rs @@ -1,4 +1,4 @@ -use crate::cli::{ast::{parser::Parser, WhereClause, Operator, OrderByClause, OrderByDirection, LimitClause}, tokenizer::token::TokenTypes}; +use crate::cli::{ast::{parser::Parser, OrderByClause, OrderByDirection, LimitClause}, tokenizer::token::TokenTypes}; use crate::db::table::Value; use hex::decode; @@ -63,40 +63,6 @@ pub fn get_table_name(parser: &mut Parser) -> Result { Ok(result) } -pub fn get_where_clause(parser: &mut Parser) -> Result, String> { - if expect_token_type(parser, TokenTypes::Where).is_err() { - return Ok(None); - } - parser.advance()?; - - let token = parser.current_token()?; - expect_token_type(parser, TokenTypes::Identifier)?; - let column = token.value.to_string(); - parser.advance()?; - - let token = parser.current_token()?; - let operator = match token.token_type { - TokenTypes::Equals => Operator::Equals, - TokenTypes::NotEquals => Operator::NotEquals, - TokenTypes::LessThan => Operator::LessThan, - TokenTypes::LessEquals => Operator::LessEquals, - TokenTypes::GreaterThan => Operator::GreaterThan, - TokenTypes::GreaterEquals => Operator::GreaterEquals, - _ => return Err(parser.format_error()), - }; - parser.advance()?; - - let value = token_to_value(parser)?; - parser.advance()?; - - return Ok(Some(WhereClause { - column: column, - operator: operator, - value: value, - })); -} - - pub fn get_order_by(parser: &mut Parser) -> Result>, String> { if expect_token_type(parser, TokenTypes::Order).is_err() { return Ok(None); diff --git a/src/cli/ast/delete_statement.rs b/src/cli/ast/delete_statement.rs index e4f3d82..b32545e 100644 --- a/src/cli/ast/delete_statement.rs +++ b/src/cli/ast/delete_statement.rs @@ -1,4 +1,5 @@ -use crate::cli::ast::{parser::Parser, SqlStatement, DeleteStatement, common::{expect_token_type, get_table_name, get_where_clause, get_order_by, get_limit}}; +use crate::cli::ast::{parser::Parser, SqlStatement, DeleteStatement, common::{expect_token_type, get_table_name, get_order_by, get_limit}}; +use crate::cli::ast::helpers::where_clause::get_where_clause; use crate::cli::tokenizer::token::TokenTypes; pub fn build(parser: &mut Parser) -> Result { diff --git a/src/cli/ast/helpers/mod.rs b/src/cli/ast/helpers/mod.rs new file mode 100644 index 0000000..0def8aa --- /dev/null +++ b/src/cli/ast/helpers/mod.rs @@ -0,0 +1 @@ +pub mod where_clause; \ No newline at end of file diff --git a/src/cli/ast/helpers/where_clause.rs b/src/cli/ast/helpers/where_clause.rs new file mode 100644 index 0000000..0ccac24 --- /dev/null +++ b/src/cli/ast/helpers/where_clause.rs @@ -0,0 +1,35 @@ +use crate::cli::ast::{parser::Parser, WhereClause, Operator, common::{expect_token_type, token_to_value}}; +use crate::cli::tokenizer::token::TokenTypes; + +pub fn get_where_clause(parser: &mut Parser) -> Result, String> { + if expect_token_type(parser, TokenTypes::Where).is_err() { + return Ok(None); + } + parser.advance()?; + + let token = parser.current_token()?; + expect_token_type(parser, TokenTypes::Identifier)?; + let column = token.value.to_string(); + parser.advance()?; + + let token = parser.current_token()?; + let operator = match token.token_type { + TokenTypes::Equals => Operator::Equals, + TokenTypes::NotEquals => Operator::NotEquals, + TokenTypes::LessThan => Operator::LessThan, + TokenTypes::LessEquals => Operator::LessEquals, + TokenTypes::GreaterThan => Operator::GreaterThan, + TokenTypes::GreaterEquals => Operator::GreaterEquals, + _ => return Err(parser.format_error()), + }; + parser.advance()?; + + let value = token_to_value(parser)?; + parser.advance()?; + + return Ok(Some(WhereClause { + column: column, + operator: operator, + value: value, + })); +} \ No newline at end of file diff --git a/src/cli/ast/mod.rs b/src/cli/ast/mod.rs index 25708b8..5b070ec 100644 --- a/src/cli/ast/mod.rs +++ b/src/cli/ast/mod.rs @@ -8,6 +8,7 @@ mod parser; mod select_statement; mod update_statement; mod delete_statement; +mod helpers; #[derive(Debug, PartialEq)] pub enum SqlStatement { diff --git a/src/cli/ast/select_statement.rs b/src/cli/ast/select_statement.rs index 7e36450..2f9d631 100644 --- a/src/cli/ast/select_statement.rs +++ b/src/cli/ast/select_statement.rs @@ -1,4 +1,5 @@ -use crate::{cli::{ast::{common::{expect_token_type, get_where_clause, tokens_to_identifier_list, get_order_by, get_limit, get_table_name}, parser::Parser, SelectStatement, SelectStatementColumns, SqlStatement, WhereClause}, tokenizer::token::TokenTypes}}; +use crate::{cli::{ast::{common::{expect_token_type, tokens_to_identifier_list, get_order_by, get_limit, get_table_name}, parser::Parser, SelectStatement, SelectStatementColumns, SqlStatement, WhereClause}, tokenizer::token::TokenTypes}}; +use crate::cli::ast::helpers::where_clause::get_where_clause; pub fn build(parser: &mut Parser) -> Result { parser.advance()?; diff --git a/src/cli/ast/update_statement.rs b/src/cli/ast/update_statement.rs index 64ea716..a7bb6fc 100644 --- a/src/cli/ast/update_statement.rs +++ b/src/cli/ast/update_statement.rs @@ -1,4 +1,5 @@ -use crate::cli::ast::{parser::Parser, SqlStatement, UpdateStatement, ColumnValue, common::{expect_token_type, get_where_clause, token_to_value, get_table_name}}; +use crate::cli::ast::{parser::Parser, SqlStatement, UpdateStatement, ColumnValue, common::{expect_token_type, token_to_value, get_table_name}}; +use crate::cli::ast::helpers::where_clause::get_where_clause; use crate::cli::tokenizer::token::TokenTypes; pub fn build(parser: &mut Parser) -> Result { From ea65de03e3b4fe7ecd302d2508e0cd8fffebfea3 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Wed, 3 Sep 2025 11:19:57 -0400 Subject: [PATCH 2/6] Add tests to WHERE CLAUSE that are specific to it --- src/cli/ast/helpers/where_clause.rs | 57 ++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/cli/ast/helpers/where_clause.rs b/src/cli/ast/helpers/where_clause.rs index 0ccac24..bef5f48 100644 --- a/src/cli/ast/helpers/where_clause.rs +++ b/src/cli/ast/helpers/where_clause.rs @@ -32,4 +32,59 @@ pub fn get_where_clause(parser: &mut Parser) -> Result, Stri operator: operator, value: value, })); -} \ No newline at end of file +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::tokenizer::scanner::Token; + use crate::db::table::Value; + + fn token(tt: TokenTypes, val: &'static str) -> Token<'static> { + Token { + token_type: tt, + value: val, + col_num: 0, + line_num: 1, + } + } + + #[test] + fn where_clause_with_all_tokens_is_generated_correctly() { + // WHERE id = 1; + let tokens = vec![ + token(TokenTypes::Where, "WHERE"), + token(TokenTypes::Identifier, "id"), + token(TokenTypes::Equals, "="), + token(TokenTypes::IntLiteral, "1"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = get_where_clause(&mut parser); + assert!(result.is_ok()); + let where_clause = result.unwrap(); + let expected = Some(WhereClause { + column: "id".to_string(), + operator: Operator::Equals, + value: Value::Integer(1), + }); + assert_eq!(expected, where_clause); + assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::SemiColon); + } + + #[test] + fn not_where_clause_returns_none() { + // SELECT * FROM users; + let tokens = vec![ + token(TokenTypes::Select, "SELECT"), + token(TokenTypes::Asterisk, "*"), + token(TokenTypes::From, "FROM"), + token(TokenTypes::Identifier, "users"), + ]; + let mut parser = Parser::new(tokens); + let result = get_where_clause(&mut parser); + assert!(result.is_ok()); + assert!(result.unwrap().is_none()); + assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::Select); + } +} From ea9fce2b860ae17efcf5f4eaa63b8e392c17b37c Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Wed, 3 Sep 2025 11:32:12 -0400 Subject: [PATCH 3/6] Update tests to use a test_utils file for common functions --- src/cli/ast/create_statement.rs | 11 +---------- src/cli/ast/delete_statement.rs | 11 +---------- src/cli/ast/insert_statement.rs | 11 +---------- src/cli/ast/mod.rs | 13 +++---------- src/cli/ast/parser.rs | 34 +++++++++++++-------------------- src/cli/ast/select_statement.rs | 11 +---------- src/cli/ast/test_utils.rs | 24 +++++++++++++++++++++++ src/cli/ast/update_statement.rs | 11 +---------- 8 files changed, 45 insertions(+), 81 deletions(-) create mode 100644 src/cli/ast/test_utils.rs diff --git a/src/cli/ast/create_statement.rs b/src/cli/ast/create_statement.rs index 983ae16..837d1dc 100644 --- a/src/cli/ast/create_statement.rs +++ b/src/cli/ast/create_statement.rs @@ -100,16 +100,7 @@ fn index_statement(_parser: &mut Parser) -> Result { #[cfg(test)] mod tests { use super::*; - use crate::cli::tokenizer::scanner::Token; - - fn token(tt: TokenTypes, val: &'static str) -> Token<'static> { - Token { - token_type: tt, - value: val, - col_num: 0, - line_num: 1, - } - } + use crate::cli::ast::test_utils::token; #[test] fn create_table_generates_proper_statement(){ diff --git a/src/cli/ast/delete_statement.rs b/src/cli/ast/delete_statement.rs index b32545e..a087e24 100644 --- a/src/cli/ast/delete_statement.rs +++ b/src/cli/ast/delete_statement.rs @@ -22,7 +22,7 @@ pub fn build(parser: &mut Parser) -> Result { #[cfg(test)] mod tests { use super::*; - use crate::cli::tokenizer::scanner::Token; + use crate::cli::ast::test_utils::token; use crate::cli::ast::OrderByClause; use crate::cli::ast::OrderByDirection; use crate::cli::ast::LimitClause; @@ -30,15 +30,6 @@ mod tests { use crate::cli::ast::WhereClause; use crate::db::table::Value; - fn token(tt: TokenTypes, val: &'static str) -> Token<'static> { - Token { - token_type: tt, - value: val, - col_num: 0, - line_num: 1, - } - } - #[test] fn delete_statement_with_all_tokens_is_generated_correctly() { // DELETE FROM users; diff --git a/src/cli/ast/insert_statement.rs b/src/cli/ast/insert_statement.rs index 76ebb6c..8f8c40e 100644 --- a/src/cli/ast/insert_statement.rs +++ b/src/cli/ast/insert_statement.rs @@ -134,16 +134,7 @@ fn or_statement(_parser: &mut Parser) -> Result { #[cfg(test)] mod tests { use super::*; - use crate::cli::tokenizer::scanner::Token; - - fn token(tt: TokenTypes, val: &'static str) -> Token<'static> { - Token { - token_type: tt, - value: val, - col_num: 0, - line_num: 1, - } - } + use crate::cli::ast::test_utils::token; #[test] fn single_row_insert_statement_is_generated_correctly() { diff --git a/src/cli/ast/mod.rs b/src/cli/ast/mod.rs index 5b070ec..3700dc3 100644 --- a/src/cli/ast/mod.rs +++ b/src/cli/ast/mod.rs @@ -9,6 +9,8 @@ mod select_statement; mod update_statement; mod delete_statement; mod helpers; +#[cfg(test)] +mod test_utils; #[derive(Debug, PartialEq)] pub enum SqlStatement { @@ -182,16 +184,7 @@ pub fn generate(tokens: Vec) -> Vec> { #[cfg(test)] mod tests { use super::*; - use crate::cli::tokenizer::scanner::Token; - - fn token(tt: TokenTypes, val: &'static str) -> Token<'static> { - Token { - token_type: tt, - value: val, - col_num: 0, - line_num: 1, - } - } + use super::test_utils::token; #[test] fn ast_handles_invalid_statements_gracefully() { diff --git a/src/cli/ast/parser.rs b/src/cli/ast/parser.rs index f414d4b..f0c2a46 100644 --- a/src/cli/ast/parser.rs +++ b/src/cli/ast/parser.rs @@ -80,15 +80,7 @@ impl<'a> Parser<'a> { mod tests { use super::*; use crate::cli::ast::{CreateTableStatement, InsertIntoStatement, SelectStatement, SelectStatementColumns}; - - fn token(tt: TokenTypes, val: &'static str, col: usize, line: usize) -> Token<'static> { - Token { - token_type: tt, - value: val, - col_num: col, - line_num: line, - } - } + use crate::cli::ast::test_utils::{token_with_location, token}; #[test] fn parser_formats_error_when_at_end_of_input() { @@ -100,7 +92,7 @@ mod tests { #[test] fn parser_formats_error_when_unexpected_token_type() { - let tokens = vec![token(TokenTypes::Insert, "INSERT", 15, 3)]; + let tokens = vec![token_with_location(TokenTypes::Insert, "INSERT", 15, 3)]; let parser = Parser::new(tokens); let result = parser.format_error(); assert_eq!(result, "Error at line 3, column 15: Unexpected value: INSERT"); @@ -152,13 +144,13 @@ mod tests { #[test] fn parser_next_statement_filters_options_correctly_handles_multiple_statements() { let tokens = vec![ - token(TokenTypes::Create, "CREATE", 1, 1), - token(TokenTypes::SemiColon, ";", 1, 1), - token(TokenTypes::Insert, "INSERT", 1, 1), - token(TokenTypes::SemiColon, ";", 1, 1), - token(TokenTypes::Select, "SELECT", 1, 1), - token(TokenTypes::SemiColon, ";", 1, 1), - token(TokenTypes::EOF, "", 1, 1), + token(TokenTypes::Create, "CREATE"), + token(TokenTypes::SemiColon, ";"), + token(TokenTypes::Insert, "INSERT"), + token(TokenTypes::SemiColon, ";"), + token(TokenTypes::Select, "SELECT"), + token(TokenTypes::SemiColon, ";"), + token(TokenTypes::EOF, ""), ]; let mut parser = Parser::new(tokens); let builder : &dyn StatementBuilder = &MockStatementBuilder; @@ -199,14 +191,14 @@ mod tests { #[test] fn parser_next_statement_handles_errors_correctly() { let tokens = vec![ - token(TokenTypes::Identifier, "users", 1, 1), - token(TokenTypes::SemiColon, ";", 1, 1), - token(TokenTypes::EOF, "", 1, 1), + token(TokenTypes::Identifier, "users"), + token(TokenTypes::SemiColon, ";"), + token(TokenTypes::EOF, ""), ]; let mut parser = Parser::new(tokens); let builder : &dyn StatementBuilder = &MockStatementBuilder; let result = parser.next_statement(builder); - let expected = Some(Err("Error at line 1, column 1: Unexpected value: users".to_string())); + let expected = Some(Err("Error at line 1, column 0: Unexpected value: users".to_string())); assert_eq!(result, expected); } } diff --git a/src/cli/ast/select_statement.rs b/src/cli/ast/select_statement.rs index 2f9d631..375dbe4 100644 --- a/src/cli/ast/select_statement.rs +++ b/src/cli/ast/select_statement.rs @@ -37,20 +37,11 @@ fn get_columns(parser: &mut Parser) -> Result { mod tests { use super::*; use crate::cli::ast::Operator; - use crate::cli::tokenizer::scanner::Token; use crate::db::table::Value; use crate::cli::ast::OrderByClause; use crate::cli::ast::OrderByDirection; use crate::cli::ast::LimitClause; - - fn token(tt: TokenTypes, val: &'static str) -> Token<'static> { - Token { - token_type: tt, - value: val, - col_num: 0, - line_num: 1, - } - } + use crate::cli::ast::test_utils::token; #[test] fn select_statement_with_all_tokens_is_generated_correctly() { diff --git a/src/cli/ast/test_utils.rs b/src/cli/ast/test_utils.rs new file mode 100644 index 0000000..f76b02f --- /dev/null +++ b/src/cli/ast/test_utils.rs @@ -0,0 +1,24 @@ + +#[cfg(test)] +use crate::cli::tokenizer::token::TokenTypes; +#[cfg(test)] +use crate::cli::tokenizer::scanner::Token; + +#[cfg(test)] +pub fn token(tt: TokenTypes, val: &'static str) -> Token<'static> { + Token { + token_type: tt, + value: val, + col_num: 0, + line_num: 1, + } +} + +pub fn token_with_location(tt: TokenTypes, val: &'static str, col: usize, line: usize) -> Token<'static> { + Token { + token_type: tt, + value: val, + col_num: col, + line_num: line, + } +} diff --git a/src/cli/ast/update_statement.rs b/src/cli/ast/update_statement.rs index a7bb6fc..2760699 100644 --- a/src/cli/ast/update_statement.rs +++ b/src/cli/ast/update_statement.rs @@ -55,19 +55,10 @@ fn get_update_values(parser: &mut Parser) -> Result, String> { #[cfg(test)] mod tests { use super::*; - use crate::cli::tokenizer::scanner::Token; use crate::db::table::Value; use crate::cli::ast::Operator; use crate::cli::ast::WhereClause; - - fn token(tt: TokenTypes, val: &'static str) -> Token<'static> { - Token { - token_type: tt, - value: val, - col_num: 0, - line_num: 1, - } - } + use crate::cli::ast::test_utils::token; #[test] fn update_statement_with_all_tokens_is_generated_correctly() { From 12cbfc9259baef9ea52cc7aab0658ee2a7bb487f Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Wed, 3 Sep 2025 11:38:10 -0400 Subject: [PATCH 4/6] Reformat files and tests --- src/cli/ast/create_statement.rs | 15 +++++++++------ src/cli/ast/delete_statement.rs | 5 ++++- src/cli/ast/insert_statement.rs | 11 +++++------ src/cli/ast/parser.rs | 2 -- src/cli/ast/select_statement.rs | 8 +++++++- src/cli/ast/update_statement.rs | 5 ++++- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/cli/ast/create_statement.rs b/src/cli/ast/create_statement.rs index 837d1dc..198dce3 100644 --- a/src/cli/ast/create_statement.rs +++ b/src/cli/ast/create_statement.rs @@ -1,4 +1,10 @@ -use crate::cli::{ast::{parser::Parser, CreateTableStatement, SqlStatement::{self, CreateTable}, common::expect_token_type}, tokenizer::token::TokenTypes}; +use crate::cli::{ + ast::{ + parser::Parser, CreateTableStatement, SqlStatement::{self, CreateTable}, + common::{expect_token_type, get_table_name} + }, + tokenizer::token::TokenTypes +}; use crate::db::table::{ColumnDefinition, DataType}; pub fn build(parser: &mut Parser) -> Result { @@ -22,11 +28,8 @@ pub fn build(parser: &mut Parser) -> Result { } fn table_statement(parser: &mut Parser) -> Result { - parser.advance()?; - - let token = parser.current_token()?; - expect_token_type(parser, TokenTypes::Identifier)?; - let table_name = token.value.to_string(); + // Get the table name + let table_name = get_table_name(parser)?; parser.advance()?; let column_definitions = column_definitions(parser)?; diff --git a/src/cli/ast/delete_statement.rs b/src/cli/ast/delete_statement.rs index a087e24..2c1d862 100644 --- a/src/cli/ast/delete_statement.rs +++ b/src/cli/ast/delete_statement.rs @@ -1,4 +1,7 @@ -use crate::cli::ast::{parser::Parser, SqlStatement, DeleteStatement, common::{expect_token_type, get_table_name, get_order_by, get_limit}}; +use crate::cli::ast::{ + parser::Parser, SqlStatement, DeleteStatement, + common::{expect_token_type, get_table_name, get_order_by, get_limit} +}; use crate::cli::ast::helpers::where_clause::get_where_clause; use crate::cli::tokenizer::token::TokenTypes; diff --git a/src/cli/ast/insert_statement.rs b/src/cli/ast/insert_statement.rs index 8f8c40e..ce26eb1 100644 --- a/src/cli/ast/insert_statement.rs +++ b/src/cli/ast/insert_statement.rs @@ -1,4 +1,7 @@ -use crate::cli::{ast::{parser::Parser, common::token_to_value, common::expect_token_type, InsertIntoStatement, SqlStatement::{self, InsertInto}}, tokenizer::token::TokenTypes}; +use crate::cli::{ + ast::{parser::Parser, common::token_to_value, common::expect_token_type, common::get_table_name, InsertIntoStatement, SqlStatement::{self, InsertInto}}, + tokenizer::token::TokenTypes +}; use crate::db::table::Value; pub fn build(parser: &mut Parser) -> Result { @@ -22,11 +25,7 @@ pub fn build(parser: &mut Parser) -> Result { } fn into_statement(parser: &mut Parser) -> Result { - parser.advance()?; - - let token = parser.current_token()?; - expect_token_type(parser, TokenTypes::Identifier)?; - let table_name = token.value.to_string(); + let table_name = get_table_name(parser)?; parser.advance()?; let token = parser.current_token()?; diff --git a/src/cli/ast/parser.rs b/src/cli/ast/parser.rs index f0c2a46..4ebc884 100644 --- a/src/cli/ast/parser.rs +++ b/src/cli/ast/parser.rs @@ -3,8 +3,6 @@ use crate::cli::{ tokenizer::scanner::Token, tokenizer::token::TokenTypes, }; - - pub struct Parser<'a> { tokens: Vec>, current: usize, diff --git a/src/cli/ast/select_statement.rs b/src/cli/ast/select_statement.rs index 375dbe4..a9bf312 100644 --- a/src/cli/ast/select_statement.rs +++ b/src/cli/ast/select_statement.rs @@ -1,4 +1,10 @@ -use crate::{cli::{ast::{common::{expect_token_type, tokens_to_identifier_list, get_order_by, get_limit, get_table_name}, parser::Parser, SelectStatement, SelectStatementColumns, SqlStatement, WhereClause}, tokenizer::token::TokenTypes}}; +use crate::{cli::{ + ast::{ + common::{expect_token_type, tokens_to_identifier_list, get_order_by, get_limit, get_table_name}, + parser::Parser, SelectStatement, SelectStatementColumns, SqlStatement, WhereClause + }, + tokenizer::token::TokenTypes +}}; use crate::cli::ast::helpers::where_clause::get_where_clause; pub fn build(parser: &mut Parser) -> Result { diff --git a/src/cli/ast/update_statement.rs b/src/cli/ast/update_statement.rs index 2760699..3a19022 100644 --- a/src/cli/ast/update_statement.rs +++ b/src/cli/ast/update_statement.rs @@ -1,4 +1,7 @@ -use crate::cli::ast::{parser::Parser, SqlStatement, UpdateStatement, ColumnValue, common::{expect_token_type, token_to_value, get_table_name}}; +use crate::cli::ast::{ + parser::Parser, SqlStatement, UpdateStatement, ColumnValue, + common::{expect_token_type, token_to_value, get_table_name} +}; use crate::cli::ast::helpers::where_clause::get_where_clause; use crate::cli::tokenizer::token::TokenTypes; From b4d724aebacab847ab3b7e30a6920d0346c0c79b Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Wed, 3 Sep 2025 11:46:02 -0400 Subject: [PATCH 5/6] Pull out ORDER BY and LIMIT into seperate generators --- src/cli/ast/common.rs | 79 +------------------------- src/cli/ast/delete_statement.rs | 12 ++-- src/cli/ast/helpers/limit_clause.rs | 39 +++++++++++++ src/cli/ast/helpers/mod.rs | 4 +- src/cli/ast/helpers/order_by_clause.rs | 47 +++++++++++++++ src/cli/ast/select_statement.rs | 6 +- 6 files changed, 100 insertions(+), 87 deletions(-) create mode 100644 src/cli/ast/helpers/limit_clause.rs create mode 100644 src/cli/ast/helpers/order_by_clause.rs diff --git a/src/cli/ast/common.rs b/src/cli/ast/common.rs index 36f4c1a..df28bbf 100644 --- a/src/cli/ast/common.rs +++ b/src/cli/ast/common.rs @@ -1,4 +1,4 @@ -use crate::cli::{ast::{parser::Parser, OrderByClause, OrderByDirection, LimitClause}, tokenizer::token::TokenTypes}; +use crate::cli::{ast::{parser::Parser}, tokenizer::token::TokenTypes}; use crate::db::table::Value; use hex::decode; @@ -61,81 +61,4 @@ pub fn get_table_name(parser: &mut Parser) -> Result { expect_token_type(parser, TokenTypes::Identifier)?; let result = token.value.to_string(); Ok(result) -} - -pub fn get_order_by(parser: &mut Parser) -> Result>, String> { - if expect_token_type(parser, TokenTypes::Order).is_err() { - return Ok(None); - } - parser.advance()?; - - expect_token_type(parser, TokenTypes::By)?; - parser.advance()?; - - let mut order_by_clauses = vec![]; - loop { - let token = parser.current_token()?; - expect_token_type(parser, TokenTypes::Identifier)?; - let column = token.value.to_string(); - parser.advance()?; - - let token = parser.current_token()?; - let direction = match token.token_type { - TokenTypes::Asc => { - parser.advance()?; - OrderByDirection::Asc - }, - TokenTypes::Desc => { - parser.advance()?; - OrderByDirection::Desc - }, - _ => OrderByDirection::Asc, - }; - - order_by_clauses.push(OrderByClause { - column: column, - direction: direction, - }); - - let token = parser.current_token()?; - if token.token_type != TokenTypes::Comma { - break; - } - parser.advance()?; - } - return Ok(Some(order_by_clauses)); -} - -pub fn get_limit(parser: &mut Parser) -> Result, String> { - if expect_token_type(parser, TokenTypes::Limit).is_err() { - return Ok(None); - } - parser.advance()?; - - expect_token_type(parser, TokenTypes::IntLiteral)?; - let limit = token_to_value(parser)?; - parser.advance()?; - - let token = parser.current_token()?; - if token.token_type != TokenTypes::Offset { - return Ok(Some(LimitClause { - limit: limit, - offset: None, - })); - } - parser.advance()?; - - expect_token_type(parser, TokenTypes::IntLiteral)?; - let offset = token_to_value(parser)?; - if let Value::Integer(offset) = offset { - if offset < 0 { - return Err(parser.format_error()); - } - }; - parser.advance()?; - - return Ok(Some(LimitClause { - limit: limit, - offset: Some(offset), - })); } \ No newline at end of file diff --git a/src/cli/ast/delete_statement.rs b/src/cli/ast/delete_statement.rs index 2c1d862..1ac811d 100644 --- a/src/cli/ast/delete_statement.rs +++ b/src/cli/ast/delete_statement.rs @@ -1,9 +1,11 @@ -use crate::cli::ast::{ - parser::Parser, SqlStatement, DeleteStatement, - common::{expect_token_type, get_table_name, get_order_by, get_limit} +use crate::cli::{ + ast::{ + parser::Parser, SqlStatement, DeleteStatement, + common::{expect_token_type, get_table_name}, + helpers::{order_by_clause::get_order_by, where_clause::get_where_clause, limit_clause::get_limit} + }, + tokenizer::token::TokenTypes }; -use crate::cli::ast::helpers::where_clause::get_where_clause; -use crate::cli::tokenizer::token::TokenTypes; pub fn build(parser: &mut Parser) -> Result { parser.advance()?; diff --git a/src/cli/ast/helpers/limit_clause.rs b/src/cli/ast/helpers/limit_clause.rs new file mode 100644 index 0000000..095945a --- /dev/null +++ b/src/cli/ast/helpers/limit_clause.rs @@ -0,0 +1,39 @@ +use crate::cli::ast::{parser::Parser, LimitClause}; +use crate::cli::tokenizer::token::TokenTypes; +use crate::db::table::Value; +use crate::cli::ast::common::expect_token_type; +use crate::cli::ast::common::token_to_value; + +pub fn get_limit(parser: &mut Parser) -> Result, String> { + if expect_token_type(parser, TokenTypes::Limit).is_err() { + return Ok(None); + } + parser.advance()?; + + expect_token_type(parser, TokenTypes::IntLiteral)?; + let limit = token_to_value(parser)?; + parser.advance()?; + + let token = parser.current_token()?; + if token.token_type != TokenTypes::Offset { + return Ok(Some(LimitClause { + limit: limit, + offset: None, + })); + } + parser.advance()?; + + expect_token_type(parser, TokenTypes::IntLiteral)?; + let offset = token_to_value(parser)?; + if let Value::Integer(offset) = offset { + if offset < 0 { + return Err(parser.format_error()); + } + }; + parser.advance()?; + + return Ok(Some(LimitClause { + limit: limit, + offset: Some(offset), + })); +} \ No newline at end of file diff --git a/src/cli/ast/helpers/mod.rs b/src/cli/ast/helpers/mod.rs index 0def8aa..3407c8c 100644 --- a/src/cli/ast/helpers/mod.rs +++ b/src/cli/ast/helpers/mod.rs @@ -1 +1,3 @@ -pub mod where_clause; \ No newline at end of file +pub mod where_clause; +pub mod order_by_clause; +pub mod limit_clause; \ No newline at end of file diff --git a/src/cli/ast/helpers/order_by_clause.rs b/src/cli/ast/helpers/order_by_clause.rs new file mode 100644 index 0000000..87e4ee8 --- /dev/null +++ b/src/cli/ast/helpers/order_by_clause.rs @@ -0,0 +1,47 @@ +use crate::cli::ast::{parser::Parser, OrderByClause, OrderByDirection}; +use crate::cli::tokenizer::token::TokenTypes; + +use crate::cli::ast::common::expect_token_type; + +pub fn get_order_by(parser: &mut Parser) -> Result>, String> { + if expect_token_type(parser, TokenTypes::Order).is_err() { + return Ok(None); + } + parser.advance()?; + + expect_token_type(parser, TokenTypes::By)?; + parser.advance()?; + + let mut order_by_clauses = vec![]; + loop { + let token = parser.current_token()?; + expect_token_type(parser, TokenTypes::Identifier)?; + let column = token.value.to_string(); + parser.advance()?; + + let token = parser.current_token()?; + let direction = match token.token_type { + TokenTypes::Asc => { + parser.advance()?; + OrderByDirection::Asc + }, + TokenTypes::Desc => { + parser.advance()?; + OrderByDirection::Desc + }, + _ => OrderByDirection::Asc, + }; + + order_by_clauses.push(OrderByClause { + column: column, + direction: direction, + }); + + let token = parser.current_token()?; + if token.token_type != TokenTypes::Comma { + break; + } + parser.advance()?; + } + return Ok(Some(order_by_clauses)); +} \ No newline at end of file diff --git a/src/cli/ast/select_statement.rs b/src/cli/ast/select_statement.rs index a9bf312..7506dde 100644 --- a/src/cli/ast/select_statement.rs +++ b/src/cli/ast/select_statement.rs @@ -1,11 +1,11 @@ use crate::{cli::{ ast::{ - common::{expect_token_type, tokens_to_identifier_list, get_order_by, get_limit, get_table_name}, - parser::Parser, SelectStatement, SelectStatementColumns, SqlStatement, WhereClause + common::{expect_token_type, tokens_to_identifier_list, get_table_name}, + parser::Parser, SelectStatement, SelectStatementColumns, SqlStatement, WhereClause, + helpers::{order_by_clause::get_order_by, where_clause::get_where_clause, limit_clause::get_limit} }, tokenizer::token::TokenTypes }}; -use crate::cli::ast::helpers::where_clause::get_where_clause; pub fn build(parser: &mut Parser) -> Result { parser.advance()?; From 85d2533bac61a44029becb9854a86bd6ceb9e79b Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Wed, 3 Sep 2025 11:55:55 -0400 Subject: [PATCH 6/6] Clean up testing and modules to use helper functions located in /helpers --- src/cli/ast/create_statement.rs | 2 +- src/cli/ast/delete_statement.rs | 6 +- src/cli/ast/{ => helpers}/common.rs | 0 src/cli/ast/helpers/limit_clause.rs | 79 +++++++++++++++++++++++++- src/cli/ast/helpers/mod.rs | 3 +- src/cli/ast/helpers/order_by_clause.rs | 70 ++++++++++++++++++++++- src/cli/ast/helpers/where_clause.rs | 12 ++-- src/cli/ast/insert_statement.rs | 5 +- src/cli/ast/mod.rs | 1 - src/cli/ast/select_statement.rs | 64 ++------------------- src/cli/ast/update_statement.rs | 2 +- 11 files changed, 167 insertions(+), 77 deletions(-) rename src/cli/ast/{ => helpers}/common.rs (100%) diff --git a/src/cli/ast/create_statement.rs b/src/cli/ast/create_statement.rs index 198dce3..ded96b2 100644 --- a/src/cli/ast/create_statement.rs +++ b/src/cli/ast/create_statement.rs @@ -1,7 +1,7 @@ use crate::cli::{ ast::{ parser::Parser, CreateTableStatement, SqlStatement::{self, CreateTable}, - common::{expect_token_type, get_table_name} + helpers::common::{expect_token_type, get_table_name} }, tokenizer::token::TokenTypes }; diff --git a/src/cli/ast/delete_statement.rs b/src/cli/ast/delete_statement.rs index 1ac811d..185fde9 100644 --- a/src/cli/ast/delete_statement.rs +++ b/src/cli/ast/delete_statement.rs @@ -1,8 +1,10 @@ use crate::cli::{ ast::{ parser::Parser, SqlStatement, DeleteStatement, - common::{expect_token_type, get_table_name}, - helpers::{order_by_clause::get_order_by, where_clause::get_where_clause, limit_clause::get_limit} + helpers::{ + common::{expect_token_type, get_table_name}, + order_by_clause::get_order_by, where_clause::get_where_clause, limit_clause::get_limit + } }, tokenizer::token::TokenTypes }; diff --git a/src/cli/ast/common.rs b/src/cli/ast/helpers/common.rs similarity index 100% rename from src/cli/ast/common.rs rename to src/cli/ast/helpers/common.rs diff --git a/src/cli/ast/helpers/limit_clause.rs b/src/cli/ast/helpers/limit_clause.rs index 095945a..8788525 100644 --- a/src/cli/ast/helpers/limit_clause.rs +++ b/src/cli/ast/helpers/limit_clause.rs @@ -1,8 +1,7 @@ use crate::cli::ast::{parser::Parser, LimitClause}; use crate::cli::tokenizer::token::TokenTypes; use crate::db::table::Value; -use crate::cli::ast::common::expect_token_type; -use crate::cli::ast::common::token_to_value; +use crate::cli::ast::helpers::common::{expect_token_type, token_to_value}; pub fn get_limit(parser: &mut Parser) -> Result, String> { if expect_token_type(parser, TokenTypes::Limit).is_err() { @@ -36,4 +35,80 @@ pub fn get_limit(parser: &mut Parser) -> Result, String> { limit: limit, offset: Some(offset), })); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::ast::test_utils::token; + + #[test] + fn limit_clause_is_generated_correctly() { + // LIMIT 10 OFFSET 5; + let tokens = vec![ + token(TokenTypes::Limit, "LIMIT"), + token(TokenTypes::IntLiteral, "10"), + token(TokenTypes::Offset, "OFFSET"), + token(TokenTypes::IntLiteral, "5"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = get_limit(&mut parser); + assert!(result.is_ok()); + let limit_clause = result.unwrap(); + let expected = Some(LimitClause { + limit: Value::Integer(10), + offset: Some(Value::Integer(5)), + }); + assert_eq!(expected, limit_clause); + } + + #[test] + fn limit_clause_with_no_offset_is_generated_correctly() { + // LIMIT 10; + let tokens = vec![ + token(TokenTypes::Limit, "LIMIT"), + token(TokenTypes::IntLiteral, "10"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = get_limit(&mut parser); + assert!(result.is_ok()); + let limit_clause = result.unwrap(); + let expected = Some(LimitClause { + limit: Value::Integer(10), + offset: None, + }); + assert_eq!(expected, limit_clause); + } + + #[test] + fn not_limit_clause_returns_none() { + // SELECT * ...; + let tokens = vec![ + token(TokenTypes::Select, "SELECT"), + token(TokenTypes::Asterisk, "*"), + ]; + let mut parser = Parser::new(tokens); + let result = get_limit(&mut parser); + assert!(result.is_ok()); + assert!(result.unwrap().is_none()); + assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::Select); + } + + #[test] + fn limit_clause_with_negative_offset_is_generated_correctly() { + // LIMIT 10 OFFSET -5; + let tokens = vec![ + token(TokenTypes::Limit, "LIMIT"), + token(TokenTypes::IntLiteral, "10"), + token(TokenTypes::Offset, "OFFSET"), + token(TokenTypes::IntLiteral, "-5"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = get_limit(&mut parser); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "Error at line 1, column 0: Unexpected value: -5"); + } } \ No newline at end of file diff --git a/src/cli/ast/helpers/mod.rs b/src/cli/ast/helpers/mod.rs index 3407c8c..e9bfff0 100644 --- a/src/cli/ast/helpers/mod.rs +++ b/src/cli/ast/helpers/mod.rs @@ -1,3 +1,4 @@ pub mod where_clause; pub mod order_by_clause; -pub mod limit_clause; \ No newline at end of file +pub mod limit_clause; +pub mod common; \ No newline at end of file diff --git a/src/cli/ast/helpers/order_by_clause.rs b/src/cli/ast/helpers/order_by_clause.rs index 87e4ee8..9ccafc4 100644 --- a/src/cli/ast/helpers/order_by_clause.rs +++ b/src/cli/ast/helpers/order_by_clause.rs @@ -1,7 +1,7 @@ use crate::cli::ast::{parser::Parser, OrderByClause, OrderByDirection}; use crate::cli::tokenizer::token::TokenTypes; -use crate::cli::ast::common::expect_token_type; +use crate::cli::ast::helpers::common::expect_token_type; pub fn get_order_by(parser: &mut Parser) -> Result>, String> { if expect_token_type(parser, TokenTypes::Order).is_err() { @@ -44,4 +44,72 @@ pub fn get_order_by(parser: &mut Parser) -> Result>, S parser.advance()?; } return Ok(Some(order_by_clauses)); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::ast::test_utils::token; + + #[test] + fn order_by_clause_is_generated_correctly() { + // ORDER BY id ASC LIMIT...; + let tokens = vec![ + token(TokenTypes::Order, "ORDER"), + token(TokenTypes::By, "BY"), + token(TokenTypes::Identifier, "id"), + token(TokenTypes::Asc, "ASC"), + token(TokenTypes::Limit, "LIMIT"), + ]; + let mut parser = Parser::new(tokens); + let result = get_order_by(&mut parser); + assert!(result.is_ok()); + let order_by_clause = result.unwrap(); + let expected = Some(vec![OrderByClause { + column: "id".to_string(), + direction: OrderByDirection::Asc, + }]); + assert_eq!(expected, order_by_clause); + } + + #[test] + fn not_order_by_clause_returns_none() { + // SELECT * ...; + let tokens = vec![ + token(TokenTypes::Select, "SELECT"), + token(TokenTypes::Asterisk, "*"), + ]; + let mut parser = Parser::new(tokens); + let result = get_order_by(&mut parser); + assert!(result.is_ok()); + assert!(result.unwrap().is_none()); + assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::Select); + } + + #[test] + fn order_by_clause_with_multiple_columns_is_generated_correctly() { + // ORDER BY id ASC, name DESC; + let tokens = vec![ + token(TokenTypes::Order, "ORDER"), + token(TokenTypes::By, "BY"), + token(TokenTypes::Identifier, "id"), + token(TokenTypes::Asc, "ASC"), + token(TokenTypes::Comma, ","), + token(TokenTypes::Identifier, "name"), + token(TokenTypes::Desc, "DESC"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = get_order_by(&mut parser); + assert!(result.is_ok()); + let order_by_clause = result.unwrap(); + let expected = Some(vec![OrderByClause { + column: "id".to_string(), + direction: OrderByDirection::Asc, + }, OrderByClause { + column: "name".to_string(), + direction: OrderByDirection::Desc, + }]); + assert_eq!(expected, order_by_clause); + } } \ No newline at end of file diff --git a/src/cli/ast/helpers/where_clause.rs b/src/cli/ast/helpers/where_clause.rs index bef5f48..790e4f1 100644 --- a/src/cli/ast/helpers/where_clause.rs +++ b/src/cli/ast/helpers/where_clause.rs @@ -1,4 +1,4 @@ -use crate::cli::ast::{parser::Parser, WhereClause, Operator, common::{expect_token_type, token_to_value}}; +use crate::cli::ast::{parser::Parser, WhereClause, Operator, helpers::common::{expect_token_type, token_to_value}}; use crate::cli::tokenizer::token::TokenTypes; pub fn get_where_clause(parser: &mut Parser) -> Result, String> { @@ -51,13 +51,13 @@ mod tests { #[test] fn where_clause_with_all_tokens_is_generated_correctly() { - // WHERE id = 1; + // WHERE id = 1 LIMIT... let tokens = vec![ token(TokenTypes::Where, "WHERE"), token(TokenTypes::Identifier, "id"), token(TokenTypes::Equals, "="), token(TokenTypes::IntLiteral, "1"), - token(TokenTypes::SemiColon, ";"), + token(TokenTypes::Limit, "LIMIT"), ]; let mut parser = Parser::new(tokens); let result = get_where_clause(&mut parser); @@ -69,17 +69,15 @@ mod tests { value: Value::Integer(1), }); assert_eq!(expected, where_clause); - assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::SemiColon); + assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::Limit); } #[test] fn not_where_clause_returns_none() { - // SELECT * FROM users; + // SELECT * ...; let tokens = vec![ token(TokenTypes::Select, "SELECT"), token(TokenTypes::Asterisk, "*"), - token(TokenTypes::From, "FROM"), - token(TokenTypes::Identifier, "users"), ]; let mut parser = Parser::new(tokens); let result = get_where_clause(&mut parser); diff --git a/src/cli/ast/insert_statement.rs b/src/cli/ast/insert_statement.rs index ce26eb1..98a186e 100644 --- a/src/cli/ast/insert_statement.rs +++ b/src/cli/ast/insert_statement.rs @@ -1,5 +1,8 @@ use crate::cli::{ - ast::{parser::Parser, common::token_to_value, common::expect_token_type, common::get_table_name, InsertIntoStatement, SqlStatement::{self, InsertInto}}, + ast::{ + helpers::common::{token_to_value, expect_token_type, get_table_name}, + parser::Parser, InsertIntoStatement, SqlStatement::{self, InsertInto} + }, tokenizer::token::TokenTypes }; use crate::db::table::Value; diff --git a/src/cli/ast/mod.rs b/src/cli/ast/mod.rs index 3700dc3..8c1d5ef 100644 --- a/src/cli/ast/mod.rs +++ b/src/cli/ast/mod.rs @@ -1,7 +1,6 @@ use crate::cli::tokenizer::{scanner::Token, token::TokenTypes}; use crate::db::table::{ColumnDefinition, Value}; -mod common; mod create_statement; mod insert_statement; mod parser; diff --git a/src/cli/ast/select_statement.rs b/src/cli/ast/select_statement.rs index 7506dde..f034776 100644 --- a/src/cli/ast/select_statement.rs +++ b/src/cli/ast/select_statement.rs @@ -1,8 +1,10 @@ use crate::{cli::{ ast::{ - common::{expect_token_type, tokens_to_identifier_list, get_table_name}, parser::Parser, SelectStatement, SelectStatementColumns, SqlStatement, WhereClause, - helpers::{order_by_clause::get_order_by, where_clause::get_where_clause, limit_clause::get_limit} + helpers::{ + common::{expect_token_type, tokens_to_identifier_list, get_table_name}, + order_by_clause::get_order_by, where_clause::get_where_clause, limit_clause::get_limit + } }, tokenizer::token::TokenTypes }}; @@ -186,62 +188,4 @@ mod tests { }), })); } - - #[test] - fn select_statement_with_limit_clause_no_offset_is_generated_correctly() { - // SELECT id FROM guests WHERE id > 1 LIMIT 10; - let tokens = vec![ - token(TokenTypes::Select, "SELECT"), - token(TokenTypes::Identifier, "id"), - token(TokenTypes::From, "FROM"), - token(TokenTypes::Identifier, "guests"), - token(TokenTypes::Where, "WHERE"), - token(TokenTypes::Identifier, "id"), - token(TokenTypes::GreaterThan, ">"), - token(TokenTypes::IntLiteral, "1"), - token(TokenTypes::Limit, "LIMIT"), - token(TokenTypes::IntLiteral, "10"), - token(TokenTypes::SemiColon, ";"), - ]; - let mut parser = Parser::new(tokens); - let result = build(&mut parser); - assert!(result.is_ok()); - let statement = result.unwrap(); - assert_eq!(statement, SqlStatement::Select(SelectStatement { - table_name: "guests".to_string(), - columns: SelectStatementColumns::Specific(vec![ - "id".to_string(), - ]), - where_clause: Some(WhereClause { - column: "id".to_string(), - operator: Operator::GreaterThan, - value: Value::Integer(1), - }), - order_by_clause: None, - limit_clause: Some(LimitClause { - limit: Value::Integer(10), - offset: None, - }), - })); - } - - #[test] - fn select_statement_with_limit_clause_with_negative_offset_is_generated_correctly() { - // SELECT id FROM guests LIMIT 10 OFFSET -5; - let tokens = vec![ - token(TokenTypes::Select, "SELECT"), - token(TokenTypes::Identifier, "id"), - token(TokenTypes::From, "FROM"), - token(TokenTypes::Identifier, "guests"), - token(TokenTypes::Limit, "LIMIT"), - token(TokenTypes::IntLiteral, "10"), - token(TokenTypes::Offset, "OFFSET"), - token(TokenTypes::IntLiteral, "-5"), - token(TokenTypes::SemiColon, ";"), - ]; - let mut parser = Parser::new(tokens); - let result = build(&mut parser); - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), "Error at line 1, column 0: Unexpected value: -5"); - } } \ No newline at end of file diff --git a/src/cli/ast/update_statement.rs b/src/cli/ast/update_statement.rs index 3a19022..7bbdc05 100644 --- a/src/cli/ast/update_statement.rs +++ b/src/cli/ast/update_statement.rs @@ -1,6 +1,6 @@ use crate::cli::ast::{ parser::Parser, SqlStatement, UpdateStatement, ColumnValue, - common::{expect_token_type, token_to_value, get_table_name} + helpers::common::{expect_token_type, token_to_value, get_table_name} }; use crate::cli::ast::helpers::where_clause::get_where_clause; use crate::cli::tokenizer::token::TokenTypes;