diff --git a/src/cli/ast/helpers/where_condition.rs b/src/cli/ast/helpers/where_condition.rs index 3b68622..e03e3e8 100644 --- a/src/cli/ast/helpers/where_condition.rs +++ b/src/cli/ast/helpers/where_condition.rs @@ -8,7 +8,7 @@ pub fn get_condition(parser: &mut Parser) -> Result { parser.advance()?; let token = parser.current_token()?; - let operator = match token.token_type { + let mut operator = match token.token_type { TokenTypes::Equals => Operator::Equals, TokenTypes::NotEquals => Operator::NotEquals, TokenTypes::LessThan => Operator::LessThan, @@ -17,6 +17,7 @@ pub fn get_condition(parser: &mut Parser) -> Result { TokenTypes::GreaterEquals => Operator::GreaterEquals, TokenTypes::In => Operator::In, TokenTypes::Not => Operator::NotIn, + TokenTypes::Is => Operator::Is, _ => return Err(parser.format_error()), }; parser.advance()?; @@ -35,6 +36,12 @@ pub fn get_condition(parser: &mut Parser) -> Result { r_side: r_side, }); } + + if operator == Operator::Is && parser.current_token()?.token_type == TokenTypes::Not { + operator = Operator::IsNot; + parser.advance()?; + } + let r_side = get_operand(parser)?; parser.advance()?; @@ -219,4 +226,43 @@ mod tests { assert!(result.is_err()); assert_eq!(result.unwrap_err(), "Error at line 1, column 0: Unexpected value: ("); } + + #[test] + fn where_condition_handles_is_operator() { + // id IS NULL;... + let tokens = vec![ + token(TokenTypes::Identifier, "id"), + token(TokenTypes::Is, "IS"), + token(TokenTypes::Null, "NULL"), + 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::Is, + r_side: Operand::Value(Value::Null), + }; + assert_where_condition(result, expected, &mut parser); + } + + #[test] + fn where_condition_handles_is_not_operator() { + // id IS NOT name;... + let tokens = vec![ + token(TokenTypes::Identifier, "id"), + token(TokenTypes::Is, "IS"), + token(TokenTypes::Not, "NOT"), + token(TokenTypes::Null, "NULL"), + 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::IsNot, + r_side: Operand::Value(Value::Null), + }; + assert_where_condition(result, expected, &mut parser); + } } \ No newline at end of file diff --git a/src/cli/ast/mod.rs b/src/cli/ast/mod.rs index ee87e89..168a845 100644 --- a/src/cli/ast/mod.rs +++ b/src/cli/ast/mod.rs @@ -88,6 +88,8 @@ pub enum Operator { GreaterEquals, In, NotIn, + Is, + IsNot, } #[derive(Debug, PartialEq)] diff --git a/src/cli/tokenizer/scanner.rs b/src/cli/tokenizer/scanner.rs index 55a6e74..1e3b22a 100644 --- a/src/cli/tokenizer/scanner.rs +++ b/src/cli/tokenizer/scanner.rs @@ -168,6 +168,7 @@ impl<'a> Scanner<'a> { slice if slice.eq_ignore_ascii_case("THEN") => TokenTypes::Then, slice if slice.eq_ignore_ascii_case("ELSE") => TokenTypes::Else, slice if slice.eq_ignore_ascii_case("END") => TokenTypes::End, + slice if slice.eq_ignore_ascii_case("IS") => TokenTypes::Is, slice if slice.eq_ignore_ascii_case("COUNT") => TokenTypes::Count, slice if slice.eq_ignore_ascii_case("SUM") => TokenTypes::Sum, slice if slice.eq_ignore_ascii_case("AVG") => TokenTypes::Avg, diff --git a/src/cli/tokenizer/token.rs b/src/cli/tokenizer/token.rs index 42b0bb5..517b11f 100644 --- a/src/cli/tokenizer/token.rs +++ b/src/cli/tokenizer/token.rs @@ -13,7 +13,7 @@ pub enum TokenTypes { Limit, Offset, // Logical Operators And, Or, In, Exists, - Case, When, Then, Else, End, + Case, When, Then, Else, End, Is, Equals, NotEquals, LessThan, LessEquals, GreaterThan, GreaterEquals, // Aggregate Functions Count, Sum, Avg, Min, Max, diff --git a/src/db/table/select/mod.rs b/src/db/table/select/mod.rs index 50015eb..e4dd3cf 100644 --- a/src/db/table/select/mod.rs +++ b/src/db/table/select/mod.rs @@ -1,4 +1,5 @@ pub mod where_stack; +pub mod where_condition; pub mod limit_clause; pub mod order_by_clause; use crate::db::table::{Table, Value}; diff --git a/src/db/table/select/where_condition.rs b/src/db/table/select/where_condition.rs new file mode 100644 index 0000000..9ce0302 --- /dev/null +++ b/src/db/table/select/where_condition.rs @@ -0,0 +1,301 @@ +use crate::db::table::{Table, Value}; +use crate::cli::ast::{Operator, Operand, WhereCondition}; +use crate::db::table::DataType; + + +// This file holds the logic for whether a row matches a where condition. +pub fn matches_where_clause(table: &Table, row: &Vec, where_clause: &WhereCondition) -> Result { + let l_side = operand_to_value(table, row, &where_clause.l_side)?; + match where_clause.operator { + Operator::In | Operator::NotIn => { + let r_side = match &where_clause.r_side { + Operand::ValueList(value_list) => value_list, + _ => return Err(format!("Found invalid r_side operand: {:?}", where_clause.r_side)), + }; + if r_side.is_empty() { + return Ok(false); + } + let result = r_side.contains(l_side); + if where_clause.operator == Operator::NotIn { + return Ok(!result); + } + return Ok(result); + }, + Operator::Is | Operator::IsNot => { + let r_side = operand_to_value(table, row, &where_clause.r_side)?; + expect_same_type(l_side, r_side)?; + + match (l_side, r_side, &where_clause.operator) { + (Value::Null, Value::Null, Operator::Is) => return Ok(true), + (Value::Null, Value::Null, Operator::IsNot) => return Ok(false), + (Value::Null, _, Operator::Is) | (_, Value::Null, Operator::Is) => return Ok(false), + (Value::Null, _, Operator::IsNot) | (_, Value::Null, Operator::IsNot) => return Ok(true), + (_, _, Operator::Is) => return Ok(l_side == r_side), + (_, _, Operator::IsNot) => return Ok(l_side != r_side), + _ => unreachable!(), + } + }, + _ => {}, + } + + let r_side = operand_to_value(table, row, &where_clause.r_side)?; + if l_side.get_type() == DataType::Null || r_side.get_type() == DataType::Null { + return Ok(false); + } + + expect_same_type(l_side, r_side)?; + + match where_clause.operator { + Operator::Equals => { + return Ok(*l_side == *r_side); + }, + Operator::NotEquals => { + return Ok(*l_side != *r_side); + }, + _ => { + match l_side.get_type() { + DataType::Integer | DataType::Real | DataType::Text => { + match where_clause.operator { + Operator::LessThan => { + return Ok(*l_side < *r_side); + }, + Operator::GreaterThan => { + return Ok(*l_side > *r_side); + }, + Operator::LessEquals => { + return Ok(*l_side <= *r_side); + }, + Operator::GreaterEquals => { + return Ok(*l_side >= *r_side); + }, + _ => { + return Err(format!("Found invalid operator: {:?}", where_clause.operator)); + }, + } + }, + _ => { + return Err(format!("Found invalid operator: {:?} for data type: {:?}", where_clause.operator, l_side.get_type())); + }, + } + } + } +} + +fn operand_to_value<'a>(table: &'a Table, row: &'a Vec, operand: &'a Operand) -> Result<&'a Value, String> { + match operand { + Operand::Value(value) => Ok(value), + Operand::Identifier(column) => { + if !table.has_column(column) { + return Err(format!("Column {} does not exist in table {}", column, table.name)); + } + Ok(table.get_column_from_row(row, column)) + }, + _ => Err(format!("Found invalid operand: {:?}", operand)), + } +} + +fn expect_same_type(l_side: &Value, r_side: &Value) -> Result<(), String> { + if l_side.get_type() != r_side.get_type() && l_side.get_type() != DataType::Null && r_side.get_type() != DataType::Null { + return Err(format!("Found different data types for l_side and r_side: {:?} and {:?}", l_side.get_type(), r_side.get_type())); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::db::table::{Table, Value, DataType, ColumnDefinition}; + use crate::cli::ast::{Operator, Operand, WhereCondition}; + + #[test] + fn matches_where_clause_returns_true_if_row_matches_where_clause() { + let table = Table::new("users".to_string(), vec![ + ColumnDefinition { + name:"id".to_string(), + data_type:DataType::Integer, + constraints: vec![] + }, + ]); + let row = vec![Value::Integer(1)]; + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(1))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + } + + #[test] + fn matches_where_clause_returns_false_if_row_does_not_match_where_clause() { + let table = Table::new("users".to_string(), vec![ + ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] }, + ]); + let row = vec![Value::Integer(2)]; + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(1))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && !result.unwrap()); + } + + #[test] + fn matches_where_clause_handles_different_data_types() { + let table = Table::new("users".to_string(), vec![ + ColumnDefinition { + name:"id".to_string(), + data_type:DataType::Integer, + constraints: vec![] + }, + ]); + let row = vec![Value::Integer(1)]; + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Text("Fletcher".to_string()))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_err()); + let expected_error = "Found different data types for l_side and r_side: Integer and Text"; + assert_eq!(expected_error, result.err().unwrap()); + } + + #[test] + fn matches_where_clause_handles_different_operators() { + let table = Table::new("users".to_string(), vec![ + ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] }, + ]); + let row = vec![Value::Integer(10)]; + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::GreaterThan,r_side: Operand::Value(Value::Integer(0))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::GreaterEquals,r_side: Operand::Value(Value::Integer(0))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::LessThan,r_side: Operand::Value(Value::Integer(20))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::LessEquals,r_side: Operand::Value(Value::Integer(20))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::NotEquals,r_side: Operand::Value(Value::Integer(10))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && !result.unwrap()); + } + + #[test] + fn matches_where_clause_handles_string_comparison() { + let table = Table::new("users".to_string(), vec![ + ColumnDefinition {name:"name".to_string(),data_type:DataType::Text, constraints: vec![] }, + ]); + let row = vec![Value::Text("lop".to_string())]; + let where_clause = WhereCondition {l_side: Operand::Identifier("name".to_string()),operator:Operator::GreaterEquals,r_side: Operand::Value(Value::Text("abc".to_string()))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("name".to_string()),operator:Operator::LessEquals,r_side: Operand::Value(Value::Text("lop".to_string()))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("name".to_string()),operator:Operator::GreaterThan,r_side: Operand::Value(Value::Text("xyz".to_string()))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && !result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("name".to_string()),operator:Operator::LessThan,r_side: Operand::Value(Value::Text("abc".to_string()))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && !result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("name".to_string()),operator:Operator::NotEquals,r_side: Operand::Value(Value::Text("abc".to_string()))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("name".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Text("lop".to_string()))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + } + + #[test] + fn matches_where_clause_handles_null() { + let table = Table::new("users".to_string(), vec![ + ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] }, + ]); + let row = vec![Value::Null]; + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::GreaterEquals,r_side: Operand::Value(Value::Integer(1))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && !result.unwrap()); + } + + #[test] + fn matches_where_clause_handles_invalid_operator_for_data_type() { + let table = Table::new("users".to_string(), vec![ + ColumnDefinition {name:"id".to_string(),data_type:DataType::Blob, constraints: vec![] }, + ]); + let row = vec![Value::Blob(vec![1, 2, 3])]; + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::GreaterEquals,r_side: Operand::Value(Value::Blob(vec![1, 2, 3]))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_err()); + let expected_error = "Found invalid operator: GreaterEquals for data type: Blob"; + assert_eq!(expected_error, result.err().unwrap()); + } + + #[test] + fn matches_where_clause_handles_null_equality() { + let table = Table::new("users".to_string(), vec![ + ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] }, + ]); + let row = vec![Value::Null]; + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Null)}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && !result.unwrap()); + } + + #[test] + fn matches_where_clause_handles_single_null_equality() { + let table = Table::new("users".to_string(), vec![ + ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] }, + ]); + let row = vec![Value::Null]; + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(1))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && !result.unwrap()); + } + + #[test] + fn matches_where_clause_handles_is_and_is_not_operators() { + let table = Table::new("users".to_string(), vec![ + ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] }, + ]); + let row = vec![Value::Integer(1)]; + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Is,r_side: Operand::Value(Value::Null)}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && !result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::IsNot,r_side: Operand::Value(Value::Null)}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Is,r_side: Operand::Value(Value::Integer(1))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::IsNot,r_side: Operand::Value(Value::Integer(1))}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && !result.unwrap()); + } + + #[test] + fn matches_where_clause_handles_in_and_not_in_operators() { + let table = Table::new("users".to_string(), vec![ + ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] }, + ]); + let row = vec![Value::Integer(1)]; + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::In,r_side: Operand::ValueList(vec![Value::Integer(1)])}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::NotIn,r_side: Operand::ValueList(vec![Value::Integer(1)])}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && !result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::In,r_side: Operand::ValueList(vec![Value::Integer(2), Value::Integer(3)])}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && !result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::NotIn,r_side: Operand::ValueList(vec![Value::Integer(2), Value::Integer(3)])}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + } + + #[test] + fn matches_where_clause_handles_in_with_diff_data_types() { + let table = Table::new("users".to_string(), vec![ + ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] }, + ]); + let row = vec![Value::Text("hello".to_string())]; + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::In,r_side: Operand::ValueList(vec![Value::Integer(2), Value::Text("hello".to_string())])}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && result.unwrap()); + let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::NotIn,r_side: Operand::ValueList(vec![Value::Integer(2), Value::Text("hello".to_string())])}; + let result = matches_where_clause(&table, &row, &where_clause); + assert!(result.is_ok() && !result.unwrap()); + } +} \ No newline at end of file diff --git a/src/db/table/select/where_stack.rs b/src/db/table/select/where_stack.rs index 060d4a0..70acc36 100644 --- a/src/db/table/select/where_stack.rs +++ b/src/db/table/select/where_stack.rs @@ -1,197 +1,14 @@ -use crate::cli::ast::{Operator, WhereCondition, Operand, WhereStackElement}; -use crate::db::table::{Table, Value, DataType}; - -// For now this function only supports one column = value where clause +use crate::cli::ast::{WhereStackElement}; +use crate::db::table::{Table, Value}; +use crate::db::table::select::where_condition::matches_where_clause; +// This file holds the logic for whether a row matches a where stack which is a vec of WhereConditions +// and logical operators stored in Reverse Polish Notation. pub fn matches_where_stack(table: &Table, row: &Vec, where_stack: &Vec) -> Result { let where_condition = match where_stack.first() { Some(WhereStackElement::Condition(where_condition)) => where_condition, _ => return Err(format!("Found nothing when expected edge")), }; - if let Operand::Identifier(column_name) = &where_condition.l_side { - if !table.has_column(column_name) { - return Err(format!("Column {} does not exist in table {}", column_name, table.name)); - } - } - matches_where_clause(table, row, where_condition) -} - -pub fn matches_where_clause(table: &Table, row: &Vec, where_clause: &WhereCondition) -> Result { - let l_side = match &where_clause.l_side { - Operand::Identifier(column) => column, - _ => return Err(format!("Found invalid left side of condition: {:?}", where_clause.l_side)), - }; - let r_side = match &where_clause.r_side { - Operand::Value(value) => value, - _ => return Err(format!("Found invalid right side of condition: {:?}", where_clause.r_side)), - }; - let column_value = table.get_column_from_row(row, &l_side); - if column_value.get_type() == DataType::Null && r_side.get_type() == DataType::Null { - return Ok(true); - } - else if column_value.get_type() == DataType::Null || r_side.get_type() == DataType::Null { - return Ok(false); - } - if column_value.get_type() != r_side.get_type() { - return Err(format!("Found different data types for column and value: {:?} and {:?}", column_value.get_type(), r_side.get_type())); - } - - match where_clause.operator { - Operator::Equals => { - return Ok(*column_value == *r_side); - }, - Operator::NotEquals => { - return Ok(*column_value != *r_side); - }, - _ => { - match column_value.get_type() { - DataType::Integer | DataType::Real | DataType::Text => { - match where_clause.operator { - Operator::LessThan => { - return Ok(*column_value < *r_side); - }, - Operator::GreaterThan => { - return Ok(*column_value > *r_side); - }, - Operator::LessEquals => { - return Ok(*column_value <= *r_side); - }, - Operator::GreaterEquals => { - return Ok(*column_value >= *r_side); - }, - _ => { - return Err(format!("Found invalid operator: {:?}", where_clause.operator)); - }, - } - }, - _ => { - return Err(format!("Found invalid operator: {:?} for data type: {:?}", where_clause.operator, column_value.get_type())); - }, - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::db::table::{Table, Value, DataType, ColumnDefinition}; - - #[test] - fn matches_where_clause_returns_true_if_row_matches_where_clause() { - let table = Table::new("users".to_string(), vec![ - ColumnDefinition { - name:"id".to_string(), - data_type:DataType::Integer, - constraints: vec![] - }, - ]); - let row = vec![Value::Integer(1)]; - let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(1))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && result.unwrap()); - } - - #[test] - fn matches_where_clause_returns_false_if_row_does_not_match_where_clause() { - let table = Table::new("users".to_string(), vec![ - ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] }, - ]); - let row = vec![Value::Integer(2)]; - let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(1))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && !result.unwrap()); - } - - #[test] - fn matches_where_clause_handles_different_data_types() { - let table = Table::new("users".to_string(), vec![ - ColumnDefinition { - name:"id".to_string(), - data_type:DataType::Integer, - constraints: vec![] - }, - ]); - let row = vec![Value::Integer(1)]; - let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Text("Fletcher".to_string()))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_err()); - let expected_error = "Found different data types for column and value: Integer and Text"; - assert_eq!(expected_error, result.err().unwrap()); - } - - #[test] - fn matches_where_clause_handles_different_operators() { - let table = Table::new("users".to_string(), vec![ - ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] }, - ]); - let row = vec![Value::Integer(10)]; - let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::GreaterThan,r_side: Operand::Value(Value::Integer(0))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && result.unwrap()); - let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::GreaterEquals,r_side: Operand::Value(Value::Integer(0))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && result.unwrap()); - let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::LessThan,r_side: Operand::Value(Value::Integer(20))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && result.unwrap()); - let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::LessEquals,r_side: Operand::Value(Value::Integer(20))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && result.unwrap()); - let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::NotEquals,r_side: Operand::Value(Value::Integer(10))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && !result.unwrap()); - } - - #[test] - fn matches_where_clause_handles_string_comparison() { - let table = Table::new("users".to_string(), vec![ - ColumnDefinition {name:"name".to_string(),data_type:DataType::Text, constraints: vec![] }, - ]); - let row = vec![Value::Text("lop".to_string())]; - let where_clause = WhereCondition {l_side: Operand::Identifier("name".to_string()),operator:Operator::GreaterEquals,r_side: Operand::Value(Value::Text("abc".to_string()))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && result.unwrap()); - let where_clause = WhereCondition {l_side: Operand::Identifier("name".to_string()),operator:Operator::LessEquals,r_side: Operand::Value(Value::Text("lop".to_string()))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && result.unwrap()); - let where_clause = WhereCondition {l_side: Operand::Identifier("name".to_string()),operator:Operator::GreaterThan,r_side: Operand::Value(Value::Text("xyz".to_string()))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && !result.unwrap()); - let where_clause = WhereCondition {l_side: Operand::Identifier("name".to_string()),operator:Operator::LessThan,r_side: Operand::Value(Value::Text("abc".to_string()))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && !result.unwrap()); - let where_clause = WhereCondition {l_side: Operand::Identifier("name".to_string()),operator:Operator::NotEquals,r_side: Operand::Value(Value::Text("abc".to_string()))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && result.unwrap()); - let where_clause = WhereCondition {l_side: Operand::Identifier("name".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Text("lop".to_string()))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && result.unwrap()); - } - - #[test] - fn matches_where_clause_handles_null() { - let table = Table::new("users".to_string(), vec![ - ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] }, - ]); - let row = vec![Value::Null]; - let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::GreaterEquals,r_side: Operand::Value(Value::Integer(1))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_ok() && !result.unwrap()); - } - - #[test] - fn matches_where_clause_handles_invalid_operator_for_data_type() { - let table = Table::new("users".to_string(), vec![ - ColumnDefinition {name:"id".to_string(),data_type:DataType::Blob, constraints: vec![] }, - ]); - let row = vec![Value::Blob(vec![1, 2, 3])]; - let where_clause = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::GreaterEquals,r_side: Operand::Value(Value::Blob(vec![1, 2, 3]))}; - let result = matches_where_clause(&table, &row, &where_clause); - assert!(result.is_err()); - let expected_error = "Found invalid operator: GreaterEquals for data type: Blob"; - assert_eq!(expected_error, result.err().unwrap()); - } } \ No newline at end of file