use rudb_common::{LogicalType, Result, Value};
use rudb_kernels::{Comparison, Connective, call_values, cast_value, combine, compare_values};
use rudb_plan::{CompareOp, ConjunctionOp, Expr, ExprRef, Plan, Slice};
use rudb_vector::Vector;
pub const VOLATILE: [&str; 17] = [
"current_connection_id",
"current_query",
"current_query_id",
"current_transaction_id",
"currval",
"error",
"gen_random_uuid",
"nextval",
"random",
"setseed",
"setval",
"sleep_ms",
"stats",
"uuid",
"uuidv4",
"uuidv7",
"write_log",
];
pub fn value_of(plan: &Plan, expr: ExprRef) -> Result<Option<Value>> {
let value = match *plan.expr(expr) {
Expr::Constant(value) => plan.value(value).clone(),
Expr::Column(_)
| Expr::Aggregate { .. }
| Expr::Window { .. }
| Expr::Lambda { .. }
| Expr::LambdaParam(_) => return Ok(None),
Expr::Cast { input, try_cast } => {
if plan.expr_type(input) == &LogicalType::TimestampTz
&& plan.expr_type(expr) == &LogicalType::Varchar
{
return Ok(None);
}
let Some(inner) = value_of(plan, input)? else { return Ok(None) };
cast_value(&inner, plan.expr_type(expr), try_cast)?
}
Expr::Compare { op, left, right } => {
let (Some(left), Some(right)) = (value_of(plan, left)?, value_of(plan, right)?) else {
return Ok(None);
};
compare_values(comparison(op), &left, &right)?
}
Expr::Conjunction { op, children } => {
let Some(values) = values_of(plan, children)? else { return Ok(None) };
let vectors: Vec<Vector> = values
.into_iter()
.map(|value| Vector::constant(LogicalType::Boolean, value, 1))
.collect();
combine(connective(op), &vectors)?.value_at(0)
}
Expr::Function { name, args } => {
let name = plan.string(name);
if VOLATILE.contains(&name) {
return Ok(None);
}
let Some(values) = values_of(plan, args)? else { return Ok(None) };
call_values(name, &values, plan.expr_type(expr), None)?
}
Expr::Case { arms, otherwise } => return case(plan, arms, otherwise),
};
Ok(Some(value))
}
fn values_of(plan: &Plan, slice: Slice) -> Result<Option<Vec<Value>>> {
let mut values = Vec::with_capacity(plan.expr_list(slice).len());
for &expr in plan.expr_list(slice) {
let Some(value) = value_of(plan, expr)? else { return Ok(None) };
values.push(value);
}
Ok(Some(values))
}
fn case(plan: &Plan, arms: Slice, otherwise: Option<ExprRef>) -> Result<Option<Value>> {
for arm in plan.arm_list(arms) {
let Some(when) = value_of(plan, arm.when)? else { return Ok(None) };
if when.as_bool() == Some(true) {
return value_of(plan, arm.then);
}
}
match otherwise {
Some(otherwise) => value_of(plan, otherwise),
None => Ok(Some(Value::Null)),
}
}
pub fn comparison(op: CompareOp) -> Comparison {
match op {
CompareOp::Equal => Comparison::Equal,
CompareOp::NotEqual => Comparison::NotEqual,
CompareOp::Less => Comparison::Less,
CompareOp::LessOrEqual => Comparison::LessOrEqual,
CompareOp::Greater => Comparison::Greater,
CompareOp::GreaterOrEqual => Comparison::GreaterOrEqual,
CompareOp::DistinctFrom => Comparison::DistinctFrom,
CompareOp::NotDistinctFrom => Comparison::NotDistinctFrom,
}
}
pub fn connective(op: ConjunctionOp) -> Connective {
match op {
ConjunctionOp::And => Connective::And,
ConjunctionOp::Or => Connective::Or,
}
}