use reifydb_core::{
interface::{
catalog::property::{ColumnPropertyKind, ColumnSaturationStrategy},
evaluate::TargetColumn,
},
value::{batch::empty_batch, column::factory},
};
use reifydb_evaluate::lower::LoweredExpr;
use reifydb_value::value::value_type::ValueType;
use crate::common::{
Env, Frozen, and, arith, batch, between, boolean, cast, column, compare, field_access, number, rows, strings,
};
#[test]
fn a_field_on_a_non_variable_object_keeps_the_old_error() {
let mut frozen = Frozen::inline(&[
"RUNTIME_009 \"\" Field 'f' not found on variable '<expression>' []",
"QUERY_001 \"missing\" column not found []",
]);
let env = Env::new();
let input = batch(vec![factory::int4("a", [1, 2])]);
for (object, code) in [("a", "RUNTIME_009"), ("missing", "QUERY_001")] {
let expression = field_access(column(object), "f");
let lowered = env.lowered(&expression, input.clone()).unwrap_err();
let old = frozen.old(&env, &expression, input.clone()).unwrap_err();
assert_eq!(lowered, old, "error of {object}.f");
assert_eq!(lowered.code, code, "code of {object}.f");
}
}
#[test]
fn a_field_on_a_non_variable_object_lowers_with_the_rest_of_its_expression() {
let env = Env::new();
let input = batch(vec![factory::int4_optional("a", [Some(5), None, Some(20)])]);
let expression = and(
between(column("a"), number("2"), number("10")),
compare("==", field_access(number("1"), "y"), number("1")),
);
let lowered = env.lowered(&expression, input).unwrap_err();
assert_eq!(lowered.code, "RUNTIME_009");
}
#[test]
fn a_new_schema_lowers_again_and_reads_the_right_column() {
let env = Env::new();
let expression = LoweredExpr::new(column("a"), "test");
let first = batch(vec![factory::int4("a", [1]), factory::int4("b", [2])]);
let moved = batch(vec![factory::int4("b", [20]), factory::int4("a", [10])]);
assert_eq!(strings(&expression.evaluate(&env.ctx(first)).unwrap()), vec!["1"]);
assert_eq!(strings(&expression.evaluate(&env.ctx(moved)).unwrap()), vec!["10"]);
}
#[test]
fn the_same_schema_reuses_the_lowering_for_later_batches() {
let env = Env::new();
let expression = LoweredExpr::new(column("a"), "test");
let first = batch(vec![factory::int4("a", [1, 2])]);
let second = batch(vec![factory::int4("a", [3, 4, 5])]);
assert_eq!(strings(&expression.evaluate(&env.ctx(first)).unwrap()), vec!["1", "2"]);
assert_eq!(strings(&expression.evaluate(&env.ctx(second)).unwrap()), vec!["3", "4", "5"]);
}
#[test]
fn a_lowering_error_is_returned_on_every_evaluate() {
let mut frozen = Frozen::inline(&[
"OPERATOR_005 \"1andtrue\" Cannot apply AND operator to number [\"AND is a logical operator that combines boolean values\", \"To convert numbers to boolean, use comparison operators like: value != 0\", \"For bitwise operations on integers, use the bitwise AND (&) operator instead\"]",
]);
let env = Env::new();
let expression = LoweredExpr::new(and(number("1"), boolean("true")), "test");
let first = expression.evaluate(&env.ctx(rows(2))).unwrap_err();
let second = expression.evaluate(&env.ctx(rows(2))).unwrap_err();
assert_eq!(first.code, second.code);
assert_eq!(first.fragment.text(), second.fragment.text());
assert_eq!(first.code, frozen.old(&env, &and(number("1"), boolean("true")), rows(2)).unwrap_err().code);
}
#[test]
fn an_expression_on_a_batch_with_no_columns_answers_row_count_rows() {
let mut frozen = Frozen::inline(&["Field { name: \"1 + 2\", data_type: Int16 } [\"3\"]"]);
let env = Env::new();
let expression = arith("+", number("1"), number("2"));
let mut ctx = env.ctx(empty_batch());
ctx.row_count = 1;
let lowered = LoweredExpr::new(expression.clone(), "test").evaluate(&ctx).unwrap();
let old = frozen.answer(LoweredExpr::new(expression.clone(), "test").evaluate(&ctx)).unwrap();
assert_eq!(lowered, old);
assert_eq!(strings(&lowered), vec!["3"]);
}
#[test]
fn a_field_that_holds_a_none_is_widened_to_an_option() {
let mut frozen = Frozen::inline(&[
"Field { name: \"cast(a, type)\", data_type: Int8, nullable: true } [\"1\", \"none\"]",
]);
let env = Env::new();
let expression = cast(column("a"), ValueType::Int1);
let mut ctx = env.ctx(batch(vec![factory::int4("a", [1, 300])]));
ctx.target = Some(TargetColumn::Partial {
source_name: None,
column_name: None,
column_type: ValueType::Int1,
properties: vec![ColumnPropertyKind::Saturation(ColumnSaturationStrategy::None)],
});
let lowered = LoweredExpr::new(expression.clone(), "test").evaluate(&ctx).unwrap();
let old = frozen.answer(LoweredExpr::new(expression.clone(), "test").evaluate(&ctx)).unwrap();
assert!(lowered.0.is_nullable());
assert_eq!(lowered, old);
assert_eq!(strings(&lowered), vec!["1", "none"]);
}