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