From 11a199db375809a13549bfbd33ef5e65b4e827ed Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Fri, 5 Sep 2025 14:46:07 -0400 Subject: [PATCH 1/3] Improve Where Condition to accept any operand on both sides --- src/cli/ast/helpers/where_stack.rs | 159 ++++++++++++++++++++--------- 1 file changed, 112 insertions(+), 47 deletions(-) diff --git a/src/cli/ast/helpers/where_stack.rs b/src/cli/ast/helpers/where_stack.rs index f16c94a..50f7fae 100644 --- a/src/cli/ast/helpers/where_stack.rs +++ b/src/cli/ast/helpers/where_stack.rs @@ -133,6 +133,7 @@ pub fn get_where_clause(parser: &mut Parser) -> Result Result, String> { let token = parser.current_token()?; match token.token_type { + // Logical operators and parentheses TokenTypes::And => { parser.advance()?; return Ok(Some(WhereStackElement::LogicalOperator(LogicalOperator::And))) @@ -153,60 +154,74 @@ fn get_where_condition(parser: &mut Parser) -> Result, parser.advance()?; return Ok(Some(WhereStackElement::Parentheses(Parentheses::Right))) }, - TokenTypes::Identifier => { - let column = token.value.to_string(); - parser.advance()?; + // Conditions + TokenTypes::Identifier | TokenTypes::IntLiteral | TokenTypes::RealLiteral | TokenTypes::String | TokenTypes::Blob | TokenTypes::Null => { + return Ok(Some(WhereStackElement::Condition(get_condition(parser)?))); + } + _ => return Ok(None), + } +} + +fn get_condition(parser: &mut Parser) -> Result { + let l_side = get_operand(parser)?; + 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, + TokenTypes::In => Operator::In, + TokenTypes::Not => Operator::NotIn, + _ => return Err(parser.format_error()), + }; + 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, - TokenTypes::In => Operator::In, - TokenTypes::Not => Operator::NotIn, - _ => return Err(parser.format_error()), - }; + if operator == Operator::NotIn || operator == Operator::In { + if operator == Operator::NotIn { + expect_token_type(parser, TokenTypes::In)?; parser.advance()?; + } + let r_side = get_operand(parser)?; + parser.advance()?; - if operator == Operator::NotIn || operator == Operator::In { - if operator == Operator::NotIn { - expect_token_type(parser, TokenTypes::In)?; - parser.advance()?; - } - expect_token_type(parser, TokenTypes::LeftParen)?; - parser.advance()?; + return Ok(WhereCondition { + l_side: l_side, + operator: operator, + r_side: r_side, + }); + } + let r_side = get_operand(parser)?; + parser.advance()?; - let values = tokens_to_value_list(parser)?; - expect_token_type(parser, TokenTypes::RightParen)?; - parser.advance()?; + return Ok(WhereCondition { + l_side: l_side, + operator, + r_side: r_side, + }); +} - return Ok(Some(WhereStackElement::Condition( - WhereCondition { - l_side: Operand::Identifier(column), - operator: operator, - r_side: Operand::ValueList(values), - }) - )); - } - let token = parser.current_token()?; - let r_side = match token.token_type { - TokenTypes::Identifier => Operand::Identifier(token.value.to_string()), - _ => Operand::Value(token_to_value(parser)?) - }; +fn get_operand(parser: &mut Parser) -> Result { + let token = parser.current_token()?; + match token.token_type { + 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::Blob => Ok(Operand::Value(token_to_value(parser)?)), + TokenTypes::Null => Ok(Operand::Value(token_to_value(parser)?)), + TokenTypes::LeftParen => { parser.advance()?; - return Ok(Some(WhereStackElement::Condition( - WhereCondition { - l_side: Operand::Identifier(column), - operator, - r_side: r_side, - }) - )); - } - _ => return Ok(None), + let values = tokens_to_value_list(parser)?; + expect_token_type(parser, TokenTypes::RightParen)?; + + Ok(Operand::ValueList(values)) + }, + _ => return Err(parser.format_error()), } } @@ -590,4 +605,54 @@ mod tests { assert_eq!(expected, where_clause); assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::Limit); } + + #[test] + fn where_stack_handles_reversed_condition() { + // WHERE 1 = id LIMIT... + let tokens = vec![ + token(TokenTypes::Where, "WHERE"), + token(TokenTypes::IntLiteral, "1"), + token(TokenTypes::Equals, "="), + token(TokenTypes::Identifier, "id"), + 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(vec![ + WhereStackElement::Condition(WhereCondition { + l_side: Operand::Value(Value::Integer(1)), + operator: Operator::Equals, + r_side: Operand::Identifier("id".to_string()), + }), + ]); + assert_eq!(expected, where_clause); + assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::SemiColon); + } + + #[test] + fn where_stack_handles_conditions_with_two_literals() { + // WHERE 1 = 2;... + let tokens = vec![ + token(TokenTypes::Where, "WHERE"), + token(TokenTypes::IntLiteral, "1"), + token(TokenTypes::Equals, "="), + token(TokenTypes::IntLiteral, "2"), + 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(vec![ + WhereStackElement::Condition(WhereCondition { + l_side: Operand::Value(Value::Integer(1)), + operator: Operator::Equals, + r_side: Operand::Value(Value::Integer(2)), + }), + ]); + assert_eq!(expected, where_clause); + assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::SemiColon); + } } From 3556079bee1ec1111b218079524e7dc15021e428 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Fri, 5 Sep 2025 15:03:40 -0400 Subject: [PATCH 2/3] Improve abstraction and add some tests for coverage --- src/cli/ast/helpers/common.rs | 20 +++ src/cli/ast/helpers/mod.rs | 1 + src/cli/ast/helpers/where_condition.rs | 200 ++++++++++++++++++++++++ src/cli/ast/helpers/where_stack.rs | 203 +------------------------ 4 files changed, 225 insertions(+), 199 deletions(-) create mode 100644 src/cli/ast/helpers/where_condition.rs diff --git a/src/cli/ast/helpers/common.rs b/src/cli/ast/helpers/common.rs index 6067049..a661c66 100644 --- a/src/cli/ast/helpers/common.rs +++ b/src/cli/ast/helpers/common.rs @@ -76,4 +76,24 @@ pub fn get_table_name(parser: &mut Parser) -> Result { expect_token_type(parser, TokenTypes::Identifier)?; let result = token.value.to_string(); Ok(result) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::ast::test_utils::token; + use crate::cli::ast::parser::Parser; + use crate::cli::tokenizer::token::TokenTypes; + + #[test] + fn value_list_handles_single_value() { + // 1);... + let tokens = vec![ + token(TokenTypes::IntLiteral, "1"), + token(TokenTypes::RightParen, ")"), + ]; + let mut parser = Parser::new(tokens); + let result = tokens_to_value_list(&mut parser); + assert_eq!(result, Ok(vec![Value::Integer(1)])); + } } \ No newline at end of file diff --git a/src/cli/ast/helpers/mod.rs b/src/cli/ast/helpers/mod.rs index 434608c..27c6bf6 100644 --- a/src/cli/ast/helpers/mod.rs +++ b/src/cli/ast/helpers/mod.rs @@ -1,4 +1,5 @@ pub mod where_stack; +pub mod where_condition; pub mod order_by_clause; pub mod limit_clause; pub mod common; \ No newline at end of file diff --git a/src/cli/ast/helpers/where_condition.rs b/src/cli/ast/helpers/where_condition.rs new file mode 100644 index 0000000..d77b13f --- /dev/null +++ b/src/cli/ast/helpers/where_condition.rs @@ -0,0 +1,200 @@ +use crate::cli::ast::{parser::Parser, WhereCondition, Operand, Operator}; +use crate::cli::tokenizer::token::TokenTypes; +use crate::cli::ast::helpers::common::{expect_token_type, token_to_value, tokens_to_value_list}; + + +pub fn get_condition(parser: &mut Parser) -> Result { + let l_side = get_operand(parser)?; + 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, + TokenTypes::In => Operator::In, + TokenTypes::Not => Operator::NotIn, + _ => return Err(parser.format_error()), + }; + parser.advance()?; + + if operator == Operator::NotIn || operator == Operator::In { + if operator == Operator::NotIn { + expect_token_type(parser, TokenTypes::In)?; + parser.advance()?; + } + let r_side = get_operand(parser)?; + parser.advance()?; + + return Ok(WhereCondition { + l_side: l_side, + operator: operator, + r_side: r_side, + }); + } + let r_side = get_operand(parser)?; + parser.advance()?; + + return Ok(WhereCondition { + l_side: l_side, + operator, + r_side: r_side, + }); +} + +pub fn get_operand(parser: &mut Parser) -> Result { + let token = parser.current_token()?; + match token.token_type { + 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::Blob => Ok(Operand::Value(token_to_value(parser)?)), + TokenTypes::Null => Ok(Operand::Value(token_to_value(parser)?)), + TokenTypes::LeftParen => { + parser.advance()?; + + let values = tokens_to_value_list(parser)?; + expect_token_type(parser, TokenTypes::RightParen)?; + + Ok(Operand::ValueList(values)) + }, + _ => return Err(parser.format_error()), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::ast::{Operator, WhereCondition, Operand}; + use crate::cli::ast::test_utils::token; + use crate::db::table::Value; + + fn assert_where_condition(result: Result, expected: WhereCondition, parser: &mut Parser) { + assert!(result.is_ok()); + let where_clause = result.unwrap(); + assert_eq!(expected, where_clause); + assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::SemiColon); + } + + #[test] + fn parses_simple_equality_condition() { + // id > 1;... + let tokens = vec![ + token(TokenTypes::Identifier, "id"), + token(TokenTypes::GreaterThan, ">"), + token(TokenTypes::IntLiteral, "1"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = get_condition(&mut parser); + let expected = WhereCondition { + l_side: Operand::Identifier("id".to_string()), + operator: Operator::GreaterThan, + r_side: Operand::Value(Value::Integer(1)), + }; + assert_where_condition(result, expected, &mut parser); + } + + #[test] + fn parses_in_operator_with_value_list() { + // id IN (1, 2, 3);... + let tokens = vec![ + token(TokenTypes::Identifier, "id"), + token(TokenTypes::In, "IN"), + token(TokenTypes::LeftParen, "("), + token(TokenTypes::IntLiteral, "1"), + token(TokenTypes::Comma, ","), + token(TokenTypes::IntLiteral, "2"), + token(TokenTypes::Comma, ","), + token(TokenTypes::IntLiteral, "3"), + token(TokenTypes::RightParen, ")"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = get_condition(&mut parser); + let expected = WhereCondition { + l_side: Operand::Identifier("id".to_string()), + operator: Operator::In, + r_side: Operand::ValueList(vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)]), + }; + assert_where_condition(result, expected, &mut parser); + } + + #[test] + fn parses_column_to_column_comparison() { + // id = name;... + let tokens = vec![ + token(TokenTypes::Identifier, "id"), + token(TokenTypes::Equals, "="), + token(TokenTypes::Identifier, "name"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = get_condition(&mut parser); + let expected = WhereCondition { + l_side: Operand::Identifier("id".to_string()), + operator: Operator::Equals, + r_side: Operand::Identifier("name".to_string()), + }; + assert_where_condition(result, expected, &mut parser); + } + + #[test] + fn where_stack_handles_reversed_condition() { + // "fletcher" < id;... + let tokens = vec![ + token(TokenTypes::String, "fletcher"), + token(TokenTypes::LessThan, "<"), + token(TokenTypes::Identifier, "id"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = get_condition(&mut parser); + let expected = WhereCondition { + l_side: Operand::Value(Value::Text("fletcher".to_string())), + operator: Operator::LessThan, + r_side: Operand::Identifier("id".to_string()), + }; + assert_where_condition(result, expected, &mut parser); + } + + #[test] + fn where_stack_handles_conditions_with_two_literals() { + // 1.1 != 2.2;... + let tokens = vec![ + token(TokenTypes::RealLiteral, "1.1"), + token(TokenTypes::NotEquals, "!="), + token(TokenTypes::RealLiteral, "2.2"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = get_condition(&mut parser); + let expected = WhereCondition { + l_side: Operand::Value(Value::Real(1.1)), + operator: Operator::NotEquals, + r_side: Operand::Value(Value::Real(2.2)), + }; + assert_where_condition(result, expected, &mut parser); + } + + #[test] + fn where_condition_handles_invalid_not_in_statement() { + // id NOT (1);... + let tokens = vec![ + token(TokenTypes::Identifier, "id"), + token(TokenTypes::Not, "NOT"), + token(TokenTypes::LeftParen, "("), + token(TokenTypes::IntLiteral, "1"), + token(TokenTypes::RightParen, ")"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = get_condition(&mut parser); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "Error at line 1, column 0: Unexpected value: ("); + } +} \ No newline at end of file diff --git a/src/cli/ast/helpers/where_stack.rs b/src/cli/ast/helpers/where_stack.rs index 50f7fae..cc25817 100644 --- a/src/cli/ast/helpers/where_stack.rs +++ b/src/cli/ast/helpers/where_stack.rs @@ -1,5 +1,6 @@ use crate::cli::{ast::{ - helpers::common::{expect_token_type, token_to_value, tokens_to_value_list}, parser::Parser, LogicalOperator, Operand, Operator, Parentheses, WhereCondition, WhereStackElement, WhereStackOperators}}; + helpers::{common::expect_token_type, where_condition::get_condition}, + parser::Parser, LogicalOperator, WhereStackElement, WhereStackOperators, Parentheses}}; use crate::cli::tokenizer::token::TokenTypes; // The WhereStack is a the method that is used to store the order of operations with Reverse Polish Notation. @@ -162,73 +163,10 @@ fn get_where_condition(parser: &mut Parser) -> Result, } } -fn get_condition(parser: &mut Parser) -> Result { - let l_side = get_operand(parser)?; - 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, - TokenTypes::In => Operator::In, - TokenTypes::Not => Operator::NotIn, - _ => return Err(parser.format_error()), - }; - parser.advance()?; - - if operator == Operator::NotIn || operator == Operator::In { - if operator == Operator::NotIn { - expect_token_type(parser, TokenTypes::In)?; - parser.advance()?; - } - let r_side = get_operand(parser)?; - parser.advance()?; - - return Ok(WhereCondition { - l_side: l_side, - operator: operator, - r_side: r_side, - }); - } - let r_side = get_operand(parser)?; - parser.advance()?; - - return Ok(WhereCondition { - l_side: l_side, - operator, - r_side: r_side, - }); -} - -fn get_operand(parser: &mut Parser) -> Result { - let token = parser.current_token()?; - match token.token_type { - 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::Blob => Ok(Operand::Value(token_to_value(parser)?)), - TokenTypes::Null => Ok(Operand::Value(token_to_value(parser)?)), - TokenTypes::LeftParen => { - parser.advance()?; - - let values = tokens_to_value_list(parser)?; - expect_token_type(parser, TokenTypes::RightParen)?; - - Ok(Operand::ValueList(values)) - }, - _ => return Err(parser.format_error()), - } -} - #[cfg(test)] mod tests { use super::*; - use crate::cli::ast::LogicalOperator; + use crate::cli::ast::{LogicalOperator, Operator, WhereCondition, Operand}; use crate::cli::ast::test_utils::token; use crate::db::table::Value; @@ -240,27 +178,6 @@ mod tests { }) } - #[test] - fn parses_simple_equality_condition() { - // WHERE id = 1 LIMIT... - let tokens = vec![ - token(TokenTypes::Where, "WHERE"), - token(TokenTypes::Identifier, "id"), - token(TokenTypes::Equals, "="), - token(TokenTypes::IntLiteral, "1"), - token(TokenTypes::Limit, "LIMIT"), - ]; - 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(vec![ - simple_condition("id", Operator::Equals, Value::Integer(1)), - ]); - assert_eq!(expected, where_clause); - assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::Limit); - } - #[test] fn returns_none_when_no_where_keyword_present() { // SELECT * ... (no WHERE clause) @@ -527,11 +444,8 @@ mod tests { } #[test] - /// Tests error handling when NOT is followed by an invalid token (AND). - /// Should return an error since NOT must be followed by a condition - /// or opening parenthesis, not another logical operator. fn returns_error_for_invalid_not_operator_usage() { - // SQL: WHERE NOT AND id = 1; (invalid: NOT followed by AND) + // WHERE NOT AND id = 1; (invalid: NOT followed by AND) let tokens = vec![ token(TokenTypes::Where, "WHERE"), token(TokenTypes::Not, "NOT"), @@ -546,113 +460,4 @@ mod tests { assert!(result.is_err()); assert_eq!(result.unwrap_err(), "Error near line 1, column 0"); } - - #[test] - /// Tests parsing of IN operator with a list of values. - /// Verifies that the value list is correctly parsed and stored - /// as an Operand::ValueList in the condition. - fn parses_in_operator_with_value_list() { - // SQL: WHERE id IN (1, 2, 3); - let tokens = vec![ - token(TokenTypes::Where, "WHERE"), - token(TokenTypes::Identifier, "id"), - token(TokenTypes::In, "IN"), - token(TokenTypes::LeftParen, "("), - token(TokenTypes::IntLiteral, "1"), - token(TokenTypes::Comma, ","), - token(TokenTypes::IntLiteral, "2"), - token(TokenTypes::Comma, ","), - token(TokenTypes::IntLiteral, "3"), - token(TokenTypes::RightParen, ")"), - 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(); - assert_eq!(where_clause, Some(vec![ - WhereStackElement::Condition(WhereCondition { - l_side: Operand::Identifier("id".to_string()), - operator: Operator::In, - r_side: Operand::ValueList(vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)]), - }), - ])); - assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::SemiColon); - } - - #[test] - /// Tests parsing WHERE clause comparing two columns instead of column to value. - /// Verifies that both operands are correctly identified as column references - /// (Operand::Identifier) rather than literal values. - fn parses_column_to_column_comparison() { - // SQL: WHERE id = name LIMIT... - let tokens = vec![ - token(TokenTypes::Where, "WHERE"), - token(TokenTypes::Identifier, "id"), - token(TokenTypes::Equals, "="), - token(TokenTypes::Identifier, "name"), - token(TokenTypes::Limit, "LIMIT"), - ]; - 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(vec![WhereStackElement::Condition(WhereCondition { - l_side: Operand::Identifier("id".to_string()), - operator: Operator::Equals, - r_side: Operand::Identifier("name".to_string()), - })]); - assert_eq!(expected, where_clause); - assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::Limit); - } - - #[test] - fn where_stack_handles_reversed_condition() { - // WHERE 1 = id LIMIT... - let tokens = vec![ - token(TokenTypes::Where, "WHERE"), - token(TokenTypes::IntLiteral, "1"), - token(TokenTypes::Equals, "="), - token(TokenTypes::Identifier, "id"), - 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(vec![ - WhereStackElement::Condition(WhereCondition { - l_side: Operand::Value(Value::Integer(1)), - operator: Operator::Equals, - r_side: Operand::Identifier("id".to_string()), - }), - ]); - assert_eq!(expected, where_clause); - assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::SemiColon); - } - - #[test] - fn where_stack_handles_conditions_with_two_literals() { - // WHERE 1 = 2;... - let tokens = vec![ - token(TokenTypes::Where, "WHERE"), - token(TokenTypes::IntLiteral, "1"), - token(TokenTypes::Equals, "="), - token(TokenTypes::IntLiteral, "2"), - 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(vec![ - WhereStackElement::Condition(WhereCondition { - l_side: Operand::Value(Value::Integer(1)), - operator: Operator::Equals, - r_side: Operand::Value(Value::Integer(2)), - }), - ]); - assert_eq!(expected, where_clause); - assert_eq!(parser.current_token().unwrap().token_type, TokenTypes::SemiColon); - } } From 38033c3430782d1007da5e081c063d20eb2c7f82 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Fri, 5 Sep 2025 15:05:14 -0400 Subject: [PATCH 3/3] Cover NOT IN case with a test --- src/cli/ast/helpers/where_condition.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/cli/ast/helpers/where_condition.rs b/src/cli/ast/helpers/where_condition.rs index d77b13f..3b68622 100644 --- a/src/cli/ast/helpers/where_condition.rs +++ b/src/cli/ast/helpers/where_condition.rs @@ -124,6 +124,28 @@ mod tests { assert_where_condition(result, expected, &mut parser); } + #[test] + fn parses_not_in_operator_with_value_list() { + // id NOT IN (1);... + let tokens = vec![ + token(TokenTypes::Identifier, "id"), + token(TokenTypes::Not, "NOT"), + token(TokenTypes::In, "IN"), + token(TokenTypes::LeftParen, "("), + token(TokenTypes::IntLiteral, "1"), + token(TokenTypes::RightParen, ")"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = get_condition(&mut parser); + let expected = WhereCondition { + l_side: Operand::Identifier("id".to_string()), + operator: Operator::NotIn, + r_side: Operand::ValueList(vec![Value::Integer(1)]), + }; + assert_where_condition(result, expected, &mut parser); + } + #[test] fn parses_column_to_column_comparison() { // id = name;... @@ -183,9 +205,9 @@ mod tests { #[test] fn where_condition_handles_invalid_not_in_statement() { - // id NOT (1);... + // X'00' NOT (1);... let tokens = vec![ - token(TokenTypes::Identifier, "id"), + token(TokenTypes::Blob, "00"), token(TokenTypes::Not, "NOT"), token(TokenTypes::LeftParen, "("), token(TokenTypes::IntLiteral, "1"),