1use crate::{
2 stmt::{
3 BinaryOp, ConstInput, Expr, ExprArg, ExprSet, Input, Limit, Offset, Projection, Statement,
4 Value,
5 },
6 Result,
7};
8use std::cmp::Ordering;
9
10enum ScopeStack<'a> {
11 Root,
12 Scope {
13 args: &'a [Value],
14 parent: &'a ScopeStack<'a>,
15 },
16}
17
18impl Statement {
19 pub fn eval(&self, mut input: impl Input) -> Result<Value> {
20 self.eval_ref(&ScopeStack::Root, &mut input)
21 }
22
23 pub fn eval_const(&self) -> Result<Value> {
24 self.eval(ConstInput::new())
25 }
26
27 fn eval_ref(&self, scope: &ScopeStack<'_>, input: &mut impl Input) -> Result<Value> {
28 match self {
29 Statement::Query(query) => {
30 if query.with.is_some() {
31 return Err(crate::Error::expression_evaluation_failed(
32 "cannot evaluate statement with WITH clause",
33 ));
34 }
35
36 if query.order_by.is_some() {
37 return Err(crate::Error::expression_evaluation_failed(
38 "cannot evaluate statement with ORDER BY clause",
39 ));
40 }
41
42 let mut result = query.body.eval_ref(scope, input)?;
43
44 if let Some(limit) = &query.limit {
45 limit.eval_ref(&mut result, scope, input)?;
46 }
47
48 if query.single {
49 let Value::List(mut items) = result else {
50 return Err(crate::Error::expression_evaluation_failed(
51 "single-row query requires body to evaluate to a list",
52 ));
53 };
54 if items.len() != 1 {
55 return Err(crate::Error::expression_evaluation_failed(
56 "single-row query did not return exactly one row",
57 ));
58 }
59 return Ok(items.remove(0));
60 }
61
62 Ok(result)
63 }
64 _ => Err(crate::Error::expression_evaluation_failed(
65 "can only evaluate Query statements",
66 )),
67 }
68 }
69}
70
71impl Limit {
72 fn eval_ref(
73 &self,
74 value: &mut Value,
75 scope: &ScopeStack<'_>,
76 input: &mut impl Input,
77 ) -> Result<()> {
78 let Value::List(ref mut items) = value else {
79 return Err(crate::Error::expression_evaluation_failed(
80 "LIMIT requires body to evaluate to a list",
81 ));
82 };
83
84 if let Some(offset) = &self.offset {
85 match offset {
86 Offset::Count(offset_expr) => {
87 let skip = offset_expr.eval_ref_usize(scope, input)?;
88 if skip >= items.len() {
89 items.clear();
90 } else {
91 items.drain(..skip);
92 }
93 }
94 Offset::After(_) => {
95 return Err(crate::Error::expression_evaluation_failed(
96 "keyset-based OFFSET cannot be evaluated client-side",
97 ));
98 }
99 }
100 }
101
102 let n = self.limit.eval_ref_usize(scope, input)?;
103 items.truncate(n);
104 Ok(())
105 }
106}
107
108impl ExprSet {
109 fn eval_ref(&self, scope: &ScopeStack<'_>, input: &mut impl Input) -> Result<Value> {
110 let ExprSet::Values(values) = self else {
111 return Err(crate::Error::expression_evaluation_failed(
112 "can only evaluate Values expressions",
113 ));
114 };
115
116 let mut ret = vec![];
117
118 for row in &values.rows {
119 ret.push(row.eval_ref(scope, input)?);
120 }
121
122 Ok(Value::List(ret))
123 }
124}
125
126impl Expr {
127 pub fn eval(&self, mut input: impl Input) -> Result<Value> {
128 self.eval_ref(&ScopeStack::Root, &mut input)
129 }
130
131 pub fn eval_bool(&self, mut input: impl Input) -> Result<bool> {
132 self.eval_ref_bool(&ScopeStack::Root, &mut input)
133 }
134
135 pub fn eval_const(&self) -> Result<Value> {
136 self.eval(ConstInput::new())
137 }
138
139 fn eval_ref(&self, scope: &ScopeStack<'_>, input: &mut impl Input) -> Result<Value> {
140 match self {
141 Expr::And(expr_and) => {
142 debug_assert!(!expr_and.operands.is_empty());
143
144 for operand in &expr_and.operands {
145 if !operand.eval_ref_bool(scope, input)? {
146 return Ok(false.into());
147 }
148 }
149
150 Ok(true.into())
151 }
152 Expr::Arg(expr_arg) => {
153 let Some(expr) = scope.resolve_arg(expr_arg, &Projection::identity(), input) else {
154 return Err(crate::Error::expression_evaluation_failed(
155 "failed to resolve argument",
156 ));
157 };
158 expr.eval_ref(scope, input)
159 }
160 Expr::BinaryOp(expr_binary_op) => {
161 let lhs = expr_binary_op.lhs.eval_ref(scope, input)?;
162 let rhs = expr_binary_op.rhs.eval_ref(scope, input)?;
163
164 match expr_binary_op.op {
165 BinaryOp::Eq => Ok((lhs == rhs).into()),
166 BinaryOp::Ne => Ok((lhs != rhs).into()),
167 BinaryOp::Ge => Ok((cmp_ordered(&lhs, &rhs)? != Ordering::Less).into()),
168 BinaryOp::Gt => Ok((cmp_ordered(&lhs, &rhs)? == Ordering::Greater).into()),
169 BinaryOp::Le => Ok((cmp_ordered(&lhs, &rhs)? != Ordering::Greater).into()),
170 BinaryOp::Lt => Ok((cmp_ordered(&lhs, &rhs)? == Ordering::Less).into()),
171 }
172 }
173 Expr::Cast(expr_cast) => expr_cast.ty.cast(expr_cast.expr.eval_ref(scope, input)?),
174 Expr::Default => Err(crate::Error::expression_evaluation_failed(
175 "DEFAULT can only be evaluated by the database",
176 )),
177 Expr::Error(expr_error) => Err(crate::Error::expression_evaluation_failed(
178 &expr_error.message,
179 )),
180 Expr::IsNull(expr_is_null) => {
181 let value = expr_is_null.expr.eval_ref(scope, input)?;
182 Ok(value.is_null().into())
183 }
184 Expr::IsVariant(_) => Err(crate::Error::expression_evaluation_failed(
185 "IsVariant must be lowered before evaluation",
186 )),
187 Expr::Let(expr_let) => {
188 let args: Vec<_> = expr_let
189 .bindings
190 .iter()
191 .map(|b| b.eval_ref(scope, input))
192 .collect::<Result<_, _>>()?;
193 let scope = scope.scope(&args);
194 expr_let.body.eval_ref(&scope, input)
195 }
196 Expr::Not(expr_not) => {
197 let value = expr_not.expr.eval_ref_bool(scope, input)?;
198 Ok((!value).into())
199 }
200 Expr::List(exprs) => {
201 let mut ret = vec![];
202
203 for expr in &exprs.items {
204 ret.push(expr.eval_ref(scope, input)?);
205 }
206
207 Ok(Value::List(ret))
208 }
209 Expr::Map(expr_map) => {
210 let mut base = expr_map.base.eval_ref(scope, input)?;
211
212 let Value::List(ref mut items) = &mut base else {
213 return Err(crate::Error::expression_evaluation_failed(
214 "Map base must evaluate to a list",
215 ));
216 };
217
218 for item in items.iter_mut() {
219 let args = [item.take()];
220 let scope = scope.scope(&args);
221 *item = expr_map.map.eval_ref(&scope, input)?;
222 }
223
224 Ok(base)
225 }
226 Expr::Project(expr_project) => match &*expr_project.base {
227 Expr::Arg(expr_arg) => {
228 let Some(expr) = scope.resolve_arg(expr_arg, &expr_project.projection, input)
229 else {
230 return Err(crate::Error::expression_evaluation_failed(
231 "failed to resolve argument",
232 ));
233 };
234
235 expr.eval_ref(scope, input)
236 }
237 Expr::Reference(expr_reference) => {
238 let Some(expr) = input.resolve_ref(expr_reference, &expr_project.projection)
239 else {
240 return Err(crate::Error::expression_evaluation_failed(
241 "failed to resolve reference",
242 ));
243 };
244
245 expr.eval_ref(scope, input)
246 }
247 _ => {
248 let base = expr_project.base.eval_ref(scope, input)?;
249 Ok(base.entry(&expr_project.projection).to_value())
250 }
251 },
252 Expr::Record(expr_record) => {
253 let mut ret = Vec::with_capacity(expr_record.len());
254
255 for expr in &expr_record.fields {
256 ret.push(expr.eval_ref(scope, input)?);
257 }
258
259 Ok(Value::record_from_vec(ret))
260 }
261 Expr::Reference(expr_reference) => {
262 let Some(expr) = input.resolve_ref(expr_reference, &Projection::identity()) else {
263 return Err(crate::Error::expression_evaluation_failed(
264 "failed to resolve reference",
265 ));
266 };
267
268 expr.eval_ref(scope, input)
269 }
270 Expr::Or(expr_or) => {
271 debug_assert!(!expr_or.operands.is_empty());
272
273 for operand in &expr_or.operands {
274 if operand.eval_ref_bool(scope, input)? {
275 return Ok(true.into());
276 }
277 }
278
279 Ok(false.into())
280 }
281 Expr::Any(expr_any) => {
282 let list = expr_any.expr.eval_ref(scope, input)?;
283
284 let Value::List(items) = list else {
285 return Err(crate::Error::expression_evaluation_failed(
286 "Any expression must evaluate to a list",
287 ));
288 };
289
290 for item in &items {
291 match item {
292 Value::Bool(true) => return Ok(true.into()),
293 Value::Bool(false) => {}
294 _ => {
295 return Err(crate::Error::expression_evaluation_failed(
296 "Any expression items must evaluate to bool",
297 ))
298 }
299 }
300 }
301
302 Ok(false.into())
303 }
304 Expr::InList(expr_in_list) => {
305 let needle = expr_in_list.expr.eval_ref(scope, input)?;
306 let list = expr_in_list.list.eval_ref(scope, input)?;
307
308 let Value::List(items) = list else {
309 return Err(crate::Error::expression_evaluation_failed(
310 "InList right-hand side must evaluate to a list",
311 ));
312 };
313
314 Ok(items.iter().any(|item| item == &needle).into())
315 }
316 Expr::Match(expr_match) => {
317 let subject = expr_match.subject.eval_ref(scope, input)?;
318 for arm in &expr_match.arms {
319 if subject == arm.pattern {
320 return arm.expr.eval_ref(scope, input);
321 }
322 }
323 expr_match.else_expr.eval_ref(scope, input)
324 }
325 Expr::Value(value) => Ok(value.clone()),
326 Expr::Func(_) => Err(crate::Error::expression_evaluation_failed(
327 "database functions cannot be evaluated client-side",
328 )),
329 _ => todo!("expr={self:#?}"),
330 }
331 }
332
333 fn eval_ref_bool(&self, scope: &ScopeStack<'_>, input: &mut impl Input) -> Result<bool> {
334 match self.eval_ref(scope, input)? {
335 Value::Bool(ret) => Ok(ret),
336 _ => Err(crate::Error::expression_evaluation_failed(
337 "expected boolean value",
338 )),
339 }
340 }
341
342 fn eval_ref_usize(&self, scope: &ScopeStack<'_>, input: &mut impl Input) -> Result<usize> {
343 match self.eval_ref(scope, input)? {
344 Value::I64(n) if n >= 0 => Ok(n as usize),
345 _ => Err(crate::Error::expression_evaluation_failed(
346 "expected non-negative integer",
347 )),
348 }
349 }
350}
351
352impl ScopeStack<'_> {
353 fn resolve_arg(
354 &self,
355 expr_arg: &ExprArg,
356 projection: &Projection,
357 input: &mut impl Input,
358 ) -> Option<Expr> {
359 let mut nesting = expr_arg.nesting;
360 let mut scope = self;
361
362 while nesting > 0 {
363 nesting -= 1;
364
365 scope = match scope {
366 ScopeStack::Root => return None,
367 ScopeStack::Scope { parent, .. } => parent,
368 };
369 }
370
371 match scope {
372 ScopeStack::Root => input.resolve_arg(expr_arg, projection),
373 ScopeStack::Scope { mut args, .. } => args.resolve_arg(expr_arg, projection),
374 }
375 }
376
377 fn scope<'child>(&'child self, args: &'child [Value]) -> ScopeStack<'child> {
378 ScopeStack::Scope { args, parent: self }
379 }
380}
381
382fn cmp_ordered(lhs: &Value, rhs: &Value) -> Result<Ordering> {
383 if lhs.is_null() || rhs.is_null() {
384 return Err(crate::Error::expression_evaluation_failed(
385 "ordered comparison with NULL is undefined",
386 ));
387 }
388 lhs.partial_cmp(rhs).ok_or_else(|| {
389 crate::Error::expression_evaluation_failed("ordered comparison between incompatible types")
390 })
391}