use vibesql_ast::Expression;
use vibesql_catalog::{ColumnSchema, TableSchema};
use vibesql_executor::ExpressionEvaluator;
use vibesql_storage::Row;
use vibesql_types::{DataType, SqlValue};
fn create_test_schema() -> TableSchema {
TableSchema::new(
"test_table".to_string(),
vec![
ColumnSchema::new("id".to_string(), DataType::Integer, false),
ColumnSchema::new(
"status".to_string(),
DataType::Varchar { max_length: Some(50) },
false,
),
ColumnSchema::new("value".to_string(), DataType::Integer, false),
],
)
}
#[test]
fn test_simple_case_basic_match() {
let schema = create_test_schema();
let evaluator = ExpressionEvaluator::new(&schema);
let case_expr = Expression::Case {
operand: Some(Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"status", false,
)))),
when_clauses: vec![
vibesql_ast::CaseWhen {
conditions: vec![Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from(
"active",
)))],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("Active User"))),
},
vibesql_ast::CaseWhen {
conditions: vec![Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from(
"inactive",
)))],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from(
"Inactive User",
))),
},
],
else_result: Some(Box::new(Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from(
"Unknown",
))))),
};
let row = Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("active")),
SqlValue::Integer(100),
]);
let result = evaluator.eval(&case_expr, &row).unwrap();
assert_eq!(result, SqlValue::Varchar(arcstr::ArcStr::from("Active User")));
let row2 = Row::new(vec![
SqlValue::Integer(2),
SqlValue::Varchar(arcstr::ArcStr::from("inactive")),
SqlValue::Integer(50),
]);
let result2 = evaluator.eval(&case_expr, &row2).unwrap();
assert_eq!(result2, SqlValue::Varchar(arcstr::ArcStr::from("Inactive User")));
let row3 = Row::new(vec![
SqlValue::Integer(3),
SqlValue::Varchar(arcstr::ArcStr::from("pending")),
SqlValue::Integer(75),
]);
let result3 = evaluator.eval(&case_expr, &row3).unwrap();
assert_eq!(result3, SqlValue::Varchar(arcstr::ArcStr::from("Unknown")));
}
#[test]
fn test_simple_case_null_handling() {
let schema = create_test_schema();
let evaluator = ExpressionEvaluator::new(&schema);
let case_expr = Expression::Case {
operand: Some(Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"status", false,
)))),
when_clauses: vec![vibesql_ast::CaseWhen {
conditions: vec![Expression::Literal(SqlValue::Null)],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("Null Status"))),
}],
else_result: Some(Box::new(Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from(
"Not Null",
))))),
};
let row = Row::new(vec![SqlValue::Integer(1), SqlValue::Null, SqlValue::Integer(100)]);
let result = evaluator.eval(&case_expr, &row).unwrap();
assert_eq!(result, SqlValue::Varchar(arcstr::ArcStr::from("Not Null")));
}
#[test]
fn test_searched_case_basic() {
let schema = create_test_schema();
let evaluator = ExpressionEvaluator::new(&schema);
let case_expr = Expression::Case {
operand: None, when_clauses: vec![
vibesql_ast::CaseWhen {
conditions: vec![Expression::BinaryOp {
op: vibesql_ast::BinaryOperator::LessThan,
left: Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"value", false,
))),
right: Box::new(Expression::Literal(SqlValue::Integer(50))),
}],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("Low"))),
},
vibesql_ast::CaseWhen {
conditions: vec![Expression::BinaryOp {
op: vibesql_ast::BinaryOperator::LessThan,
left: Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"value", false,
))),
right: Box::new(Expression::Literal(SqlValue::Integer(100))),
}],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("Medium"))),
},
],
else_result: Some(Box::new(Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from(
"High",
))))),
};
let row = Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("test")),
SqlValue::Integer(25),
]);
let result = evaluator.eval(&case_expr, &row).unwrap();
assert_eq!(result, SqlValue::Varchar(arcstr::ArcStr::from("Low")));
let row2 = Row::new(vec![
SqlValue::Integer(2),
SqlValue::Varchar(arcstr::ArcStr::from("test")),
SqlValue::Integer(75),
]);
let result2 = evaluator.eval(&case_expr, &row2).unwrap();
assert_eq!(result2, SqlValue::Varchar(arcstr::ArcStr::from("Medium")));
let row3 = Row::new(vec![
SqlValue::Integer(3),
SqlValue::Varchar(arcstr::ArcStr::from("test")),
SqlValue::Integer(150),
]);
let result3 = evaluator.eval(&case_expr, &row3).unwrap();
assert_eq!(result3, SqlValue::Varchar(arcstr::ArcStr::from("High")));
}
#[test]
fn test_searched_case_null_condition() {
let schema = create_test_schema();
let evaluator = ExpressionEvaluator::new(&schema);
let case_expr = Expression::Case {
operand: None,
when_clauses: vec![vibesql_ast::CaseWhen {
conditions: vec![Expression::Literal(SqlValue::Null)],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("yes"))),
}],
else_result: Some(Box::new(Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from(
"no",
))))),
};
let row = Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("test")),
SqlValue::Integer(100),
]);
let result = evaluator.eval(&case_expr, &row).unwrap();
assert_eq!(result, SqlValue::Varchar(arcstr::ArcStr::from("no")));
}
#[test]
fn test_case_no_else_defaults_to_null() {
let schema = create_test_schema();
let evaluator = ExpressionEvaluator::new(&schema);
let case_expr = Expression::Case {
operand: Some(Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"value", false,
)))),
when_clauses: vec![
vibesql_ast::CaseWhen {
conditions: vec![Expression::Literal(SqlValue::Integer(1))],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("one"))),
},
vibesql_ast::CaseWhen {
conditions: vec![Expression::Literal(SqlValue::Integer(2))],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("two"))),
},
],
else_result: None, };
let row = Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("test")),
SqlValue::Integer(3),
]);
let result = evaluator.eval(&case_expr, &row).unwrap();
assert_eq!(result, SqlValue::Null);
}
#[test]
fn test_case_lazy_evaluation() {
let schema = create_test_schema();
let evaluator = ExpressionEvaluator::new(&schema);
let case_expr = Expression::Case {
operand: Some(Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"value", false,
)))),
when_clauses: vec![
vibesql_ast::CaseWhen {
conditions: vec![Expression::Literal(SqlValue::Integer(1))],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("first"))),
},
vibesql_ast::CaseWhen {
conditions: vec![Expression::Literal(SqlValue::Integer(1))],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("second"))),
},
],
else_result: Some(Box::new(Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from(
"other",
))))),
};
let row = Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("test")),
SqlValue::Integer(1),
]);
let result = evaluator.eval(&case_expr, &row).unwrap();
assert_eq!(result, SqlValue::Varchar(arcstr::ArcStr::from("first")));
}
#[test]
fn test_case_comma_separated_matching_first() {
let schema = create_test_schema();
let evaluator = ExpressionEvaluator::new(&schema);
let case_expr = Expression::Case {
operand: Some(Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"value", false,
)))),
when_clauses: vec![vibesql_ast::CaseWhen {
conditions: vec![
Expression::Literal(SqlValue::Integer(2)),
Expression::Literal(SqlValue::Integer(3)),
Expression::Literal(SqlValue::Integer(4)),
],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("match"))),
}],
else_result: Some(Box::new(Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from(
"no",
))))),
};
let row = Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("test")),
SqlValue::Integer(2),
]);
let result = evaluator.eval(&case_expr, &row).unwrap();
assert_eq!(result, SqlValue::Varchar(arcstr::ArcStr::from("match")));
}
#[test]
fn test_case_comma_separated_matching_last() {
let schema = create_test_schema();
let evaluator = ExpressionEvaluator::new(&schema);
let case_expr = Expression::Case {
operand: Some(Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"value", false,
)))),
when_clauses: vec![vibesql_ast::CaseWhen {
conditions: vec![
Expression::Literal(SqlValue::Integer(2)),
Expression::Literal(SqlValue::Integer(3)),
Expression::Literal(SqlValue::Integer(4)),
],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("match"))),
}],
else_result: Some(Box::new(Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from(
"no",
))))),
};
let row = Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("test")),
SqlValue::Integer(4),
]);
let result = evaluator.eval(&case_expr, &row).unwrap();
assert_eq!(result, SqlValue::Varchar(arcstr::ArcStr::from("match")));
}
#[test]
fn test_case_comma_separated_no_match() {
let schema = create_test_schema();
let evaluator = ExpressionEvaluator::new(&schema);
let case_expr = Expression::Case {
operand: Some(Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"value", false,
)))),
when_clauses: vec![vibesql_ast::CaseWhen {
conditions: vec![
Expression::Literal(SqlValue::Integer(2)),
Expression::Literal(SqlValue::Integer(3)),
Expression::Literal(SqlValue::Integer(4)),
],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("match"))),
}],
else_result: Some(Box::new(Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from(
"no",
))))),
};
let row = Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("test")),
SqlValue::Integer(5),
]);
let result = evaluator.eval(&case_expr, &row).unwrap();
assert_eq!(result, SqlValue::Varchar(arcstr::ArcStr::from("no")));
}
#[test]
fn test_case_comma_separated_duplicate_values() {
let schema = create_test_schema();
let evaluator = ExpressionEvaluator::new(&schema);
let case_expr = Expression::Case {
operand: Some(Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"value", false,
)))),
when_clauses: vec![vibesql_ast::CaseWhen {
conditions: vec![
Expression::Literal(SqlValue::Integer(2)),
Expression::Literal(SqlValue::Integer(2)),
],
result: Expression::Literal(SqlValue::Integer(1)),
}],
else_result: Some(Box::new(Expression::Literal(SqlValue::Integer(1)))),
};
let row = Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("test")),
SqlValue::Integer(0),
]);
let result = evaluator.eval(&case_expr, &row).unwrap();
assert_eq!(result, SqlValue::Integer(1));
}
#[test]
fn test_case_comma_separated_with_null() {
let schema = create_test_schema();
let evaluator = ExpressionEvaluator::new(&schema);
let case_expr = Expression::Case {
operand: Some(Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"value", false,
)))),
when_clauses: vec![vibesql_ast::CaseWhen {
conditions: vec![
Expression::Literal(SqlValue::Integer(2)),
Expression::Literal(SqlValue::Integer(2)),
],
result: Expression::Literal(SqlValue::Integer(1)),
}],
else_result: Some(Box::new(Expression::Literal(SqlValue::Null))),
};
let row = Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("test")),
SqlValue::Integer(2),
]);
let result = evaluator.eval(&case_expr, &row).unwrap();
assert_eq!(result, SqlValue::Integer(1));
}
#[test]
fn test_case_comma_separated_varchar() {
let schema = create_test_schema();
let evaluator = ExpressionEvaluator::new(&schema);
let case_expr = Expression::Case {
operand: Some(Box::new(Expression::ColumnRef(vibesql_ast::ColumnIdentifier::simple(
"status", false,
)))),
when_clauses: vec![vibesql_ast::CaseWhen {
conditions: vec![
Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("active"))),
Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("pending"))),
Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("new"))),
],
result: Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from("open"))),
}],
else_result: Some(Box::new(Expression::Literal(SqlValue::Varchar(arcstr::ArcStr::from(
"closed",
))))),
};
let row = Row::new(vec![
SqlValue::Integer(1),
SqlValue::Varchar(arcstr::ArcStr::from("pending")),
SqlValue::Integer(100),
]);
let result = evaluator.eval(&case_expr, &row).unwrap();
assert_eq!(result, SqlValue::Varchar(arcstr::ArcStr::from("open")));
}