Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/db/table/helpers/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::HashSet;

use crate::db::table::{Table, Value, DataType};
use crate::interpreter::ast::{SelectStatementColumns, WhereStackElement, OrderByClause, LimitClause};
use crate::db::table::helpers::where_stack::matches_where_stack;
use crate::db::table::helpers::where_clause::row_matches_where_stack;
use crate::db::table::helpers::{order_by_clause::get_ordered_row_indicies, limit_clause::get_limited_rows};

pub struct DistinctOn<'a> {
Expand Down Expand Up @@ -42,7 +42,7 @@ pub fn get_row_indicies_matching_where_clause(table: &Table, where_clause: &Opti
if let Some(where_clause) = where_clause {
let mut row_indicies: Vec<usize> = vec![];
for (i, row) in table.rows.iter().enumerate() {
if matches_where_stack(table, &row, &where_clause)? {
if row_matches_where_stack(table, &row, &where_clause)? {
row_indicies.push(i);
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/db/table/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
pub mod limit_clause;
pub mod order_by_clause;
pub mod common;
pub mod where_stack;
pub mod where_condition;
pub mod where_clause;
89 changes: 89 additions & 0 deletions src/db/table/helpers/where_clause/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
mod where_condition;
mod where_stack;
use crate::interpreter::ast::{WhereStackElement, WhereCondition};
use crate::db::table::{Table, Value};


// We create an interface here to allow us to create a spy for testing short circuiting.
trait MatchesWhereClause {
fn matches_where_clause(&mut self, table: &Table, row: &Vec<Value>, where_clause: &WhereCondition) -> Result<bool, String>;
}

struct WhereConditionEvaluator;

impl MatchesWhereClause for WhereConditionEvaluator {
fn matches_where_clause(&mut self, table: &Table, row: &Vec<Value>, where_clause: &WhereCondition) -> Result<bool, String> {
where_condition::matches_where_clause(table, row, where_clause)
}
}

// This is the public function that is used to check if a row matches a where stack.
pub fn row_matches_where_stack(table: &Table, row: &Vec<Value>, where_stack: &Vec<WhereStackElement>) -> Result<bool, String> {
where_stack::matches_where_stack(table, row, where_stack, &mut WhereConditionEvaluator{})
}

#[cfg(test)]
mod tests {
use super::*;
use crate::db::table::{Table, Value, ColumnDefinition, DataType};
use crate::interpreter::ast::{WhereStackElement, Operand, Operator, LogicalOperator};

struct SpyWhereConditionEvaluator {
conditions_evaluated: Vec<WhereCondition>,
}

impl MatchesWhereClause for SpyWhereConditionEvaluator {
fn matches_where_clause(&mut self, table: &Table, row: &Vec<Value>, where_clause: &WhereCondition) -> Result<bool, String> {
self.conditions_evaluated.push(where_clause.clone());
where_condition::matches_where_clause(table, row, where_clause)
}
}


#[test]
fn matches_where_stack_works_short_circuits() {
// WHERE (id = 1 OR id = 2);
let table = Table::new("users".to_string(), vec![
ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] },
ColumnDefinition {name:"name".to_string(),data_type:DataType::Text, constraints: vec![] },
]);
let mut spy_where_condition_evaluator = SpyWhereConditionEvaluator{conditions_evaluated: vec![]};
let row = vec![
Value::Integer(1),
Value::Text("John".to_string()),
];
let condition_1 = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(1))};
let condition_2 = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(2))};
let where_stack = vec![
WhereStackElement::Condition(condition_1.clone()),
WhereStackElement::Condition(condition_2.clone()),
WhereStackElement::LogicalOperator(LogicalOperator::Or),
];
let result = where_stack::matches_where_stack(&table, &row, &where_stack, &mut spy_where_condition_evaluator);
assert!(result.is_ok() && result.unwrap());
assert_eq!(spy_where_condition_evaluator.conditions_evaluated, vec![condition_1]);
}

#[test]
fn matches_where_stack_does_not_short_circuit_if_condition_does_not_match() {
let table = Table::new("users".to_string(), vec![
ColumnDefinition {name:"id".to_string(),data_type:DataType::Integer, constraints: vec![] },
ColumnDefinition {name:"name".to_string(),data_type:DataType::Text, constraints: vec![] },
]);
let mut spy_where_condition_evaluator = SpyWhereConditionEvaluator{conditions_evaluated: vec![]};
let row = vec![
Value::Integer(1),
Value::Text("John".to_string()),
];
let condition_1 = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(2))};
let condition_2 = WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(1))};
let where_stack = vec![
WhereStackElement::Condition(condition_1.clone()),
WhereStackElement::Condition(condition_2.clone()),
WhereStackElement::LogicalOperator(LogicalOperator::Or),
];
let result = where_stack::matches_where_stack(&table, &row, &where_stack, &mut spy_where_condition_evaluator);
assert!(result.is_ok() && result.unwrap());
assert_eq!(spy_where_condition_evaluator.conditions_evaluated, vec![condition_1, condition_2]);
}
}
Original file line number Diff line number Diff line change
@@ -1,47 +1,63 @@
use crate::interpreter::ast::{WhereStackElement, LogicalOperator};
use crate::db::table::{Table, Value};
use crate::db::table::helpers::where_condition::matches_where_clause;
use crate::interpreter::ast::{WhereStackElement, LogicalOperator, WhereCondition};
use crate::db::table::helpers::where_clause::MatchesWhereClause;


// 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<Value>, where_stack: &Vec<WhereStackElement>) -> Result<bool, String> {
let mut result_stack = vec![];

enum Condition<'a> {
Boolean(bool),
WhereCondition(&'a WhereCondition),
}

impl<'a> Condition<'a> {
fn evaluate(&self, table: &Table, row: &Vec<Value>, where_clause_evaluator: &mut dyn MatchesWhereClause) -> Result<bool, String> {
match self {
Condition::Boolean(boolean) => Ok(*boolean),
Condition::WhereCondition(where_condition) => where_clause_evaluator.matches_where_clause(table, row, where_condition),
}
}
}

pub fn matches_where_stack(table: &Table, row: &Vec<Value>, where_stack: &Vec<WhereStackElement>, where_clause_evaluator: &mut dyn MatchesWhereClause) -> Result<bool, String> {
let mut result_stack: Vec<Condition> = vec![];
for where_stack_element in where_stack {
match where_stack_element {
WhereStackElement::Condition(where_condition) => {
result_stack.push(matches_where_clause(table, row, where_condition)?);
result_stack.push(Condition::WhereCondition(where_condition));
},
WhereStackElement::LogicalOperator(logical_operator) => {
let pop1 = match result_stack.pop() {
Some(pop1) => pop1,
None => return Err(format!("Error evaluating where clause with table: {:?}", table)),
};
match logical_operator {
LogicalOperator::Not => {
result_stack.push(!pop1);
LogicalOperator::Not => {
result_stack.push(Condition::Boolean(!pop1.evaluate(table, row, where_clause_evaluator)?));
}
LogicalOperator::And => {
let pop2 = match result_stack.pop() {
Some(pop2) => pop2,
None => return Err(format!("Error evaluating where clause with table: {:?}", table)),
};
result_stack.push(pop1 && pop2);
result_stack.push(Condition::Boolean(pop2.evaluate(table, row, where_clause_evaluator)? && pop1.evaluate(table, row, where_clause_evaluator)?));
}
LogicalOperator::Or => {
let pop2 = match result_stack.pop() {
Some(pop2) => pop2,
None => return Err(format!("Error evaluating where clause with table: {:?}", table)),
};
result_stack.push(pop1 || pop2);
result_stack.push(Condition::Boolean(pop2.evaluate(table, row, where_clause_evaluator)? || pop1.evaluate(table, row, where_clause_evaluator)?));
}
}
},
_ => unreachable!(),
_ => unreachable!(), // There should be no Parentheses in the final where stack.
}
}

if let Some(result) = result_stack.pop() {
return Ok(result);
return Ok(result.evaluate(table, row, where_clause_evaluator)?);
}
else {
return Err(format!("Error evaluating where clause with table: {:?}", table));
Expand All @@ -54,20 +70,20 @@ mod tests {
use crate::db::table::{Table, Value, ColumnDefinition, DataType};
use crate::interpreter::ast::{WhereStackElement, LogicalOperator};
use crate::interpreter::ast::{Operator, Operand, WhereCondition};
use crate::db::table::helpers::where_clause::WhereConditionEvaluator;

fn simple_condition(l_side: &str, operator: Operator, r_side: Value) -> WhereStackElement {
WhereStackElement::Condition(WhereCondition {l_side: Operand::Identifier(l_side.to_string()), operator, r_side: Operand::Value(r_side)})
}


#[test]
fn matches_where_stack_returns_true_if_row_matches_where_stack_with_single_condition() {
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_stack = vec![WhereStackElement::Condition(WhereCondition {l_side: Operand::Identifier("id".to_string()),operator:Operator::Equals,r_side: Operand::Value(Value::Integer(1))})];
let result = matches_where_stack(&table, &row, &where_stack);
let result = matches_where_stack(&table, &row, &where_stack, &mut WhereConditionEvaluator{});
assert!(result.is_ok() && result.unwrap());
}

Expand All @@ -92,23 +108,23 @@ mod tests {
WhereStackElement::LogicalOperator(LogicalOperator::Not),
WhereStackElement::LogicalOperator(LogicalOperator::Or),
];
let result = matches_where_stack(&table, &row, &where_stack);
let result = matches_where_stack(&table, &row, &where_stack, &mut WhereConditionEvaluator{});
assert!(result.is_ok() && result.unwrap());

let row = vec![
Value::Integer(2),
Value::Text("Fletcher".to_string()),
Value::Integer(15),
];
let result = matches_where_stack(&table, &row, &where_stack);
let result = matches_where_stack(&table, &row, &where_stack, &mut WhereConditionEvaluator{});
assert!(result.is_ok() && result.unwrap());

let row = vec![
Value::Integer(2),
Value::Text("John".to_string()),
Value::Integer(25),
];
let result = matches_where_stack(&table, &row, &where_stack);
let result = matches_where_stack(&table, &row, &where_stack, &mut WhereConditionEvaluator{});
assert!(result.is_ok() && !result.unwrap());
}
}
3 changes: 3 additions & 0 deletions src/interpreter/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ impl SelectStatementColumns {
}

#[derive(Debug, PartialEq)]
#[cfg_attr(test, derive(Clone))]
pub enum Operator {
Equals,
NotEquals,
Expand All @@ -178,6 +179,7 @@ pub enum Operator {
}

#[derive(Debug, PartialEq)]
#[cfg_attr(test, derive(Clone))]
pub struct WhereCondition {
pub l_side: Operand,
pub operator: Operator,
Expand All @@ -186,6 +188,7 @@ pub struct WhereCondition {


#[derive(Debug, PartialEq)]
#[cfg_attr(test, derive(Clone))]
pub enum Operand {
Value(Value),
ValueList(Vec<Value>),
Expand Down
Loading