#[cfg(test)]
mod tests {
use crate::executor::aggregate::{AggDef, AggFunc, HashAggregateNode};
use crate::executor::filter::FilterNode;
use crate::executor::join::HashJoinNode;
use crate::executor::limit::LimitNode;
use crate::executor::node::PlanNode;
use crate::executor::project::{PassThroughNode, ProjectExpr, ProjectNode};
use crate::executor::scan::{EmptyNode, ValuesNode};
use crate::executor::sort::{SortKey, SortNode};
use crate::executor::types::{ColumnMeta, Row, Schema};
use crate::soch_ql::SochValue;
use crate::sql::ast::{BinaryOperator, ColumnRef, Expr, JoinType, Literal};
fn schema_abc() -> Schema {
Schema::new(vec![
ColumnMeta::new("a"),
ColumnMeta::new("b"),
ColumnMeta::new("c"),
])
}
fn schema_id_name_age() -> Schema {
Schema::new(vec![
ColumnMeta::new("id"),
ColumnMeta::new("name"),
ColumnMeta::new("age"),
])
}
fn sample_rows() -> Vec<Row> {
vec![
vec![
SochValue::Int(1),
SochValue::Text("Alice".into()),
SochValue::Int(30),
],
vec![
SochValue::Int(2),
SochValue::Text("Bob".into()),
SochValue::Int(25),
],
vec![
SochValue::Int(3),
SochValue::Text("Carol".into()),
SochValue::Int(35),
],
vec![
SochValue::Int(4),
SochValue::Text("Dave".into()),
SochValue::Int(25),
],
vec![
SochValue::Int(5),
SochValue::Text("Eve".into()),
SochValue::Int(30),
],
]
}
fn values_node(schema: Schema, rows: Vec<Row>) -> Box<dyn PlanNode> {
Box::new(ValuesNode::new(schema, rows))
}
#[test]
fn test_schema_index_of() {
let s = schema_abc();
assert_eq!(s.index_of("a"), Some(0));
assert_eq!(s.index_of("b"), Some(1));
assert_eq!(s.index_of("c"), Some(2));
assert_eq!(s.index_of("d"), None);
}
#[test]
fn test_schema_qualified_index() {
let s = Schema::new(vec![
ColumnMeta::qualified("t1", "id"),
ColumnMeta::qualified("t2", "id"),
]);
assert_eq!(s.index_of_qualified(Some("t1"), "id"), Some(0));
assert_eq!(s.index_of_qualified(Some("t2"), "id"), Some(1));
assert_eq!(s.index_of_qualified(None, "id"), Some(0)); }
#[test]
fn test_schema_merge() {
let s1 = Schema::new(vec![ColumnMeta::new("a")]);
let s2 = Schema::new(vec![ColumnMeta::new("b")]);
let merged = s1.merge(&s2);
assert_eq!(merged.len(), 2);
assert_eq!(merged.column_names(), vec!["a", "b"]);
}
#[test]
fn test_values_node_basic() {
let rows = vec![
vec![SochValue::Int(1), SochValue::Text("hello".into())],
vec![SochValue::Int(2), SochValue::Text("world".into())],
];
let schema = Schema::new(vec![ColumnMeta::new("x"), ColumnMeta::new("y")]);
let mut node = ValuesNode::new(schema, rows);
let r1 = node.next().unwrap();
assert!(r1.is_some());
assert_eq!(r1.unwrap()[0], SochValue::Int(1));
let r2 = node.next().unwrap();
assert!(r2.is_some());
assert_eq!(r2.unwrap()[0], SochValue::Int(2));
let r3 = node.next().unwrap();
assert!(r3.is_none());
}
#[test]
fn test_values_node_reset() {
let rows = vec![vec![SochValue::Int(42)]];
let schema = Schema::new(vec![ColumnMeta::new("x")]);
let mut node = ValuesNode::new(schema, rows);
assert!(node.next().unwrap().is_some());
assert!(node.next().unwrap().is_none());
node.reset().unwrap();
assert!(node.next().unwrap().is_some());
assert!(node.next().unwrap().is_none());
}
#[test]
fn test_values_node_collect_all() {
let rows = sample_rows();
let mut node = ValuesNode::new(schema_id_name_age(), rows.clone());
let collected = node.collect_all().unwrap();
assert_eq!(collected.len(), 5);
assert_eq!(collected, rows);
}
#[test]
fn test_empty_node() {
let mut node = EmptyNode::new(schema_abc());
assert!(node.next().unwrap().is_none());
assert_eq!(node.schema().len(), 3);
}
#[test]
fn test_filter_equality() {
let predicate = Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("age"))),
op: BinaryOperator::Eq,
right: Box::new(Expr::Literal(Literal::Integer(25))),
};
let input = values_node(schema_id_name_age(), sample_rows());
let mut filter = FilterNode::new(input, predicate);
let rows = filter.collect_all().unwrap();
assert_eq!(rows.len(), 2); assert_eq!(rows[0][1], SochValue::Text("Bob".into()));
assert_eq!(rows[1][1], SochValue::Text("Dave".into()));
}
#[test]
fn test_filter_greater_than() {
let predicate = Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("age"))),
op: BinaryOperator::Gt,
right: Box::new(Expr::Literal(Literal::Integer(28))),
};
let input = values_node(schema_id_name_age(), sample_rows());
let mut filter = FilterNode::new(input, predicate);
let rows = filter.collect_all().unwrap();
assert_eq!(rows.len(), 3); }
#[test]
fn test_filter_or_condition() {
let predicate = Expr::BinaryOp {
left: Box::new(Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("age"))),
op: BinaryOperator::Eq,
right: Box::new(Expr::Literal(Literal::Integer(25))),
}),
op: BinaryOperator::Or,
right: Box::new(Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("age"))),
op: BinaryOperator::Eq,
right: Box::new(Expr::Literal(Literal::Integer(35))),
}),
};
let input = values_node(schema_id_name_age(), sample_rows());
let mut filter = FilterNode::new(input, predicate);
let rows = filter.collect_all().unwrap();
assert_eq!(rows.len(), 3); }
#[test]
fn test_filter_and_condition() {
let predicate = Expr::BinaryOp {
left: Box::new(Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("age"))),
op: BinaryOperator::Ge,
right: Box::new(Expr::Literal(Literal::Integer(30))),
}),
op: BinaryOperator::And,
right: Box::new(Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("name"))),
op: BinaryOperator::Ne,
right: Box::new(Expr::Literal(Literal::String("Eve".into()))),
}),
};
let input = values_node(schema_id_name_age(), sample_rows());
let mut filter = FilterNode::new(input, predicate);
let rows = filter.collect_all().unwrap();
assert_eq!(rows.len(), 2); }
#[test]
fn test_filter_no_matches() {
let predicate = Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("age"))),
op: BinaryOperator::Gt,
right: Box::new(Expr::Literal(Literal::Integer(100))),
};
let input = values_node(schema_id_name_age(), sample_rows());
let mut filter = FilterNode::new(input, predicate);
let rows = filter.collect_all().unwrap();
assert_eq!(rows.len(), 0);
}
#[test]
fn test_project_select_columns() {
let input = values_node(schema_id_name_age(), sample_rows());
let mut proj = ProjectNode::columns(input, vec!["name".into(), "age".into()]);
let rows = proj.collect_all().unwrap();
assert_eq!(rows.len(), 5);
assert_eq!(proj.schema().len(), 2);
assert_eq!(proj.schema().column_names(), vec!["name", "age"]);
assert_eq!(
rows[0],
vec![SochValue::Text("Alice".into()), SochValue::Int(30)]
);
}
#[test]
fn test_project_expression() {
let input = values_node(schema_id_name_age(), sample_rows());
let exprs = vec![ProjectExpr {
expr: Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("age"))),
op: BinaryOperator::Plus,
right: Box::new(Expr::Literal(Literal::Integer(10))),
},
alias: "age_plus_10".into(),
}];
let mut proj = ProjectNode::new(input, exprs);
let rows = proj.collect_all().unwrap();
assert_eq!(rows.len(), 5);
assert_eq!(rows[0][0], SochValue::Int(40)); assert_eq!(rows[1][0], SochValue::Int(35)); }
#[test]
fn test_project_multiply() {
let input = values_node(schema_id_name_age(), sample_rows());
let exprs = vec![ProjectExpr {
expr: Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("age"))),
op: BinaryOperator::Multiply,
right: Box::new(Expr::Literal(Literal::Integer(2))),
},
alias: "double_age".into(),
}];
let mut proj = ProjectNode::new(input, exprs);
let rows = proj.collect_all().unwrap();
assert_eq!(rows.len(), 5);
assert_eq!(rows[0][0], SochValue::Int(60)); assert_eq!(rows[1][0], SochValue::Int(50)); }
#[test]
fn test_project_literal() {
let input = values_node(schema_id_name_age(), sample_rows());
let exprs = vec![ProjectExpr {
expr: Expr::Literal(Literal::String("hello".into())),
alias: "tag".into(),
}];
let mut proj = ProjectNode::new(input, exprs);
let rows = proj.collect_all().unwrap();
assert_eq!(rows.len(), 5);
for row in &rows {
assert_eq!(row[0], SochValue::Text("hello".into()));
}
}
#[test]
fn test_passthrough() {
let input = values_node(schema_id_name_age(), sample_rows());
let mut pass = PassThroughNode::new(input);
let rows = pass.collect_all().unwrap();
assert_eq!(rows.len(), 5);
assert_eq!(pass.schema().len(), 3);
}
#[test]
fn test_sort_ascending() {
let input = values_node(schema_id_name_age(), sample_rows());
let sort_keys = vec![SortKey {
expr: Expr::Column(ColumnRef::new("name")),
ascending: true,
nulls_first: true,
}];
let mut sort = SortNode::new(input, sort_keys);
let rows = sort.collect_all().unwrap();
assert_eq!(rows.len(), 5);
assert_eq!(rows[0][1], SochValue::Text("Alice".into()));
assert_eq!(rows[1][1], SochValue::Text("Bob".into()));
assert_eq!(rows[2][1], SochValue::Text("Carol".into()));
assert_eq!(rows[3][1], SochValue::Text("Dave".into()));
assert_eq!(rows[4][1], SochValue::Text("Eve".into()));
}
#[test]
fn test_sort_descending() {
let input = values_node(schema_id_name_age(), sample_rows());
let sort_keys = vec![SortKey {
expr: Expr::Column(ColumnRef::new("age")),
ascending: false,
nulls_first: false,
}];
let mut sort = SortNode::new(input, sort_keys);
let rows = sort.collect_all().unwrap();
assert_eq!(rows.len(), 5);
assert_eq!(rows[0][2], SochValue::Int(35)); assert!(rows[1][2] == SochValue::Int(30));
assert!(rows[2][2] == SochValue::Int(30));
assert_eq!(rows[3][2], SochValue::Int(25));
assert_eq!(rows[4][2], SochValue::Int(25));
}
#[test]
fn test_sort_with_nulls() {
let schema = Schema::new(vec![ColumnMeta::new("x")]);
let rows = vec![
vec![SochValue::Int(3)],
vec![SochValue::Null],
vec![SochValue::Int(1)],
vec![SochValue::Null],
vec![SochValue::Int(2)],
];
let input = values_node(schema.clone(), rows.clone());
let sort_keys = vec![SortKey {
expr: Expr::Column(ColumnRef::new("x")),
ascending: true,
nulls_first: true,
}];
let mut sort = SortNode::new(input, sort_keys);
let result = sort.collect_all().unwrap();
assert_eq!(result[0][0], SochValue::Null);
assert_eq!(result[1][0], SochValue::Null);
assert_eq!(result[2][0], SochValue::Int(1));
assert_eq!(result[3][0], SochValue::Int(2));
assert_eq!(result[4][0], SochValue::Int(3));
}
#[test]
fn test_limit_only() {
let input = values_node(schema_id_name_age(), sample_rows());
let mut limit = LimitNode::new(input, Some(3), 0);
let rows = limit.collect_all().unwrap();
assert_eq!(rows.len(), 3);
assert_eq!(rows[0][0], SochValue::Int(1));
assert_eq!(rows[2][0], SochValue::Int(3));
}
#[test]
fn test_offset_only() {
let input = values_node(schema_id_name_age(), sample_rows());
let mut limit = LimitNode::new(input, None, 3);
let rows = limit.collect_all().unwrap();
assert_eq!(rows.len(), 2); assert_eq!(rows[0][0], SochValue::Int(4));
assert_eq!(rows[1][0], SochValue::Int(5));
}
#[test]
fn test_limit_and_offset() {
let input = values_node(schema_id_name_age(), sample_rows());
let mut limit = LimitNode::new(input, Some(2), 1);
let rows = limit.collect_all().unwrap();
assert_eq!(rows.len(), 2); assert_eq!(rows[0][0], SochValue::Int(2)); assert_eq!(rows[1][0], SochValue::Int(3)); }
#[test]
fn test_limit_larger_than_input() {
let input = values_node(schema_id_name_age(), sample_rows());
let mut limit = LimitNode::new(input, Some(100), 0);
let rows = limit.collect_all().unwrap();
assert_eq!(rows.len(), 5);
}
#[test]
fn test_offset_past_end() {
let input = values_node(schema_id_name_age(), sample_rows());
let mut limit = LimitNode::new(input, None, 100);
let rows = limit.collect_all().unwrap();
assert_eq!(rows.len(), 0);
}
#[test]
fn test_aggregate_count_all() {
let input = values_node(schema_id_name_age(), sample_rows());
let aggs = vec![AggDef {
func: AggFunc::Count,
expr: None, alias: "count".into(),
}];
let mut agg = HashAggregateNode::new(input, vec![], aggs);
let rows = agg.collect_all().unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0][0], SochValue::Int(5));
}
#[test]
fn test_aggregate_sum_avg() {
let input = values_node(schema_id_name_age(), sample_rows());
let aggs = vec![
AggDef {
func: AggFunc::Sum,
expr: Some(Expr::Column(ColumnRef::new("age"))),
alias: "total_age".into(),
},
AggDef {
func: AggFunc::Avg,
expr: Some(Expr::Column(ColumnRef::new("age"))),
alias: "avg_age".into(),
},
];
let mut agg = HashAggregateNode::new(input, vec![], aggs);
let rows = agg.collect_all().unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0][0], SochValue::Int(145));
assert_eq!(rows[0][1], SochValue::Float(29.0));
}
#[test]
fn test_aggregate_min_max() {
let input = values_node(schema_id_name_age(), sample_rows());
let aggs = vec![
AggDef {
func: AggFunc::Min,
expr: Some(Expr::Column(ColumnRef::new("age"))),
alias: "min_age".into(),
},
AggDef {
func: AggFunc::Max,
expr: Some(Expr::Column(ColumnRef::new("age"))),
alias: "max_age".into(),
},
];
let mut agg = HashAggregateNode::new(input, vec![], aggs);
let rows = agg.collect_all().unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0][0], SochValue::Int(25));
assert_eq!(rows[0][1], SochValue::Int(35));
}
#[test]
fn test_aggregate_group_by() {
let input = values_node(schema_id_name_age(), sample_rows());
let group_by = vec![Expr::Column(ColumnRef::new("age"))];
let aggs = vec![
AggDef {
func: AggFunc::Count,
expr: None,
alias: "cnt".into(),
},
AggDef {
func: AggFunc::Sum,
expr: Some(Expr::Column(ColumnRef::new("id"))),
alias: "sum_id".into(),
},
];
let mut agg = HashAggregateNode::new(input, group_by, aggs);
let mut rows = agg.collect_all().unwrap();
assert_eq!(rows.len(), 3);
rows.sort_by(|a, b| {
if let (SochValue::Int(ia), SochValue::Int(ib)) = (&a[0], &b[0]) {
ia.cmp(ib)
} else {
std::cmp::Ordering::Equal
}
});
assert_eq!(rows[0][0], SochValue::Int(25));
assert_eq!(rows[0][1], SochValue::Int(2));
assert_eq!(rows[0][2], SochValue::Int(6));
assert_eq!(rows[1][0], SochValue::Int(30));
assert_eq!(rows[1][1], SochValue::Int(2));
assert_eq!(rows[1][2], SochValue::Int(6));
assert_eq!(rows[2][0], SochValue::Int(35));
assert_eq!(rows[2][1], SochValue::Int(1));
assert_eq!(rows[2][2], SochValue::Int(3));
}
#[test]
fn test_aggregate_empty_input() {
let input = values_node(schema_id_name_age(), vec![]);
let aggs = vec![AggDef {
func: AggFunc::Count,
expr: None,
alias: "count".into(),
}];
let mut agg = HashAggregateNode::new(input, vec![], aggs);
let rows = agg.collect_all().unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0][0], SochValue::Int(0));
}
#[test]
fn test_filter_then_sort_then_limit() {
let input = values_node(schema_id_name_age(), sample_rows());
let filter = FilterNode::new(
input,
Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("age"))),
op: BinaryOperator::Ge,
right: Box::new(Expr::Literal(Literal::Integer(30))),
},
);
let sort = SortNode::new(
Box::new(filter),
vec![SortKey {
expr: Expr::Column(ColumnRef::new("name")),
ascending: true,
nulls_first: true,
}],
);
let mut limit = LimitNode::new(Box::new(sort), Some(2), 0);
let rows = limit.collect_all().unwrap();
assert_eq!(rows.len(), 2);
assert_eq!(rows[0][1], SochValue::Text("Alice".into()));
assert_eq!(rows[1][1], SochValue::Text("Carol".into()));
}
#[test]
fn test_project_with_filter() {
let input = values_node(schema_id_name_age(), sample_rows());
let filter = FilterNode::new(
input,
Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("id"))),
op: BinaryOperator::Lt,
right: Box::new(Expr::Literal(Literal::Integer(3))),
},
);
let exprs = vec![
ProjectExpr {
expr: Expr::Column(ColumnRef::new("name")),
alias: "name".into(),
},
ProjectExpr {
expr: Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("age"))),
op: BinaryOperator::Plus,
right: Box::new(Expr::Literal(Literal::Integer(1))),
},
alias: "next_age".into(),
},
];
let mut proj = ProjectNode::new(Box::new(filter), exprs);
let rows = proj.collect_all().unwrap();
assert_eq!(rows.len(), 2); assert_eq!(
rows[0],
vec![SochValue::Text("Alice".into()), SochValue::Int(31)]
);
assert_eq!(
rows[1],
vec![SochValue::Text("Bob".into()), SochValue::Int(26)]
);
}
#[test]
fn test_aggregate_with_filter() {
let input = values_node(schema_id_name_age(), sample_rows());
let filter = FilterNode::new(
input,
Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("age"))),
op: BinaryOperator::Lt,
right: Box::new(Expr::Literal(Literal::Integer(35))),
},
);
let group_by = vec![Expr::Column(ColumnRef::new("age"))];
let aggs = vec![AggDef {
func: AggFunc::Count,
expr: None,
alias: "cnt".into(),
}];
let mut agg = HashAggregateNode::new(Box::new(filter), group_by, aggs);
let mut rows = agg.collect_all().unwrap();
assert_eq!(rows.len(), 2);
rows.sort_by(|a, b| {
if let (SochValue::Int(ia), SochValue::Int(ib)) = (&a[0], &b[0]) {
ia.cmp(ib)
} else {
std::cmp::Ordering::Equal
}
});
assert_eq!(rows[0], vec![SochValue::Int(25), SochValue::Int(2)]);
assert_eq!(rows[1], vec![SochValue::Int(30), SochValue::Int(2)]);
}
#[test]
fn test_hash_join_inner() {
let users_schema = Schema::new(vec![
ColumnMeta::qualified("users", "id"),
ColumnMeta::qualified("users", "name"),
]);
let users = vec![
vec![SochValue::Int(1), SochValue::Text("Alice".into())],
vec![SochValue::Int(2), SochValue::Text("Bob".into())],
vec![SochValue::Int(3), SochValue::Text("Carol".into())],
];
let orders_schema = Schema::new(vec![
ColumnMeta::qualified("orders", "user_id"),
ColumnMeta::qualified("orders", "product"),
]);
let orders = vec![
vec![SochValue::Int(1), SochValue::Text("Widget".into())],
vec![SochValue::Int(2), SochValue::Text("Gadget".into())],
vec![SochValue::Int(1), SochValue::Text("Doohickey".into())],
];
let left = values_node(users_schema, users);
let right = values_node(orders_schema, orders);
let build_key = Expr::Column(ColumnRef::qualified("users", "id"));
let probe_key = Expr::Column(ColumnRef::qualified("orders", "user_id"));
let mut join = HashJoinNode::new(left, right, build_key, probe_key, JoinType::Inner);
let mut rows = join.collect_all().unwrap();
assert_eq!(rows.len(), 3);
rows.sort_by(|a, b| format!("{:?}", a).cmp(&format!("{:?}", b)));
assert_eq!(join.schema().len(), 4);
}
#[test]
fn test_eval_literal() {
use crate::executor::eval::eval_expr;
let schema = Schema::new(vec![]);
let row = vec![];
assert_eq!(
eval_expr(&Expr::Literal(Literal::Integer(42)), &row, &schema).unwrap(),
SochValue::Int(42)
);
assert_eq!(
eval_expr(&Expr::Literal(Literal::Float(3.14)), &row, &schema).unwrap(),
SochValue::Float(3.14)
);
assert_eq!(
eval_expr(&Expr::Literal(Literal::String("hi".into())), &row, &schema).unwrap(),
SochValue::Text("hi".into())
);
assert_eq!(
eval_expr(&Expr::Literal(Literal::Boolean(true)), &row, &schema).unwrap(),
SochValue::Bool(true)
);
assert_eq!(
eval_expr(&Expr::Literal(Literal::Null), &row, &schema).unwrap(),
SochValue::Null
);
}
#[test]
fn test_eval_column_ref() {
use crate::executor::eval::eval_expr;
let schema = Schema::new(vec![ColumnMeta::new("x"), ColumnMeta::new("y")]);
let row = vec![SochValue::Int(10), SochValue::Text("hello".into())];
assert_eq!(
eval_expr(&Expr::Column(ColumnRef::new("x")), &row, &schema).unwrap(),
SochValue::Int(10)
);
assert_eq!(
eval_expr(&Expr::Column(ColumnRef::new("y")), &row, &schema).unwrap(),
SochValue::Text("hello".into())
);
}
#[test]
fn test_eval_arithmetic() {
use crate::executor::eval::eval_expr;
let schema = Schema::new(vec![ColumnMeta::new("a")]);
let row = vec![SochValue::Int(10)];
let expr = Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("a"))),
op: BinaryOperator::Plus,
right: Box::new(Expr::Literal(Literal::Integer(5))),
};
assert_eq!(eval_expr(&expr, &row, &schema).unwrap(), SochValue::Int(15));
let expr = Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("a"))),
op: BinaryOperator::Multiply,
right: Box::new(Expr::Literal(Literal::Integer(3))),
};
assert_eq!(eval_expr(&expr, &row, &schema).unwrap(), SochValue::Int(30));
}
#[test]
fn test_eval_comparison() {
use crate::executor::eval::eval_predicate;
let schema = Schema::new(vec![ColumnMeta::new("x")]);
let row = vec![SochValue::Int(10)];
let expr = Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("x"))),
op: BinaryOperator::Eq,
right: Box::new(Expr::Literal(Literal::Integer(10))),
};
assert!(eval_predicate(&expr, &row, &schema).unwrap());
let expr = Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("x"))),
op: BinaryOperator::Lt,
right: Box::new(Expr::Literal(Literal::Integer(5))),
};
assert!(!eval_predicate(&expr, &row, &schema).unwrap());
}
#[test]
fn test_eval_is_null() {
use crate::executor::eval::eval_predicate;
let schema = Schema::new(vec![ColumnMeta::new("x")]);
let null_row = vec![SochValue::Null];
let non_null_row = vec![SochValue::Int(5)];
let is_null = Expr::IsNull {
expr: Box::new(Expr::Column(ColumnRef::new("x"))),
negated: false,
};
assert!(eval_predicate(&is_null, &null_row, &schema).unwrap());
assert!(!eval_predicate(&is_null, &non_null_row, &schema).unwrap());
let is_not_null = Expr::IsNull {
expr: Box::new(Expr::Column(ColumnRef::new("x"))),
negated: true,
};
assert!(!eval_predicate(&is_not_null, &null_row, &schema).unwrap());
assert!(eval_predicate(&is_not_null, &non_null_row, &schema).unwrap());
}
#[test]
fn test_eval_between() {
use crate::executor::eval::eval_predicate;
let schema = Schema::new(vec![ColumnMeta::new("x")]);
let row = vec![SochValue::Int(5)];
let between = Expr::Between {
expr: Box::new(Expr::Column(ColumnRef::new("x"))),
low: Box::new(Expr::Literal(Literal::Integer(1))),
high: Box::new(Expr::Literal(Literal::Integer(10))),
negated: false,
};
assert!(eval_predicate(&between, &row, &schema).unwrap());
let not_between = Expr::Between {
expr: Box::new(Expr::Column(ColumnRef::new("x"))),
low: Box::new(Expr::Literal(Literal::Integer(1))),
high: Box::new(Expr::Literal(Literal::Integer(3))),
negated: true,
};
assert!(eval_predicate(¬_between, &row, &schema).unwrap());
}
#[test]
fn test_eval_in_list() {
use crate::executor::eval::eval_predicate;
let schema = Schema::new(vec![ColumnMeta::new("x")]);
let row = vec![SochValue::Int(3)];
let in_list = Expr::InList {
expr: Box::new(Expr::Column(ColumnRef::new("x"))),
list: vec![
Expr::Literal(Literal::Integer(1)),
Expr::Literal(Literal::Integer(2)),
Expr::Literal(Literal::Integer(3)),
],
negated: false,
};
assert!(eval_predicate(&in_list, &row, &schema).unwrap());
let not_in = Expr::InList {
expr: Box::new(Expr::Column(ColumnRef::new("x"))),
list: vec![
Expr::Literal(Literal::Integer(10)),
Expr::Literal(Literal::Integer(20)),
],
negated: false,
};
assert!(!eval_predicate(¬_in, &row, &schema).unwrap());
}
#[test]
fn test_eval_like() {
use crate::executor::eval::eval_predicate;
let schema = Schema::new(vec![ColumnMeta::new("name")]);
let row = vec![SochValue::Text("Alice".into())];
let like = Expr::Like {
expr: Box::new(Expr::Column(ColumnRef::new("name"))),
pattern: Box::new(Expr::Literal(Literal::String("Ali%".into()))),
escape: None,
negated: false,
};
assert!(eval_predicate(&like, &row, &schema).unwrap());
let not_match = Expr::Like {
expr: Box::new(Expr::Column(ColumnRef::new("name"))),
pattern: Box::new(Expr::Literal(Literal::String("%ob".into()))),
escape: None,
negated: false,
};
assert!(!eval_predicate(¬_match, &row, &schema).unwrap());
}
#[test]
fn test_full_pipeline_filter_project_sort_limit() {
let input = values_node(schema_id_name_age(), sample_rows());
let filter = FilterNode::new(
input,
Expr::BinaryOp {
left: Box::new(Expr::Column(ColumnRef::new("age"))),
op: BinaryOperator::Gt,
right: Box::new(Expr::Literal(Literal::Integer(25))),
},
);
let project = ProjectNode::columns(Box::new(filter), vec!["name".into(), "age".into()]);
let sort = SortNode::new(
Box::new(project),
vec![SortKey {
expr: Expr::Column(ColumnRef::new("age")),
ascending: false,
nulls_first: false,
}],
);
let mut limit = LimitNode::new(Box::new(sort), Some(2), 0);
let rows = limit.collect_all().unwrap();
assert_eq!(rows.len(), 2);
assert_eq!(rows[0][1], SochValue::Int(35)); assert_eq!(rows[1][1], SochValue::Int(30)); }
}