1use crate::{
20 Result,
21 stmt::{
22 BinaryOp, ConstInput, Expr, ExprArg, ExprSet, Input, Limit, Projection, Statement, Value,
23 },
24};
25use std::cmp::Ordering;
26
27enum ScopeStack<'a> {
28 Root,
29 Scope {
30 args: &'a [Value],
31 parent: &'a ScopeStack<'a>,
32 },
33}
34
35impl Statement {
36 pub fn eval(&self, mut input: impl Input) -> Result<Value> {
44 self.eval_ref(&ScopeStack::Root, &mut input)
45 }
46
47 pub fn eval_const(&self) -> Result<Value> {
49 self.eval(ConstInput::new())
50 }
51
52 fn eval_ref(&self, scope: &ScopeStack<'_>, input: &mut impl Input) -> Result<Value> {
53 match self {
54 Statement::Query(query) => {
55 if query.with.is_some() {
56 return Err(crate::Error::expression_evaluation_failed(
57 "cannot evaluate statement with WITH clause",
58 ));
59 }
60
61 if query.order_by.is_some() {
62 return Err(crate::Error::expression_evaluation_failed(
63 "cannot evaluate statement with ORDER BY clause",
64 ));
65 }
66
67 let mut result = query.body.eval_ref(scope, input)?;
68
69 if let Some(limit) = &query.limit {
70 limit.eval_ref(&mut result, scope, input)?;
71 }
72
73 if query.single {
74 let Value::List(mut items) = result else {
75 return Err(crate::Error::expression_evaluation_failed(
76 "single-row query requires body to evaluate to a list",
77 ));
78 };
79 if items.len() != 1 {
80 return Err(crate::Error::expression_evaluation_failed(
81 "single-row query did not return exactly one row",
82 ));
83 }
84 return Ok(items.remove(0));
85 }
86
87 Ok(result)
88 }
89 _ => Err(crate::Error::expression_evaluation_failed(
90 "can only evaluate Query statements",
91 )),
92 }
93 }
94}
95
96impl Limit {
97 fn eval_ref(
98 &self,
99 value: &mut Value,
100 scope: &ScopeStack<'_>,
101 input: &mut impl Input,
102 ) -> Result<()> {
103 let Value::List(items) = value else {
104 return Err(crate::Error::expression_evaluation_failed(
105 "LIMIT requires body to evaluate to a list",
106 ));
107 };
108
109 match self {
110 Limit::Cursor(_) => {
111 return Err(crate::Error::expression_evaluation_failed(
112 "cursor-based pagination cannot be evaluated client-side",
113 ));
114 }
115 Limit::Offset(limit_offset) => {
116 if let Some(offset_expr) = &limit_offset.offset {
117 let skip = offset_expr.eval_ref_usize(scope, input)?;
118 if skip >= items.len() {
119 items.clear();
120 } else {
121 items.drain(..skip);
122 }
123 }
124
125 let n = limit_offset.limit.eval_ref_usize(scope, input)?;
126 items.truncate(n);
127 }
128 }
129 Ok(())
130 }
131}
132
133impl ExprSet {
134 fn eval_ref(&self, scope: &ScopeStack<'_>, input: &mut impl Input) -> Result<Value> {
135 let ExprSet::Values(values) = self else {
136 return Err(crate::Error::expression_evaluation_failed(
137 "can only evaluate Values expressions",
138 ));
139 };
140
141 let mut ret = vec![];
142
143 for row in &values.rows {
144 ret.push(row.eval_ref(scope, input)?);
145 }
146
147 Ok(Value::List(ret))
148 }
149}
150
151impl Expr {
152 pub fn eval(&self, mut input: impl Input) -> Result<Value> {
155 self.eval_ref(&ScopeStack::Root, &mut input)
156 }
157
158 pub fn eval_bool(&self, mut input: impl Input) -> Result<bool> {
164 self.eval_ref_bool(&ScopeStack::Root, &mut input)
165 }
166
167 pub fn eval_const(&self) -> Result<Value> {
169 self.eval(ConstInput::new())
170 }
171
172 fn eval_ref(&self, scope: &ScopeStack<'_>, input: &mut impl Input) -> Result<Value> {
173 match self {
174 Expr::And(expr_and) => {
175 debug_assert!(!expr_and.operands.is_empty());
176
177 for operand in &expr_and.operands {
178 if !operand.eval_ref_bool(scope, input)? {
179 return Ok(false.into());
180 }
181 }
182
183 Ok(true.into())
184 }
185 Expr::Arg(expr_arg) => {
186 let Some(expr) = scope.resolve_arg(expr_arg, &Projection::identity(), input) else {
187 return Err(crate::Error::expression_evaluation_failed(
188 "failed to resolve argument",
189 ));
190 };
191 expr.eval_ref(scope, input)
192 }
193 Expr::BinaryOp(expr_binary_op) => {
194 let lhs = expr_binary_op.lhs.eval_ref(scope, input)?;
195 let rhs = expr_binary_op.rhs.eval_ref(scope, input)?;
196
197 match expr_binary_op.op {
198 BinaryOp::Eq => Ok((lhs == rhs).into()),
199 BinaryOp::Ne => Ok((lhs != rhs).into()),
200 BinaryOp::Ge => Ok((cmp_ordered(&lhs, &rhs)? != Ordering::Less).into()),
201 BinaryOp::Gt => Ok((cmp_ordered(&lhs, &rhs)? == Ordering::Greater).into()),
202 BinaryOp::Le => Ok((cmp_ordered(&lhs, &rhs)? != Ordering::Greater).into()),
203 BinaryOp::Lt => Ok((cmp_ordered(&lhs, &rhs)? == Ordering::Less).into()),
204 BinaryOp::Add => lhs.checked_add(&rhs).ok_or_else(|| {
205 crate::Error::expression_evaluation_failed(
206 "arithmetic overflow or type mismatch in `+`",
207 )
208 }),
209 BinaryOp::Sub => lhs.checked_sub(&rhs).ok_or_else(|| {
210 crate::Error::expression_evaluation_failed(
211 "arithmetic overflow or type mismatch in `-`",
212 )
213 }),
214 }
215 }
216 Expr::Cast(expr_cast) => expr_cast.ty.cast(expr_cast.expr.eval_ref(scope, input)?),
217 Expr::Default => Err(crate::Error::expression_evaluation_failed(
218 "DEFAULT can only be evaluated by the database",
219 )),
220 Expr::Error(expr_error) => Err(crate::Error::expression_evaluation_failed(
221 &expr_error.message,
222 )),
223 Expr::IsNull(expr_is_null) => {
224 let value = expr_is_null.expr.eval_ref(scope, input)?;
225 Ok(value.is_null().into())
226 }
227 Expr::IsVariant(_) => Err(crate::Error::expression_evaluation_failed(
228 "IsVariant must be lowered before evaluation",
229 )),
230 Expr::Let(expr_let) => {
231 let args: Vec<_> = expr_let
232 .bindings
233 .iter()
234 .map(|b| b.eval_ref(scope, input))
235 .collect::<Result<_, _>>()?;
236 let scope = scope.scope(&args);
237 expr_let.body.eval_ref(&scope, input)
238 }
239 Expr::Not(expr_not) => {
240 let value = expr_not.expr.eval_ref_bool(scope, input)?;
241 Ok((!value).into())
242 }
243 Expr::List(exprs) => {
244 let mut ret = vec![];
245
246 for expr in &exprs.items {
247 ret.push(expr.eval_ref(scope, input)?);
248 }
249
250 Ok(Value::List(ret))
251 }
252 Expr::Map(expr_map) => {
253 let mut base = expr_map.base.eval_ref(scope, input)?;
254
255 let Value::List(items) = &mut base else {
256 return Err(crate::Error::expression_evaluation_failed(
257 "Map base must evaluate to a list",
258 ));
259 };
260
261 for item in items.iter_mut() {
262 let args = [item.take()];
263 let scope = scope.scope(&args);
264 *item = expr_map.map.eval_ref(&scope, input)?;
265 }
266
267 Ok(base)
268 }
269 Expr::Project(expr_project) => match &*expr_project.base {
270 Expr::Arg(expr_arg) => {
271 let Some(expr) = scope.resolve_arg(expr_arg, &expr_project.projection, input)
272 else {
273 return Err(crate::Error::expression_evaluation_failed(
274 "failed to resolve argument",
275 ));
276 };
277
278 expr.eval_ref(scope, input)
279 }
280 Expr::Reference(expr_reference) => {
281 let Some(expr) = input.resolve_ref(expr_reference, &expr_project.projection)
282 else {
283 return Err(crate::Error::expression_evaluation_failed(
284 "failed to resolve reference",
285 ));
286 };
287
288 expr.eval_ref(scope, input)
289 }
290 _ => {
291 let base = expr_project.base.eval_ref(scope, input)?;
292 Ok(base.entry(&expr_project.projection).to_value())
293 }
294 },
295 Expr::Record(expr_record) => {
296 let mut ret = Vec::with_capacity(expr_record.len());
297
298 for expr in &expr_record.fields {
299 ret.push(expr.eval_ref(scope, input)?);
300 }
301
302 Ok(Value::record_from_vec(ret))
303 }
304 Expr::Reference(expr_reference) => {
305 let Some(expr) = input.resolve_ref(expr_reference, &Projection::identity()) else {
306 return Err(crate::Error::expression_evaluation_failed(
307 "failed to resolve reference",
308 ));
309 };
310
311 expr.eval_ref(scope, input)
312 }
313 Expr::Or(expr_or) => {
314 debug_assert!(!expr_or.operands.is_empty());
315
316 for operand in &expr_or.operands {
317 if operand.eval_ref_bool(scope, input)? {
318 return Ok(true.into());
319 }
320 }
321
322 Ok(false.into())
323 }
324 Expr::Any(expr_any) => {
325 let list = expr_any.expr.eval_ref(scope, input)?;
326
327 let Value::List(items) = list else {
328 return Err(crate::Error::expression_evaluation_failed(
329 "Any expression must evaluate to a list",
330 ));
331 };
332
333 for item in &items {
334 match item {
335 Value::Bool(true) => return Ok(true.into()),
336 Value::Bool(false) => {}
337 _ => {
338 return Err(crate::Error::expression_evaluation_failed(
339 "Any expression items must evaluate to bool",
340 ));
341 }
342 }
343 }
344
345 Ok(false.into())
346 }
347 Expr::InList(expr_in_list) => {
348 let needle = expr_in_list.expr.eval_ref(scope, input)?;
349 let list = expr_in_list.list.eval_ref(scope, input)?;
350
351 let Value::List(items) = list else {
352 return Err(crate::Error::expression_evaluation_failed(
353 "InList right-hand side must evaluate to a list",
354 ));
355 };
356
357 Ok(items.iter().any(|item| item == &needle).into())
358 }
359 Expr::AnyOp(e) => {
360 let lhs = e.lhs.eval_ref(scope, input)?;
361 let rhs = e.rhs.eval_ref(scope, input)?;
362 let Value::List(items) = rhs else {
363 return Err(crate::Error::expression_evaluation_failed(
364 "ANY right-hand side must evaluate to a list",
365 ));
366 };
367 Ok(any_all_compare(&lhs, &items, e.op, false)?.into())
368 }
369 Expr::AllOp(e) => {
370 let lhs = e.lhs.eval_ref(scope, input)?;
371 let rhs = e.rhs.eval_ref(scope, input)?;
372 let Value::List(items) = rhs else {
373 return Err(crate::Error::expression_evaluation_failed(
374 "ALL right-hand side must evaluate to a list",
375 ));
376 };
377 Ok(any_all_compare(&lhs, &items, e.op, true)?.into())
378 }
379 Expr::Match(expr_match) => {
380 let subject = expr_match.subject.eval_ref(scope, input)?;
381 for arm in &expr_match.arms {
382 if subject == arm.pattern {
383 return arm.expr.eval_ref(scope, input);
384 }
385 }
386 expr_match.else_expr.eval_ref(scope, input)
387 }
388 Expr::Exists(expr_exists) => {
389 match &expr_exists.subquery.body {
393 ExprSet::Values(values) => {
394 for row in &values.rows {
395 let val = row.eval_ref(scope, input)?;
396 match val {
397 Value::List(items) if items.is_empty() => {}
399 Value::Null => {}
401 _ => return Ok(true.into()),
403 }
404 }
405 Ok(false.into())
406 }
407 _ => todo!("ExprExists with non-Values body"),
408 }
409 }
410 Expr::Value(value) => Ok(value.clone()),
411 Expr::Func(_) => Err(crate::Error::expression_evaluation_failed(
412 "database functions cannot be evaluated client-side",
413 )),
414 _ => todo!("expr={self:#?}"),
415 }
416 }
417
418 fn eval_ref_bool(&self, scope: &ScopeStack<'_>, input: &mut impl Input) -> Result<bool> {
419 match self.eval_ref(scope, input)? {
420 Value::Bool(ret) => Ok(ret),
421 _ => Err(crate::Error::expression_evaluation_failed(
422 "expected boolean value",
423 )),
424 }
425 }
426
427 fn eval_ref_usize(&self, scope: &ScopeStack<'_>, input: &mut impl Input) -> Result<usize> {
428 match self.eval_ref(scope, input)? {
429 Value::I64(n) if n >= 0 => Ok(n as usize),
430 _ => Err(crate::Error::expression_evaluation_failed(
431 "expected non-negative integer",
432 )),
433 }
434 }
435}
436
437impl ScopeStack<'_> {
438 fn resolve_arg(
439 &self,
440 expr_arg: &ExprArg,
441 projection: &Projection,
442 input: &mut impl Input,
443 ) -> Option<Expr> {
444 let mut nesting = expr_arg.nesting;
445 let mut scope = self;
446
447 while nesting > 0 {
448 nesting -= 1;
449
450 scope = match scope {
451 ScopeStack::Root => return None,
452 ScopeStack::Scope { parent, .. } => parent,
453 };
454 }
455
456 match scope {
457 ScopeStack::Root => input.resolve_arg(expr_arg, projection),
458 &ScopeStack::Scope { mut args, .. } => args.resolve_arg(expr_arg, projection),
459 }
460 }
461
462 fn scope<'child>(&'child self, args: &'child [Value]) -> ScopeStack<'child> {
463 ScopeStack::Scope { args, parent: self }
464 }
465}
466
467fn cmp_ordered(lhs: &Value, rhs: &Value) -> Result<Ordering> {
468 if lhs.is_null() || rhs.is_null() {
469 return Err(crate::Error::expression_evaluation_failed(
470 "ordered comparison with NULL is undefined",
471 ));
472 }
473 lhs.partial_cmp(rhs).ok_or_else(|| {
474 crate::Error::expression_evaluation_failed("ordered comparison between incompatible types")
475 })
476}
477
478fn any_all_compare(lhs: &Value, items: &[Value], op: BinaryOp, all: bool) -> Result<bool> {
479 for item in items {
480 let matches = match op {
481 BinaryOp::Eq => lhs == item,
482 BinaryOp::Ne => lhs != item,
483 BinaryOp::Ge => cmp_ordered(lhs, item)? != Ordering::Less,
484 BinaryOp::Gt => cmp_ordered(lhs, item)? == Ordering::Greater,
485 BinaryOp::Le => cmp_ordered(lhs, item)? != Ordering::Greater,
486 BinaryOp::Lt => cmp_ordered(lhs, item)? == Ordering::Less,
487 BinaryOp::Add | BinaryOp::Sub => {
488 return Err(crate::Error::expression_evaluation_failed(
489 "ANY/ALL only supports comparison operators",
490 ));
491 }
492 };
493 if all {
494 if !matches {
495 return Ok(false);
496 }
497 } else if matches {
498 return Ok(true);
499 }
500 }
501 Ok(all)
503}