alopex_sql/executor/evaluator/
mod.rs1pub(crate) mod binary_op;
8mod case_expr;
9mod column_ref;
10pub(crate) mod conditional;
11mod context;
12pub(crate) mod datetime;
13pub(crate) mod fts;
14mod function_call;
15pub(crate) mod hash;
16mod is_null;
17pub(crate) mod json;
18mod literal;
19pub(crate) mod nested;
20pub(crate) mod numeric;
21pub(crate) mod pattern;
22pub(crate) mod predicate;
23pub mod registry;
24pub(crate) mod string;
25mod timestamp;
26pub(crate) mod type_fn;
27mod unary_op;
28pub mod vector_ops;
29
30pub use vector_ops::{VectorError, VectorMetric, vector_distance, vector_similarity};
31
32pub use context::EvalContext;
33pub(crate) use context::begin_statement;
34pub(crate) use timestamp::{coerce_value, try_coerce_value};
35
36use crate::executor::{EvaluationError, ExecutorError, Result};
37use crate::planner::typed_expr::TypedExpr;
38use crate::planner::typed_expr::TypedExprKind;
39use crate::storage::SqlValue;
40
41pub fn evaluate(expr: &TypedExpr, ctx: &EvalContext<'_>) -> Result<SqlValue> {
43 match &expr.kind {
44 TypedExprKind::Literal(lit) => literal::eval_literal(lit, &expr.resolved_type),
45 TypedExprKind::ColumnRef { column_index, .. } => {
46 column_ref::eval_column_ref(*column_index, ctx)
47 }
48 TypedExprKind::BinaryOp { left, op, right } => {
49 binary_op::eval_binary_op(op, left, right, &expr.resolved_type, ctx)
50 }
51 TypedExprKind::UnaryOp { op, operand } => unary_op::eval_unary_op(op, operand, ctx),
52 TypedExprKind::Case {
53 operand,
54 branches,
55 else_expr,
56 } => case_expr::evaluate_case(operand.as_deref(), branches, else_expr.as_deref(), ctx),
57 TypedExprKind::IsNull { expr, negated } => is_null::eval_is_null(expr, *negated, ctx),
58 TypedExprKind::VectorLiteral(values) => {
59 Ok(SqlValue::Vector(values.iter().map(|v| *v as f32).collect()))
60 }
61 TypedExprKind::FunctionCall {
62 name,
63 args,
64 distinct,
65 star,
66 filter,
67 order_by,
68 over,
69 } => {
70 if over.is_some() {
71 return Err(ExecutorError::InvalidOperation {
72 operation: "evaluate window function as scalar expression".into(),
73 reason: "window expressions must be evaluated by the Window operator".into(),
74 });
75 }
76 if filter.is_some() || !order_by.is_empty() {
77 return Err(ExecutorError::InvalidOperation {
78 operation: "evaluate aggregate clause as scalar expression".into(),
79 reason: "FILTER and aggregate ORDER BY must be evaluated by the \
80 Aggregate operator"
81 .into(),
82 });
83 }
84 let value = function_call::evaluate_function_call(name, args, *distinct, *star, ctx)?;
85 if matches!(
86 expr.resolved_type,
87 crate::planner::ResolvedType::Array(_)
88 | crate::planner::ResolvedType::Map { .. }
89 | crate::planner::ResolvedType::Struct(_)
90 ) {
91 timestamp::coerce_value(value, &expr.resolved_type)
92 } else {
93 Ok(value)
94 }
95 }
96 TypedExprKind::Cast { expr, target_type } => {
97 timestamp::evaluate_cast(expr, target_type, ctx)
98 }
99 TypedExprKind::TryCast { expr, target_type } => {
100 timestamp::evaluate_try_cast(expr, target_type, ctx)
101 }
102 TypedExprKind::Like {
103 expr,
104 pattern,
105 escape,
106 negated,
107 kind,
108 } => pattern::evaluate_pattern(expr, pattern, escape.as_deref(), *negated, *kind, ctx),
109 TypedExprKind::Between {
110 expr,
111 low,
112 high,
113 negated,
114 } => evaluate_between(expr, low, high, *negated, ctx),
115 TypedExprKind::InList {
116 expr,
117 list,
118 negated,
119 } => evaluate_in_list(expr, list, *negated, ctx),
120 other => Err(ExecutorError::Evaluation(
122 EvaluationError::UnsupportedExpression(format!("{other:?}")),
123 )),
124 }
125}
126
127fn evaluate_between(
128 expr: &TypedExpr,
129 low: &TypedExpr,
130 high: &TypedExpr,
131 negated: bool,
132 ctx: &EvalContext<'_>,
133) -> Result<SqlValue> {
134 let value = evaluate(expr, ctx)?;
135 let lower = binary_op::eval_binary_values(
136 &crate::ast::expr::BinaryOp::GtEq,
137 value.clone(),
138 evaluate(low, ctx)?,
139 )?;
140 let upper = binary_op::eval_binary_values(
141 &crate::ast::expr::BinaryOp::LtEq,
142 value,
143 evaluate(high, ctx)?,
144 )?;
145 let result = binary_op::eval_binary_values(&crate::ast::expr::BinaryOp::And, lower, upper)?;
146 negate_predicate(result, negated)
147}
148
149fn evaluate_in_list(
150 expr: &TypedExpr,
151 list: &[TypedExpr],
152 negated: bool,
153 ctx: &EvalContext<'_>,
154) -> Result<SqlValue> {
155 let value = evaluate(expr, ctx)?;
156 let mut unknown = false;
157
158 for item in list {
159 match binary_op::eval_binary_values(
160 &crate::ast::expr::BinaryOp::Eq,
161 value.clone(),
162 evaluate(item, ctx)?,
163 )? {
164 SqlValue::Boolean(true) => return Ok(SqlValue::Boolean(!negated)),
165 SqlValue::Boolean(false) => {}
166 SqlValue::Null => unknown = true,
167 other => {
168 return Err(ExecutorError::Evaluation(EvaluationError::TypeMismatch {
169 expected: "Boolean".into(),
170 actual: other.type_name().into(),
171 }));
172 }
173 }
174 }
175
176 if unknown {
177 Ok(SqlValue::Null)
178 } else {
179 Ok(SqlValue::Boolean(negated))
180 }
181}
182
183fn negate_predicate(value: SqlValue, negated: bool) -> Result<SqlValue> {
184 if !negated {
185 return Ok(value);
186 }
187 match value {
188 SqlValue::Boolean(value) => Ok(SqlValue::Boolean(!value)),
189 SqlValue::Null => Ok(SqlValue::Null),
190 other => Err(ExecutorError::Evaluation(EvaluationError::TypeMismatch {
191 expected: "Boolean".into(),
192 actual: other.type_name().into(),
193 })),
194 }
195}