From 69b37a2ed6ecb6d82e009f4a9e24902a9f7d0c95 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Tue, 16 Sep 2025 12:12:59 -0400 Subject: [PATCH 1/6] Add integration test for select statements with nums --- tests/crud_test.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/crud_test.rs b/tests/crud_test.rs index 093c56a..c34ba64 100644 --- a/tests/crud_test.rs +++ b/tests/crud_test.rs @@ -249,3 +249,34 @@ fn test_distinct_with_limit_and_offset() { let row = result[5].as_ref().unwrap().as_ref().unwrap(); assert_eq!(expected, *row); } + +#[test] +fn test_limit_clause() { + let mut database = Database::new(); + let sql = " + SELECT 1 FROM test_table; -- This should fail because table does not exist. + CREATE TABLE test_table ( + id INTEGER, + name TEXT + ); + SELECT 'alice' FROM test_table; -- This should succeed and return nothing. + INSERT INTO test_table (id, name) VALUES (1, 'John'); + SELECT name, 1.24 FROM test_table; -- This should succeed and return 1.24. + INSERT INTO test_table (id, name) VALUES (2, 'Jane'); + SELECT id, 'bob' FROM test_table; -- This should succeed and return two rows. + "; + let result = run_sql(&mut database, sql); + let expected = vec![ + Err("Execution Error with statement starting on line 2 \n Error: Table `test_table` does not exist".to_string()), + Ok(None), + Ok(Some(vec![])), + Ok(None), + Ok(Some(vec![Row(vec![Value::Text("John".to_string()), Value::Real(1.24)])])), + Ok(None), + Ok(Some(vec![ + Row(vec![Value::Integer(1), Value::Text("bob".to_string())]), + Row(vec![Value::Integer(2), Value::Text("bob".to_string())]), + ])), + ]; + assert_eq!(expected, result); +} \ No newline at end of file From 4a0ff3f8c4988a93770e41deffc93a87fe7556c1 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Tue, 16 Sep 2025 12:27:17 -0400 Subject: [PATCH 2/6] Fix bug with HexLiterals / Blobs being treated incorrectly --- src/interpreter/ast/helpers/token.rs | 4 ++-- .../ast/helpers/where_clause/where_condition.rs | 4 ++-- src/interpreter/ast/insert_statement.rs | 2 +- tests/crud_test.rs | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/interpreter/ast/helpers/token.rs b/src/interpreter/ast/helpers/token.rs index 6029bda..b6defd5 100644 --- a/src/interpreter/ast/helpers/token.rs +++ b/src/interpreter/ast/helpers/token.rs @@ -31,8 +31,8 @@ pub fn token_to_value(parser: &Parser) -> Result { .map_err(|_| parser.format_error())?; Ok(Value::Real(num)) } - TokenTypes::String => Ok(Value::Text(token.value.to_string())), - TokenTypes::Blob => { + TokenTypes::String => Ok(Value::Text(token.value.to_string())), // TODO: rename to StringLiteral + TokenTypes::HexLiteral => { let bytes = hex_decode(token.value).map_err(|_| parser.format_error())?; Ok(Value::Blob(bytes)) } diff --git a/src/interpreter/ast/helpers/where_clause/where_condition.rs b/src/interpreter/ast/helpers/where_clause/where_condition.rs index 029955c..9aed271 100644 --- a/src/interpreter/ast/helpers/where_clause/where_condition.rs +++ b/src/interpreter/ast/helpers/where_clause/where_condition.rs @@ -60,7 +60,7 @@ pub fn get_operand(parser: &mut Parser) -> Result { TokenTypes::IntLiteral => Ok(Operand::Value(token_to_value(parser)?)), TokenTypes::RealLiteral => Ok(Operand::Value(token_to_value(parser)?)), TokenTypes::String => Ok(Operand::Value(token_to_value(parser)?)), - TokenTypes::Blob => Ok(Operand::Value(token_to_value(parser)?)), + TokenTypes::HexLiteral => Ok(Operand::Value(token_to_value(parser)?)), TokenTypes::Null => Ok(Operand::Value(token_to_value(parser)?)), TokenTypes::LeftParen => { parser.advance()?; @@ -226,7 +226,7 @@ mod tests { fn where_condition_handles_invalid_not_in_statement() { // X'00' NOT (1);... let tokens = vec![ - token(TokenTypes::Blob, "00"), + token(TokenTypes::HexLiteral, "FF"), token(TokenTypes::Not, "NOT"), token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "1"), diff --git a/src/interpreter/ast/insert_statement.rs b/src/interpreter/ast/insert_statement.rs index a5ae094..91f32eb 100644 --- a/src/interpreter/ast/insert_statement.rs +++ b/src/interpreter/ast/insert_statement.rs @@ -223,7 +223,7 @@ mod tests { token(TokenTypes::LeftParen, "("), token(TokenTypes::RealLiteral, "1.1"), token(TokenTypes::Comma, ","), - token(TokenTypes::Blob, "AAB000"), + token(TokenTypes::HexLiteral, "AAB000"), token(TokenTypes::Comma, ","), token(TokenTypes::Null, "NULL"), token(TokenTypes::RightParen, ")"), diff --git a/tests/crud_test.rs b/tests/crud_test.rs index c34ba64..196e8ca 100644 --- a/tests/crud_test.rs +++ b/tests/crud_test.rs @@ -251,7 +251,7 @@ fn test_distinct_with_limit_and_offset() { } #[test] -fn test_limit_clause() { +fn test_select_clauses_with_literals() { let mut database = Database::new(); let sql = " SELECT 1 FROM test_table; -- This should fail because table does not exist. @@ -263,7 +263,7 @@ fn test_limit_clause() { INSERT INTO test_table (id, name) VALUES (1, 'John'); SELECT name, 1.24 FROM test_table; -- This should succeed and return 1.24. INSERT INTO test_table (id, name) VALUES (2, 'Jane'); - SELECT id, 'bob' FROM test_table; -- This should succeed and return two rows. + SELECT X'1234', id, NULL FROM test_table; -- This should succeed and return two rows. "; let result = run_sql(&mut database, sql); let expected = vec![ @@ -274,8 +274,8 @@ fn test_limit_clause() { Ok(Some(vec![Row(vec![Value::Text("John".to_string()), Value::Real(1.24)])])), Ok(None), Ok(Some(vec![ - Row(vec![Value::Integer(1), Value::Text("bob".to_string())]), - Row(vec![Value::Integer(2), Value::Text("bob".to_string())]), + Row(vec![Value::Blob(vec![18, 52]), Value::Integer(1), Value::Null]), + Row(vec![Value::Blob(vec![18, 52]), Value::Integer(2), Value::Null]), ])), ]; assert_eq!(expected, result); From 055da46a81d8d83077b3ecb96d215389c947a330 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Tue, 16 Sep 2025 12:30:23 -0400 Subject: [PATCH 3/6] Rename Add literal to the suffix of all tokens that are literals --- src/interpreter/ast/helpers/common.rs | 2 +- src/interpreter/ast/helpers/token.rs | 4 ++-- .../ast/helpers/where_clause/mod.rs | 14 +++++++------- .../helpers/where_clause/where_condition.rs | 4 ++-- .../where_clause/where_stack_element.rs | 2 +- src/interpreter/ast/insert_statement.rs | 18 +++++++++--------- src/interpreter/ast/mod.rs | 8 ++++---- src/interpreter/ast/select_statement_stack.rs | 4 ++-- src/interpreter/ast/update_statement.rs | 4 ++-- src/interpreter/tokenizer/mod.rs | 14 +++++++------- src/interpreter/tokenizer/scanner.rs | 8 ++++---- src/interpreter/tokenizer/token.rs | 6 +++--- 12 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/interpreter/ast/helpers/common.rs b/src/interpreter/ast/helpers/common.rs index 2a39a62..a7b9838 100644 --- a/src/interpreter/ast/helpers/common.rs +++ b/src/interpreter/ast/helpers/common.rs @@ -221,7 +221,7 @@ pub fn get_selectables( // Literals TokenTypes::IntLiteral => SelectableStackElement::Value(token_to_value(parser)?), TokenTypes::RealLiteral => SelectableStackElement::Value(token_to_value(parser)?), - TokenTypes::String => SelectableStackElement::Value(token_to_value(parser)?), + TokenTypes::StringLiteral => SelectableStackElement::Value(token_to_value(parser)?), TokenTypes::HexLiteral => SelectableStackElement::Value(token_to_value(parser)?), TokenTypes::Null => SelectableStackElement::Value(token_to_value(parser)?), // TODO: handle ValueList (arrays) diff --git a/src/interpreter/ast/helpers/token.rs b/src/interpreter/ast/helpers/token.rs index b6defd5..190e893 100644 --- a/src/interpreter/ast/helpers/token.rs +++ b/src/interpreter/ast/helpers/token.rs @@ -31,7 +31,7 @@ pub fn token_to_value(parser: &Parser) -> Result { .map_err(|_| parser.format_error())?; Ok(Value::Real(num)) } - TokenTypes::String => Ok(Value::Text(token.value.to_string())), // TODO: rename to StringLiteral + TokenTypes::StringLiteral => Ok(Value::Text(token.value.to_string())), // TODO: rename to StringLiteral TokenTypes::HexLiteral => { let bytes = hex_decode(token.value).map_err(|_| parser.format_error())?; Ok(Value::Blob(bytes)) @@ -70,7 +70,7 @@ pub fn token_to_data_type(parser: &mut Parser) -> Result { pub fn token_to_string(token: &Token) -> String { match token.token_type { - TokenTypes::String => format!("'{}'", token.value), + TokenTypes::StringLiteral => format!("'{}'", token.value), TokenTypes::HexLiteral => format!("X'{}'", token.value), TokenTypes::EOF | TokenTypes::SemiColon diff --git a/src/interpreter/ast/helpers/where_clause/mod.rs b/src/interpreter/ast/helpers/where_clause/mod.rs index 133f7fa..657a076 100644 --- a/src/interpreter/ast/helpers/where_clause/mod.rs +++ b/src/interpreter/ast/helpers/where_clause/mod.rs @@ -140,7 +140,7 @@ mod tests { token(TokenTypes::And, "AND"), token(TokenTypes::Identifier, "name"), token(TokenTypes::Equals, "="), - token(TokenTypes::String, "John"), + token(TokenTypes::StringLiteral, "John"), token(TokenTypes::SemiColon, ";"), ]; let mut parser = Parser::new(tokens); @@ -171,7 +171,7 @@ mod tests { token(TokenTypes::And, "AND"), token(TokenTypes::Identifier, "name"), token(TokenTypes::Equals, "="), - token(TokenTypes::String, "John"), + token(TokenTypes::StringLiteral, "John"), token(TokenTypes::Or, "OR"), token(TokenTypes::Identifier, "age"), token(TokenTypes::GreaterThan, ">"), @@ -209,7 +209,7 @@ mod tests { token(TokenTypes::Not, "NOT"), token(TokenTypes::Identifier, "name"), token(TokenTypes::Equals, "="), - token(TokenTypes::String, "John"), + token(TokenTypes::StringLiteral, "John"), token(TokenTypes::And, "AND"), token(TokenTypes::Not, "NOT"), token(TokenTypes::Identifier, "age"), @@ -249,7 +249,7 @@ mod tests { token(TokenTypes::Or, "OR"), token(TokenTypes::Identifier, "name"), token(TokenTypes::Equals, "="), - token(TokenTypes::String, "John"), + token(TokenTypes::StringLiteral, "John"), token(TokenTypes::RightParen, ")"), token(TokenTypes::And, "AND"), token(TokenTypes::Not, "NOT"), @@ -299,7 +299,7 @@ mod tests { token(TokenTypes::LeftParen, "("), token(TokenTypes::Identifier, "name"), token(TokenTypes::Equals, "="), - token(TokenTypes::String, "John"), + token(TokenTypes::StringLiteral, "John"), token(TokenTypes::And, "AND"), token(TokenTypes::Identifier, "age"), token(TokenTypes::GreaterThan, ">"), @@ -339,7 +339,7 @@ mod tests { token(TokenTypes::Or, "OR"), token(TokenTypes::Identifier, "name"), token(TokenTypes::Equals, "="), - token(TokenTypes::String, "John"), + token(TokenTypes::StringLiteral, "John"), token(TokenTypes::SemiColon, ";"), ]; let mut parser = Parser::new(tokens); @@ -365,7 +365,7 @@ mod tests { token(TokenTypes::Or, "OR"), token(TokenTypes::Identifier, "name"), token(TokenTypes::Equals, "="), - token(TokenTypes::String, "John"), + token(TokenTypes::StringLiteral, "John"), token(TokenTypes::RightParen, ")"), token(TokenTypes::RightParen, ")"), token(TokenTypes::SemiColon, ";"), diff --git a/src/interpreter/ast/helpers/where_clause/where_condition.rs b/src/interpreter/ast/helpers/where_clause/where_condition.rs index 9aed271..dda7681 100644 --- a/src/interpreter/ast/helpers/where_clause/where_condition.rs +++ b/src/interpreter/ast/helpers/where_clause/where_condition.rs @@ -59,7 +59,7 @@ pub fn get_operand(parser: &mut Parser) -> Result { TokenTypes::Identifier => Ok(Operand::Identifier(token.value.to_string())), TokenTypes::IntLiteral => Ok(Operand::Value(token_to_value(parser)?)), TokenTypes::RealLiteral => Ok(Operand::Value(token_to_value(parser)?)), - TokenTypes::String => Ok(Operand::Value(token_to_value(parser)?)), + TokenTypes::StringLiteral => Ok(Operand::Value(token_to_value(parser)?)), TokenTypes::HexLiteral => Ok(Operand::Value(token_to_value(parser)?)), TokenTypes::Null => Ok(Operand::Value(token_to_value(parser)?)), TokenTypes::LeftParen => { @@ -188,7 +188,7 @@ mod tests { fn where_stack_handles_reversed_condition() { // "fletcher" < id;... let tokens = vec![ - token(TokenTypes::String, "fletcher"), + token(TokenTypes::StringLiteral, "fletcher"), token(TokenTypes::LessThan, "<"), token(TokenTypes::Identifier, "id"), token(TokenTypes::SemiColon, ";"), diff --git a/src/interpreter/ast/helpers/where_clause/where_stack_element.rs b/src/interpreter/ast/helpers/where_clause/where_stack_element.rs index bcf0756..e4c2825 100644 --- a/src/interpreter/ast/helpers/where_clause/where_stack_element.rs +++ b/src/interpreter/ast/helpers/where_clause/where_stack_element.rs @@ -27,7 +27,7 @@ pub fn get_where_stack_element( TokenTypes::Identifier | TokenTypes::IntLiteral | TokenTypes::RealLiteral - | TokenTypes::String + | TokenTypes::StringLiteral | TokenTypes::Blob | TokenTypes::Null => { return Ok(Some(WhereStackElement::Condition(get_condition(parser)?))); diff --git a/src/interpreter/ast/insert_statement.rs b/src/interpreter/ast/insert_statement.rs index 91f32eb..5e719b9 100644 --- a/src/interpreter/ast/insert_statement.rs +++ b/src/interpreter/ast/insert_statement.rs @@ -149,7 +149,7 @@ mod tests { token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "1"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Alice"), + token(TokenTypes::StringLiteral, "Alice"), token(TokenTypes::RightParen, ")"), token(TokenTypes::SemiColon, ";"), ]; @@ -178,13 +178,13 @@ mod tests { token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "1"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Alice"), + token(TokenTypes::StringLiteral, "Alice"), token(TokenTypes::RightParen, ")"), token(TokenTypes::Comma, ","), token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "2"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Bob"), + token(TokenTypes::StringLiteral, "Bob"), token(TokenTypes::RightParen, ")"), token(TokenTypes::SemiColon, ";"), ]; @@ -259,7 +259,7 @@ mod tests { token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "1"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Alice"), + token(TokenTypes::StringLiteral, "Alice"), token(TokenTypes::RightParen, ")"), token(TokenTypes::SemiColon, ";"), ]; @@ -295,15 +295,15 @@ mod tests { token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "1"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Alice"), + token(TokenTypes::StringLiteral, "Alice"), token(TokenTypes::RightParen, ")"), token(TokenTypes::Comma, ","), token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "2"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Bob"), + token(TokenTypes::StringLiteral, "Bob"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Charlie"), + token(TokenTypes::StringLiteral, "Charlie"), token(TokenTypes::RightParen, ")"), token(TokenTypes::SemiColon, ";"), ]; @@ -330,9 +330,9 @@ mod tests { token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "1"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Alice"), + token(TokenTypes::StringLiteral, "Alice"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Bob"), + token(TokenTypes::StringLiteral, "Bob"), token(TokenTypes::RightParen, ")"), token(TokenTypes::SemiColon, ";"), ]; diff --git a/src/interpreter/ast/mod.rs b/src/interpreter/ast/mod.rs index ebc323b..18e7d29 100644 --- a/src/interpreter/ast/mod.rs +++ b/src/interpreter/ast/mod.rs @@ -436,7 +436,7 @@ mod tests { token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "1"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Alice"), + token(TokenTypes::StringLiteral, "Alice"), token(TokenTypes::RightParen, ")"), token(TokenTypes::SemiColon, ";"), token(TokenTypes::EOF, ""), @@ -465,7 +465,7 @@ mod tests { token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "1"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Alice"), + token(TokenTypes::StringLiteral, "Alice"), token(TokenTypes::RightParen, ")"), token(TokenTypes::SemiColon, ";"), token(TokenTypes::EOF, ""), @@ -521,7 +521,7 @@ mod tests { token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "1"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Alice"), + token(TokenTypes::StringLiteral, "Alice"), token(TokenTypes::RightParen, ")"), token(TokenTypes::SemiColon, ";"), token(TokenTypes::EOF, ""), @@ -559,7 +559,7 @@ mod tests { token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "1"), token(TokenTypes::Comma, ","), - token(TokenTypes::String, "Alice"), + token(TokenTypes::StringLiteral, "Alice"), token(TokenTypes::RightParen, ")"), token(TokenTypes::SemiColon, ";"), token(TokenTypes::EOF, ""), diff --git a/src/interpreter/ast/select_statement_stack.rs b/src/interpreter/ast/select_statement_stack.rs index fa63aaf..5c3f57b 100644 --- a/src/interpreter/ast/select_statement_stack.rs +++ b/src/interpreter/ast/select_statement_stack.rs @@ -329,7 +329,7 @@ mod tests { token(TokenTypes::Where, "WHERE"), token(TokenTypes::Identifier, "name"), token(TokenTypes::Equals, "="), - token(TokenTypes::String, "Henry"), + token(TokenTypes::StringLiteral, "Henry"), token(TokenTypes::Union, "UNION"), token(TokenTypes::All, "ALL"), token(TokenTypes::Select, "SELECT"), @@ -339,7 +339,7 @@ mod tests { token(TokenTypes::Where, "WHERE"), token(TokenTypes::Identifier, "name"), token(TokenTypes::Equals, "="), - token(TokenTypes::String, "John"), + token(TokenTypes::StringLiteral, "John"), token(TokenTypes::Order, "ORDER"), token(TokenTypes::By, "BY"), token(TokenTypes::Identifier, "name"), diff --git a/src/interpreter/ast/update_statement.rs b/src/interpreter/ast/update_statement.rs index c86257e..c293916 100644 --- a/src/interpreter/ast/update_statement.rs +++ b/src/interpreter/ast/update_statement.rs @@ -85,7 +85,7 @@ mod tests { token(TokenTypes::Set, "SET"), token(TokenTypes::Identifier, "column"), token(TokenTypes::Equals, "="), - token(TokenTypes::String, "value"), + token(TokenTypes::StringLiteral, "value"), token(TokenTypes::SemiColon, ";"), ]; let mut parser = Parser::new(tokens); @@ -155,7 +155,7 @@ mod tests { token(TokenTypes::Comma, ","), token(TokenTypes::Identifier, "active"), token(TokenTypes::Equals, "="), - token(TokenTypes::String, "False"), + token(TokenTypes::StringLiteral, "False"), token(TokenTypes::Where, "WHERE"), token(TokenTypes::Identifier, "id"), token(TokenTypes::Equals, "="), diff --git a/src/interpreter/tokenizer/mod.rs b/src/interpreter/tokenizer/mod.rs index f5857ef..b2c9254 100644 --- a/src/interpreter/tokenizer/mod.rs +++ b/src/interpreter/tokenizer/mod.rs @@ -47,7 +47,7 @@ mod tests { token(TokenTypes::Where, "WHERE", 20, 1), token(TokenTypes::Identifier, "name", 26, 1), token(TokenTypes::Equals, "=", 31, 1), - token(TokenTypes::String, "Fletcher", 33, 1), + token(TokenTypes::StringLiteral, "Fletcher", 33, 1), token(TokenTypes::SemiColon, ";", 43, 1), token(TokenTypes::EOF, "", 0, 0), ]; @@ -66,7 +66,7 @@ mod tests { token(TokenTypes::LeftParen, "(", 28, 1), token(TokenTypes::IntLiteral, "199", 29, 1), token(TokenTypes::RightParen, ")", 32, 1), - token(TokenTypes::String, "Fletcher", 0, 2), + token(TokenTypes::StringLiteral, "Fletcher", 0, 2), token(TokenTypes::Error, "\"", 10, 2), token(TokenTypes::EOF, "", 0, 0), ]; @@ -168,9 +168,9 @@ mod tests { token(TokenTypes::Minus, "-", 10, 14), token(TokenTypes::Divide, "/", 12, 14), token(TokenTypes::Modulo, "%", 14, 14), - token(TokenTypes::String, "string", 8, 15), - token(TokenTypes::True, "TRUE", 17, 15), - token(TokenTypes::False, "FALSE", 22, 15), + token(TokenTypes::StringLiteral, "string", 8, 15), + token(TokenTypes::TrueLiteral, "TRUE", 17, 15), + token(TokenTypes::FalseLiteral, "FALSE", 22, 15), token(TokenTypes::Identifier, "fletchers_table", 8, 16), token(TokenTypes::EOF, "", 0, 0), ]; @@ -210,7 +210,7 @@ mod tests { fn tokenizer_parses_string_identifiers() { let result = tokenize("'string' \"identifier\""); let expected = vec![ - token(TokenTypes::String, "string", 0, 1), + token(TokenTypes::StringLiteral, "string", 0, 1), token(TokenTypes::Identifier, "identifier", 9, 1), token(TokenTypes::EOF, "", 0, 0), ]; @@ -243,7 +243,7 @@ mod tests { token(TokenTypes::Where, "WHERE", 20, 1), token(TokenTypes::Identifier, "name", 26, 1), token(TokenTypes::Equals, "=", 31, 1), - token(TokenTypes::String, "Fletcher", 33, 1), + token(TokenTypes::StringLiteral, "Fletcher", 33, 1), token(TokenTypes::SemiColon, ";", 43, 1), token(TokenTypes::EOF, "", 0, 0), ]; diff --git a/src/interpreter/tokenizer/scanner.rs b/src/interpreter/tokenizer/scanner.rs index 717e140..74755f5 100644 --- a/src/interpreter/tokenizer/scanner.rs +++ b/src/interpreter/tokenizer/scanner.rs @@ -65,7 +65,7 @@ impl<'a> Scanner<'a> { fn build_string_token(&mut self, start: usize, token_type: TokenTypes) -> Token<'a> { return match token_type { - TokenTypes::String => { + TokenTypes::StringLiteral => { self.advance(); Token { token_type: token_type, @@ -111,7 +111,7 @@ impl<'a> Scanner<'a> { return TokenTypes::Error; } } - return TokenTypes::String; + return TokenTypes::StringLiteral; } fn read_identifier(&mut self, start: usize) -> TokenTypes { @@ -195,8 +195,8 @@ impl<'a> Scanner<'a> { slice if slice.eq_ignore_ascii_case("AVG") => TokenTypes::Avg, slice if slice.eq_ignore_ascii_case("MIN") => TokenTypes::Min, slice if slice.eq_ignore_ascii_case("MAX") => TokenTypes::Max, - slice if slice.eq_ignore_ascii_case("TRUE") => TokenTypes::True, - slice if slice.eq_ignore_ascii_case("FALSE") => TokenTypes::False, + slice if slice.eq_ignore_ascii_case("TRUE") => TokenTypes::TrueLiteral, + slice if slice.eq_ignore_ascii_case("FALSE") => TokenTypes::FalseLiteral, _ => TokenTypes::Identifier, }; } diff --git a/src/interpreter/tokenizer/token.rs b/src/interpreter/tokenizer/token.rs index 89fd39c..56dab9e 100644 --- a/src/interpreter/tokenizer/token.rs +++ b/src/interpreter/tokenizer/token.rs @@ -103,10 +103,10 @@ pub enum TokenTypes { Modulo, // Literals // TODO: add Literal - String, + StringLiteral, IntLiteral, - True, - False, + TrueLiteral, // Make these work as aliases for 1 and 0 + FalseLiteral, HexLiteral, RealLiteral, // Others From f6bff52c1d62d0d8274ac33f3c453770c1ced157 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Tue, 16 Sep 2025 12:40:06 -0400 Subject: [PATCH 4/6] Add test for order by clause with precompute --- src/db/table/helpers/order_by_clause.rs | 102 +++++++----------------- src/interpreter/tokenizer/token.rs | 1 - 2 files changed, 30 insertions(+), 73 deletions(-) diff --git a/src/db/table/helpers/order_by_clause.rs b/src/db/table/helpers/order_by_clause.rs index a24b424..670e376 100644 --- a/src/db/table/helpers/order_by_clause.rs +++ b/src/db/table/helpers/order_by_clause.rs @@ -23,6 +23,7 @@ pub fn apply_order_by_from_precomputed( fn perform_comparisons(row1: &Row, row2: &Row, order_by_clause: &OrderByClause) -> Ordering { // TODO: small optimization: only compute subsequent selectables if the previous ordering resulted in equality + // Fletcher - I think this is done here? Can this comment be removed? for (i, direction) in order_by_clause.directions.iter().enumerate() { let ordering = row1[i].compare(&row2[i], direction); if ordering != Ordering::Equal { @@ -35,78 +36,35 @@ fn perform_comparisons(row1: &Row, row2: &Row, order_by_clause: &OrderByClause) #[cfg(test)] mod tests { - use crate::db::table::{ColumnDefinition, DataType, Row, RowStack, Table, Value}; + use super::*; + use crate::db::table::{Row, Value}; + use crate::interpreter::ast::OrderByClause; + use crate::interpreter::ast::SelectableStack; + use crate::interpreter::ast::SelectableStackElement; + use crate::interpreter::ast::OrderByDirection; - fn _default_table() -> Table { - Table { - name: "users".to_string(), - columns: vec![ - ColumnDefinition { - name: "id".to_string(), - data_type: DataType::Integer, - constraints: vec![], - }, - ColumnDefinition { - name: "name".to_string(), - data_type: DataType::Text, - constraints: vec![], - }, - ColumnDefinition { - name: "money".to_string(), - data_type: DataType::Real, - constraints: vec![], - }, - ColumnDefinition { - name: "some_data".to_string(), - data_type: DataType::Blob, - constraints: vec![], - }, - ], - rows: vec![ - RowStack::new(Row(vec![ - Value::Integer(3), - Value::Text("c_Jim".to_string()), - Value::Real(3000.0), - Value::Blob(b"0022".to_vec()), - ])), - RowStack::new(Row(vec![ - Value::Integer(1), - Value::Text("a_John".to_string()), - Value::Real(1000.0), - Value::Blob(b"0000".to_vec()), - ])), - RowStack::new(Row(vec![ - Value::Null, - Value::Null, - Value::Null, - Value::Null, - ])), - RowStack::new(Row(vec![ - Value::Integer(2), - Value::Text("b_Jane".to_string()), - Value::Real(2000.0), - Value::Blob(b"0201".to_vec()), - ])), - RowStack::new(Row(vec![ - Value::Integer(3), - Value::Text("b_Jim".to_string()), - Value::Real(1500.0), - Value::Blob(b"0102".to_vec()), - ])), - RowStack::new(Row(vec![ - Value::Integer(4), - Value::Text("a_Jim".to_string()), - Value::Real(500.0), - Value::Blob(b"0101".to_vec()), - ])), - RowStack::new(Row(vec![ - Value::Integer(1), - Value::Text("a_Jim".to_string()), - Value::Real(5000.0), - Value::Blob(b"0401".to_vec()), - ])), - ], - } + #[test] + fn apply_order_by_from_precomputed_single_column_asc() { + let mut to_order = vec!["second", "fourth", "third", "first"]; + + let precomputed = vec![ + Row(vec![Value::Integer(25)]), + Row(vec![Value::Integer(55)]), + Row(vec![Value::Integer(35)]), + Row(vec![Value::Integer(22)]), + ]; + + let order_by_clause = OrderByClause { + columns: SelectableStack { + selectables: vec![SelectableStackElement::Column("age".to_string())], + }, + column_names: vec!["age".to_string()], + directions: vec![OrderByDirection::Asc], + }; + + apply_order_by_from_precomputed(&mut to_order, precomputed, "default", &order_by_clause); + + assert_eq!(to_order, vec!["first", "second", "third", "fourth"]); } - // TODO: tests + // TODO: add more tests } diff --git a/src/interpreter/tokenizer/token.rs b/src/interpreter/tokenizer/token.rs index 56dab9e..af576fa 100644 --- a/src/interpreter/tokenizer/token.rs +++ b/src/interpreter/tokenizer/token.rs @@ -102,7 +102,6 @@ pub enum TokenTypes { Divide, Modulo, // Literals - // TODO: add Literal StringLiteral, IntLiteral, TrueLiteral, // Make these work as aliases for 1 and 0 From 9b92a57f8bda99a94f6894b1abe26706f4757691 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Tue, 16 Sep 2025 12:46:09 -0400 Subject: [PATCH 5/6] Swap from returning an int to returning an order --- src/interpreter/ast/helpers/common.rs | 29 +++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/interpreter/ast/helpers/common.rs b/src/interpreter/ast/helpers/common.rs index a7b9838..512fc17 100644 --- a/src/interpreter/ast/helpers/common.rs +++ b/src/interpreter/ast/helpers/common.rs @@ -5,6 +5,7 @@ use crate::interpreter::{ }, tokenizer::token::TokenTypes, }; +use std::cmp::Ordering; // Returns an error if the current token does not match the given token type pub fn expect_token_type(parser: &Parser, token_type: TokenTypes) -> Result<(), String> { @@ -191,7 +192,7 @@ pub fn get_selectables( match operators.last() { Some(last) => match last { ExtendedSelectableStackElement::SelectableStackElement(inner) => { - if compare_precedence(&value, inner)? > 0 { + if compare_precedence(&value, inner)? == Ordering::Greater { output.push(inner.clone()); operators.pop(); } else { @@ -277,15 +278,15 @@ pub fn exists_clause( pub fn compare_precedence( first: &SelectableStackElement, second: &SelectableStackElement, -) -> Result { +) -> Result { let first_precedence = get_precedence(first)?; let second_precedence = get_precedence(second)?; return if second_precedence == first_precedence { - Ok(0) + Ok(Ordering::Equal) } else if second_precedence > first_precedence { - Ok(-1) + Ok(Ordering::Less) } else { - Ok(1) + Ok(Ordering::Greater) }; } @@ -336,7 +337,6 @@ pub fn hex_decode(hex: &str) -> Result, String> { #[cfg(test)] mod tests { use super::*; - // TODO: add more tests #[test] fn decode_handles_valid_hex_string() { @@ -358,4 +358,21 @@ mod tests { let expected = "Hex string must have even length"; assert_eq!(expected, result.err().unwrap()); } + + #[test] + fn precedence_handles_correctly() { + let result = compare_precedence(&SelectableStackElement::MathOperator(MathOperator::Multiply), &SelectableStackElement::MathOperator(MathOperator::Add)); + assert!(result.is_ok()); + assert_eq!(Ordering::Greater, result.unwrap()); + let result = compare_precedence(&SelectableStackElement::MathOperator(MathOperator::Add), &SelectableStackElement::MathOperator(MathOperator::Multiply)); + assert!(result.is_ok()); + assert_eq!(Ordering::Less, result.unwrap()); + let result = compare_precedence(&SelectableStackElement::LogicalOperator(LogicalOperator::And), &SelectableStackElement::LogicalOperator(LogicalOperator::Or)); + assert!(result.is_ok()); + assert_eq!(Ordering::Greater, result.unwrap()); + let result = compare_precedence(&SelectableStackElement::LogicalOperator(LogicalOperator::Not), &SelectableStackElement::LogicalOperator(LogicalOperator::And)); + assert!(result.is_ok()); + assert_eq!(Ordering::Greater, result.unwrap()); + } + // TODO: add more tests } From 35a4285e6623b593e2290a2ce78ec80c89cd2a94 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Tue, 16 Sep 2025 12:46:23 -0400 Subject: [PATCH 6/6] Prettify --- src/db/table/helpers/order_by_clause.rs | 4 ++-- src/interpreter/ast/helpers/common.rs | 20 ++++++++++++++++---- tests/crud_test.rs | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/db/table/helpers/order_by_clause.rs b/src/db/table/helpers/order_by_clause.rs index 670e376..76ff3ef 100644 --- a/src/db/table/helpers/order_by_clause.rs +++ b/src/db/table/helpers/order_by_clause.rs @@ -39,14 +39,14 @@ mod tests { use super::*; use crate::db::table::{Row, Value}; use crate::interpreter::ast::OrderByClause; + use crate::interpreter::ast::OrderByDirection; use crate::interpreter::ast::SelectableStack; use crate::interpreter::ast::SelectableStackElement; - use crate::interpreter::ast::OrderByDirection; #[test] fn apply_order_by_from_precomputed_single_column_asc() { let mut to_order = vec!["second", "fourth", "third", "first"]; - + let precomputed = vec![ Row(vec![Value::Integer(25)]), Row(vec![Value::Integer(55)]), diff --git a/src/interpreter/ast/helpers/common.rs b/src/interpreter/ast/helpers/common.rs index 512fc17..79cd50f 100644 --- a/src/interpreter/ast/helpers/common.rs +++ b/src/interpreter/ast/helpers/common.rs @@ -361,16 +361,28 @@ mod tests { #[test] fn precedence_handles_correctly() { - let result = compare_precedence(&SelectableStackElement::MathOperator(MathOperator::Multiply), &SelectableStackElement::MathOperator(MathOperator::Add)); + let result = compare_precedence( + &SelectableStackElement::MathOperator(MathOperator::Multiply), + &SelectableStackElement::MathOperator(MathOperator::Add), + ); assert!(result.is_ok()); assert_eq!(Ordering::Greater, result.unwrap()); - let result = compare_precedence(&SelectableStackElement::MathOperator(MathOperator::Add), &SelectableStackElement::MathOperator(MathOperator::Multiply)); + let result = compare_precedence( + &SelectableStackElement::MathOperator(MathOperator::Add), + &SelectableStackElement::MathOperator(MathOperator::Multiply), + ); assert!(result.is_ok()); assert_eq!(Ordering::Less, result.unwrap()); - let result = compare_precedence(&SelectableStackElement::LogicalOperator(LogicalOperator::And), &SelectableStackElement::LogicalOperator(LogicalOperator::Or)); + let result = compare_precedence( + &SelectableStackElement::LogicalOperator(LogicalOperator::And), + &SelectableStackElement::LogicalOperator(LogicalOperator::Or), + ); assert!(result.is_ok()); assert_eq!(Ordering::Greater, result.unwrap()); - let result = compare_precedence(&SelectableStackElement::LogicalOperator(LogicalOperator::Not), &SelectableStackElement::LogicalOperator(LogicalOperator::And)); + let result = compare_precedence( + &SelectableStackElement::LogicalOperator(LogicalOperator::Not), + &SelectableStackElement::LogicalOperator(LogicalOperator::And), + ); assert!(result.is_ok()); assert_eq!(Ordering::Greater, result.unwrap()); } diff --git a/tests/crud_test.rs b/tests/crud_test.rs index 196e8ca..457c4e2 100644 --- a/tests/crud_test.rs +++ b/tests/crud_test.rs @@ -279,4 +279,4 @@ fn test_select_clauses_with_literals() { ])), ]; assert_eq!(expected, result); -} \ No newline at end of file +}