use crate::err::Error;
use crate::exec::function::FunctionRegistry;
use crate::expr::visit::{MutVisitor, VisitMut};
use crate::expr::{BinaryOperator, Cond, Expr};
use crate::val::Number;
pub(crate) fn try_literal_to_value(
lit: &crate::expr::literal::Literal,
) -> Option<crate::val::Value> {
use crate::expr::literal::Literal;
use crate::val::Value;
match lit {
Literal::None => Some(Value::None),
Literal::Null => Some(Value::Null),
Literal::Bool(x) => Some(Value::Bool(*x)),
Literal::Float(x) => Some(Value::Number(Number::Float(*x))),
Literal::Integer(i) => Some(Value::Number(Number::Int(*i))),
Literal::Decimal(d) => Some(Value::Number(Number::Decimal(*d))),
Literal::String(s) => Some(Value::String(s.clone())),
Literal::Uuid(u) => Some(Value::Uuid(*u)),
Literal::Datetime(dt) => Some(Value::Datetime(*dt)),
Literal::Duration(d) => Some(Value::Duration(*d)),
Literal::RecordId(rid) => {
use crate::expr::RecordIdKeyLit;
let key = match &rid.key {
RecordIdKeyLit::Number(n) => crate::val::RecordIdKey::Number(*n),
RecordIdKeyLit::String(s) => crate::val::RecordIdKey::String(s.clone()),
RecordIdKeyLit::Uuid(u) => crate::val::RecordIdKey::Uuid(*u),
_ => return None,
};
Some(Value::RecordId(crate::val::RecordId::new(rid.table.clone(), key)))
}
Literal::Array(arr) => {
let values: Option<Vec<Value>> = arr.iter().map(try_expr_to_value).collect();
values.map(|v| Value::Array(v.into()))
}
Literal::Bytes(_)
| Literal::Regex(_)
| Literal::Geometry(_)
| Literal::File(_)
| Literal::Object(_)
| Literal::Set(_)
| Literal::UnboundedRange => None,
}
}
pub(crate) fn try_expr_to_value(expr: &Expr) -> Option<crate::val::Value> {
match expr {
Expr::Literal(lit) => try_literal_to_value(lit),
_ => None,
}
}
pub(crate) fn fold_condition_expressions(cond: &mut Cond, registry: &FunctionRegistry) {
let mut folder = ExpressionFolder {
registry,
};
let _ = folder.visit_mut_expr(&mut cond.0);
}
struct ExpressionFolder<'a> {
registry: &'a FunctionRegistry,
}
impl MutVisitor for ExpressionFolder<'_> {
type Error = std::convert::Infallible;
fn visit_mut_expr(&mut self, expr: &mut Expr) -> Result<(), Self::Error> {
expr.visit_mut(self)?;
if let Some(folded) = try_fold_to_literal(expr, self.registry) {
*expr = folded;
}
Ok(())
}
fn visit_mut_select(
&mut self,
_: &mut crate::expr::SelectStatement,
) -> Result<(), Self::Error> {
Ok(())
}
}
fn try_fold_to_literal(expr: &Expr, registry: &FunctionRegistry) -> Option<Expr> {
use crate::expr::Function;
use crate::val::{Datetime, Value};
match expr {
Expr::FunctionCall(fc)
if matches!(&fc.receiver, Function::Normal(name) if name == "time::now")
&& fc.arguments.is_empty() =>
{
Some(Value::Datetime(Datetime::now()).into_literal())
}
Expr::FunctionCall(fc) => {
let Function::Normal(name) = &fc.receiver else {
return None;
};
let func = registry.get(name.as_str())?;
if !func.is_pure() || func.is_async() {
return None;
}
let args: Option<Vec<Value>> = fc.arguments.iter().map(try_expr_to_value).collect();
let args = args?;
let result = func.invoke(args).ok()?;
Some(result.into_literal())
}
Expr::Binary {
op: BinaryOperator::Inside,
right,
..
} if is_empty_array_literal(right) => {
Some(Expr::Literal(crate::expr::literal::Literal::Bool(false)))
}
Expr::Binary {
left,
op,
right,
} => {
if let Some(short) = try_short_circuit_bool(left, op, right) {
return Some(short);
}
let left_val = try_expr_to_value(left)?;
let right_val = try_expr_to_value(right)?;
let result = try_eval_binary(op, left_val, right_val)?;
Some(result.into_literal())
}
_ => None,
}
}
fn is_empty_array_literal(expr: &Expr) -> bool {
matches!(expr, Expr::Literal(crate::expr::literal::Literal::Array(arr)) if arr.is_empty())
}
fn try_short_circuit_bool(left: &Expr, op: &BinaryOperator, right: &Expr) -> Option<Expr> {
use crate::expr::literal::Literal;
let l_bool = match left {
Expr::Literal(Literal::Bool(b)) => Some(*b),
_ => None,
};
let r_bool = match right {
Expr::Literal(Literal::Bool(b)) => Some(*b),
_ => None,
};
match (op, l_bool, r_bool) {
(BinaryOperator::And, Some(false), _) | (BinaryOperator::And, _, Some(false)) => {
Some(Expr::Literal(Literal::Bool(false)))
}
(BinaryOperator::And, Some(true), _) => Some(right.clone()),
(BinaryOperator::And, _, Some(true)) => Some(left.clone()),
(BinaryOperator::Or, Some(true), _) | (BinaryOperator::Or, _, Some(true)) => {
Some(Expr::Literal(Literal::Bool(true)))
}
(BinaryOperator::Or, Some(false), _) => Some(right.clone()),
(BinaryOperator::Or, _, Some(false)) => Some(left.clone()),
_ => None,
}
}
fn try_eval_binary(
op: &BinaryOperator,
left: crate::val::Value,
right: crate::val::Value,
) -> Option<crate::val::Value> {
use crate::val::{TryAdd, TrySub};
match op {
BinaryOperator::Add => left.try_add(right).ok(),
BinaryOperator::Subtract => left.try_sub(right).ok(),
_ => None,
}
}
pub(crate) fn literal_to_value(
lit: crate::expr::literal::Literal,
) -> Result<crate::val::Value, Error> {
use crate::expr::literal::Literal;
use crate::val::{Range, Value};
if let Some(value) = try_literal_to_value(&lit) {
return Ok(value);
}
match lit {
Literal::UnboundedRange => Ok(Value::Range(Box::new(Range::unbounded()))),
Literal::Bytes(b) => Ok(Value::Bytes(b)),
Literal::Regex(r) => Ok(Value::Regex(r)),
Literal::Geometry(g) => Ok(Value::Geometry(g)),
Literal::File(f) => Ok(Value::File(f)),
other => Err(Error::Internal(format!(
"Literal should be handled upstream in physical_expr(): {:?}",
std::mem::discriminant(&other)
))),
}
}
pub(crate) fn key_lit_to_expr(lit: &crate::expr::RecordIdKeyLit) -> Result<Expr, Error> {
use crate::expr::RecordIdKeyLit;
match lit {
RecordIdKeyLit::Number(n) => Ok(Expr::Literal(crate::expr::literal::Literal::Integer(*n))),
RecordIdKeyLit::String(s) => {
Ok(Expr::Literal(crate::expr::literal::Literal::String(s.clone())))
}
RecordIdKeyLit::Uuid(u) => Ok(Expr::Literal(crate::expr::literal::Literal::Uuid(*u))),
RecordIdKeyLit::Array(exprs) => {
Ok(Expr::Literal(crate::expr::literal::Literal::Array(exprs.clone())))
}
RecordIdKeyLit::Object(entries) => {
Ok(Expr::Literal(crate::expr::literal::Literal::Object(entries.clone())))
}
RecordIdKeyLit::Generate(_) => Err(Error::Query {
message: "Generated keys (rand, ulid, uuid) cannot be used in graph range bounds"
.to_string(),
}),
RecordIdKeyLit::Range(_) => Err(Error::Query {
message: "Nested range keys cannot be used in graph range bounds".to_string(),
}),
}
}