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 }
205 }
206 Expr::Cast(expr_cast) => expr_cast.ty.cast(expr_cast.expr.eval_ref(scope, input)?),
207 Expr::Default => Err(crate::Error::expression_evaluation_failed(
208 "DEFAULT can only be evaluated by the database",
209 )),
210 Expr::Error(expr_error) => Err(crate::Error::expression_evaluation_failed(
211 &expr_error.message,
212 )),
213 Expr::IsNull(expr_is_null) => {
214 let value = expr_is_null.expr.eval_ref(scope, input)?;
215 Ok(value.is_null().into())
216 }
217 Expr::IsVariant(_) => Err(crate::Error::expression_evaluation_failed(
218 "IsVariant must be lowered before evaluation",
219 )),
220 Expr::Let(expr_let) => {
221 let args: Vec<_> = expr_let
222 .bindings
223 .iter()
224 .map(|b| b.eval_ref(scope, input))
225 .collect::<Result<_, _>>()?;
226 let scope = scope.scope(&args);
227 expr_let.body.eval_ref(&scope, input)
228 }
229 Expr::Not(expr_not) => {
230 let value = expr_not.expr.eval_ref_bool(scope, input)?;
231 Ok((!value).into())
232 }
233 Expr::List(exprs) => {
234 let mut ret = vec![];
235
236 for expr in &exprs.items {
237 ret.push(expr.eval_ref(scope, input)?);
238 }
239
240 Ok(Value::List(ret))
241 }
242 Expr::Map(expr_map) => {
243 let mut base = expr_map.base.eval_ref(scope, input)?;
244
245 let Value::List(items) = &mut base else {
246 return Err(crate::Error::expression_evaluation_failed(
247 "Map base must evaluate to a list",
248 ));
249 };
250
251 for item in items.iter_mut() {
252 let args = [item.take()];
253 let scope = scope.scope(&args);
254 *item = expr_map.map.eval_ref(&scope, input)?;
255 }
256
257 Ok(base)
258 }
259 Expr::Project(expr_project) => match &*expr_project.base {
260 Expr::Arg(expr_arg) => {
261 let Some(expr) = scope.resolve_arg(expr_arg, &expr_project.projection, input)
262 else {
263 return Err(crate::Error::expression_evaluation_failed(
264 "failed to resolve argument",
265 ));
266 };
267
268 expr.eval_ref(scope, input)
269 }
270 Expr::Reference(expr_reference) => {
271 let Some(expr) = input.resolve_ref(expr_reference, &expr_project.projection)
272 else {
273 return Err(crate::Error::expression_evaluation_failed(
274 "failed to resolve reference",
275 ));
276 };
277
278 expr.eval_ref(scope, input)
279 }
280 _ => {
281 let base = expr_project.base.eval_ref(scope, input)?;
282 Ok(base.entry(&expr_project.projection).to_value())
283 }
284 },
285 Expr::Record(expr_record) => {
286 let mut ret = Vec::with_capacity(expr_record.len());
287
288 for expr in &expr_record.fields {
289 ret.push(expr.eval_ref(scope, input)?);
290 }
291
292 Ok(Value::record_from_vec(ret))
293 }
294 Expr::Reference(expr_reference) => {
295 let Some(expr) = input.resolve_ref(expr_reference, &Projection::identity()) else {
296 return Err(crate::Error::expression_evaluation_failed(
297 "failed to resolve reference",
298 ));
299 };
300
301 expr.eval_ref(scope, input)
302 }
303 Expr::Or(expr_or) => {
304 debug_assert!(!expr_or.operands.is_empty());
305
306 for operand in &expr_or.operands {
307 if operand.eval_ref_bool(scope, input)? {
308 return Ok(true.into());
309 }
310 }
311
312 Ok(false.into())
313 }
314 Expr::Any(expr_any) => {
315 let list = expr_any.expr.eval_ref(scope, input)?;
316
317 let Value::List(items) = list else {
318 return Err(crate::Error::expression_evaluation_failed(
319 "Any expression must evaluate to a list",
320 ));
321 };
322
323 for item in &items {
324 match item {
325 Value::Bool(true) => return Ok(true.into()),
326 Value::Bool(false) => {}
327 _ => {
328 return Err(crate::Error::expression_evaluation_failed(
329 "Any expression items must evaluate to bool",
330 ));
331 }
332 }
333 }
334
335 Ok(false.into())
336 }
337 Expr::InList(expr_in_list) => {
338 let needle = expr_in_list.expr.eval_ref(scope, input)?;
339 let list = expr_in_list.list.eval_ref(scope, input)?;
340
341 let Value::List(items) = list else {
342 return Err(crate::Error::expression_evaluation_failed(
343 "InList right-hand side must evaluate to a list",
344 ));
345 };
346
347 Ok(items.iter().any(|item| item == &needle).into())
348 }
349 Expr::Match(expr_match) => {
350 let subject = expr_match.subject.eval_ref(scope, input)?;
351 for arm in &expr_match.arms {
352 if subject == arm.pattern {
353 return arm.expr.eval_ref(scope, input);
354 }
355 }
356 expr_match.else_expr.eval_ref(scope, input)
357 }
358 Expr::Exists(expr_exists) => {
359 match &expr_exists.subquery.body {
363 ExprSet::Values(values) => {
364 for row in &values.rows {
365 let val = row.eval_ref(scope, input)?;
366 match val {
367 Value::List(items) if items.is_empty() => {}
369 Value::Null => {}
371 _ => return Ok(true.into()),
373 }
374 }
375 Ok(false.into())
376 }
377 _ => todo!("ExprExists with non-Values body"),
378 }
379 }
380 Expr::Value(value) => Ok(value.clone()),
381 Expr::Func(_) => Err(crate::Error::expression_evaluation_failed(
382 "database functions cannot be evaluated client-side",
383 )),
384 _ => todo!("expr={self:#?}"),
385 }
386 }
387
388 fn eval_ref_bool(&self, scope: &ScopeStack<'_>, input: &mut impl Input) -> Result<bool> {
389 match self.eval_ref(scope, input)? {
390 Value::Bool(ret) => Ok(ret),
391 _ => Err(crate::Error::expression_evaluation_failed(
392 "expected boolean value",
393 )),
394 }
395 }
396
397 fn eval_ref_usize(&self, scope: &ScopeStack<'_>, input: &mut impl Input) -> Result<usize> {
398 match self.eval_ref(scope, input)? {
399 Value::I64(n) if n >= 0 => Ok(n as usize),
400 _ => Err(crate::Error::expression_evaluation_failed(
401 "expected non-negative integer",
402 )),
403 }
404 }
405}
406
407impl ScopeStack<'_> {
408 fn resolve_arg(
409 &self,
410 expr_arg: &ExprArg,
411 projection: &Projection,
412 input: &mut impl Input,
413 ) -> Option<Expr> {
414 let mut nesting = expr_arg.nesting;
415 let mut scope = self;
416
417 while nesting > 0 {
418 nesting -= 1;
419
420 scope = match scope {
421 ScopeStack::Root => return None,
422 ScopeStack::Scope { parent, .. } => parent,
423 };
424 }
425
426 match scope {
427 ScopeStack::Root => input.resolve_arg(expr_arg, projection),
428 &ScopeStack::Scope { mut args, .. } => args.resolve_arg(expr_arg, projection),
429 }
430 }
431
432 fn scope<'child>(&'child self, args: &'child [Value]) -> ScopeStack<'child> {
433 ScopeStack::Scope { args, parent: self }
434 }
435}
436
437fn cmp_ordered(lhs: &Value, rhs: &Value) -> Result<Ordering> {
438 if lhs.is_null() || rhs.is_null() {
439 return Err(crate::Error::expression_evaluation_failed(
440 "ordered comparison with NULL is undefined",
441 ));
442 }
443 lhs.partial_cmp(rhs).ok_or_else(|| {
444 crate::Error::expression_evaluation_failed("ordered comparison between incompatible types")
445 })
446}