1use crate::error::DkitError;
2use crate::query::filter::evaluate_condition;
3use crate::query::parser::{ArithmeticOp, Expr, LiteralValue, WindowFunc};
4use crate::value::Value;
5
6pub fn evaluate_expr(row: &Value, expr: &Expr) -> Result<Value, DkitError> {
8 match expr {
9 Expr::Field(name) => match row {
10 Value::Object(map) => Ok(map.get(name).cloned().unwrap_or(Value::Null)),
11 _ => Err(DkitError::QueryError(format!(
12 "cannot access field '{}' on non-object value",
13 name
14 ))),
15 },
16 Expr::Literal(lit) => Ok(literal_to_value(lit)),
17 Expr::FuncCall { name, args } => {
18 let evaluated: Result<Vec<Value>, DkitError> =
19 args.iter().map(|a| evaluate_expr(row, a)).collect();
20 call_function(name, evaluated?)
21 }
22 Expr::BinaryOp { op, left, right } => {
23 let lv = evaluate_expr(row, left)?;
24 let rv = evaluate_expr(row, right)?;
25 evaluate_binary_op(op, &lv, &rv)
26 }
27 Expr::If {
28 condition,
29 then_expr,
30 else_expr,
31 } => {
32 if evaluate_condition(row, condition)? {
33 evaluate_expr(row, then_expr)
34 } else {
35 evaluate_expr(row, else_expr)
36 }
37 }
38 Expr::Case { branches, default } => {
39 for (condition, expr) in branches {
40 if evaluate_condition(row, condition)? {
41 return evaluate_expr(row, expr);
42 }
43 }
44 match default {
45 Some(expr) => evaluate_expr(row, expr),
46 None => Ok(Value::Null),
47 }
48 }
49 Expr::Window { .. } => Err(DkitError::QueryError(
50 "window functions can only be used in select with array input".to_string(),
51 )),
52 }
53}
54
55fn evaluate_binary_op(op: &ArithmeticOp, left: &Value, right: &Value) -> Result<Value, DkitError> {
57 if matches!(op, ArithmeticOp::Add) {
59 if let (Value::String(a), Value::String(b)) = (left, right) {
60 return Ok(Value::String(format!("{}{}", a, b)));
61 }
62 if matches!(left, Value::String(_)) || matches!(right, Value::String(_)) {
64 let a = value_to_display_string(left);
65 let b = value_to_display_string(right);
66 return Ok(Value::String(format!("{}{}", a, b)));
67 }
68 }
69
70 if matches!(left, Value::Null) || matches!(right, Value::Null) {
72 return Ok(Value::Null);
73 }
74
75 let (lf, li) = to_numeric(left)?;
77 let (rf, ri) = to_numeric(right)?;
78
79 if let (Some(a), Some(b)) = (li, ri) {
81 return match op {
82 ArithmeticOp::Add => Ok(Value::Integer(a.wrapping_add(b))),
83 ArithmeticOp::Sub => Ok(Value::Integer(a.wrapping_sub(b))),
84 ArithmeticOp::Mul => Ok(Value::Integer(a.wrapping_mul(b))),
85 ArithmeticOp::Div => {
86 if b == 0 {
87 return Err(DkitError::QueryError("division by zero".to_string()));
88 }
89 if a % b == 0 {
91 Ok(Value::Integer(a / b))
92 } else {
93 Ok(Value::Float(lf / rf))
94 }
95 }
96 };
97 }
98
99 match op {
101 ArithmeticOp::Add => Ok(Value::Float(lf + rf)),
102 ArithmeticOp::Sub => Ok(Value::Float(lf - rf)),
103 ArithmeticOp::Mul => Ok(Value::Float(lf * rf)),
104 ArithmeticOp::Div => {
105 if rf == 0.0 {
106 return Err(DkitError::QueryError("division by zero".to_string()));
107 }
108 Ok(Value::Float(lf / rf))
109 }
110 }
111}
112
113fn to_numeric(v: &Value) -> Result<(f64, Option<i64>), DkitError> {
115 match v {
116 Value::Integer(n) => Ok((*n as f64, Some(*n))),
117 Value::Float(f) => Ok((*f, None)),
118 Value::Bool(b) => {
119 let n = if *b { 1 } else { 0 };
120 Ok((n as f64, Some(n)))
121 }
122 _ => Err(DkitError::QueryError(format!(
123 "cannot perform arithmetic on {} value",
124 value_type_name(v)
125 ))),
126 }
127}
128
129fn value_to_display_string(v: &Value) -> String {
131 match v {
132 Value::String(s) => s.clone(),
133 Value::Integer(n) => n.to_string(),
134 Value::Float(f) => f.to_string(),
135 Value::Bool(b) => b.to_string(),
136 Value::Null => "null".to_string(),
137 _ => format!("{}", v),
138 }
139}
140
141pub fn expr_default_key(expr: &Expr) -> String {
143 match expr {
144 Expr::Field(f) => f.clone(),
145 Expr::Literal(_) => "value".to_string(),
146 Expr::FuncCall { name, args } => {
147 if let Some(first) = args.first() {
148 format!("{}_{}", name, expr_default_key(first))
149 } else {
150 name.clone()
151 }
152 }
153 Expr::BinaryOp { left, .. } => expr_default_key(left),
154 Expr::If { then_expr, .. } => expr_default_key(then_expr),
155 Expr::Case { branches, .. } => {
156 if let Some((_, expr)) = branches.first() {
157 expr_default_key(expr)
158 } else {
159 "case".to_string()
160 }
161 }
162 Expr::Window { func, .. } => match func {
163 WindowFunc::RowNumber => "row_number".to_string(),
164 WindowFunc::Rank => "rank".to_string(),
165 WindowFunc::DenseRank => "dense_rank".to_string(),
166 WindowFunc::Lag { expr, .. } => format!("lag_{}", expr_default_key(expr)),
167 WindowFunc::Lead { expr, .. } => format!("lead_{}", expr_default_key(expr)),
168 WindowFunc::FirstValue { expr } => format!("first_value_{}", expr_default_key(expr)),
169 WindowFunc::LastValue { expr } => format!("last_value_{}", expr_default_key(expr)),
170 WindowFunc::Aggregate { func: agg, expr } => {
171 let name = match agg {
172 crate::query::parser::AggregateFunc::Count => "count",
173 crate::query::parser::AggregateFunc::Sum => "sum",
174 crate::query::parser::AggregateFunc::Avg => "avg",
175 crate::query::parser::AggregateFunc::Min => "min",
176 crate::query::parser::AggregateFunc::Max => "max",
177 _ => "agg",
178 };
179 format!("{}_{}", name, expr_default_key(expr))
180 }
181 },
182 }
183}
184
185fn literal_to_value(lit: &LiteralValue) -> Value {
186 match lit {
187 LiteralValue::String(s) => Value::String(s.clone()),
188 LiteralValue::Integer(n) => Value::Integer(*n),
189 LiteralValue::Float(f) => Value::Float(*f),
190 LiteralValue::Bool(b) => Value::Bool(*b),
191 LiteralValue::Null => Value::Null,
192 LiteralValue::List(items) => Value::Array(items.iter().map(literal_to_value).collect()),
193 }
194}
195
196fn call_function(name: &str, args: Vec<Value>) -> Result<Value, DkitError> {
198 match name {
199 "upper" => {
201 let s = require_one_string(name, &args)?;
202 Ok(Value::String(s.to_uppercase()))
203 }
204 "lower" => {
205 let s = require_one_string(name, &args)?;
206 Ok(Value::String(s.to_lowercase()))
207 }
208 "trim" => {
209 let s = require_one_string(name, &args)?;
210 Ok(Value::String(s.trim().to_string()))
211 }
212 "ltrim" => {
213 let s = require_one_string(name, &args)?;
214 Ok(Value::String(s.trim_start().to_string()))
215 }
216 "rtrim" => {
217 let s = require_one_string(name, &args)?;
218 Ok(Value::String(s.trim_end().to_string()))
219 }
220 "length" => match args.as_slice() {
221 [Value::String(s)] => Ok(Value::Integer(s.chars().count() as i64)),
222 [Value::Array(a)] => Ok(Value::Integer(a.len() as i64)),
223 [Value::Null] => Ok(Value::Integer(0)),
224 [v] => Err(DkitError::QueryError(format!(
225 "length() requires a string or array argument, got {}",
226 value_type_name(v)
227 ))),
228 _ => Err(DkitError::QueryError(format!(
229 "length() takes 1 argument, got {}",
230 args.len()
231 ))),
232 },
233 "substr" => {
234 if args.len() < 2 || args.len() > 3 {
235 return Err(DkitError::QueryError(format!(
236 "substr() takes 2 or 3 arguments, got {}",
237 args.len()
238 )));
239 }
240 let s = match &args[0] {
241 Value::String(s) => s.clone(),
242 Value::Null => return Ok(Value::Null),
243 v => {
244 return Err(DkitError::QueryError(format!(
245 "substr() first argument must be a string, got {}",
246 value_type_name(v)
247 )))
248 }
249 };
250 let start = require_integer_arg("substr", &args[1], "start")? as usize;
251 let chars: Vec<char> = s.chars().collect();
252 let start = start.min(chars.len());
253 if args.len() == 3 {
254 let len = require_integer_arg("substr", &args[2], "length")? as usize;
255 let end = (start + len).min(chars.len());
256 Ok(Value::String(chars[start..end].iter().collect()))
257 } else {
258 Ok(Value::String(chars[start..].iter().collect()))
259 }
260 }
261 "concat" => {
262 if args.is_empty() {
263 return Ok(Value::String(String::new()));
264 }
265 let mut result = String::new();
266 for arg in &args {
267 match arg {
268 Value::String(s) => result.push_str(s),
269 Value::Null => {}
270 v => result.push_str(&v.to_string()),
271 }
272 }
273 Ok(Value::String(result))
274 }
275 "replace" => {
276 if args.len() != 3 {
277 return Err(DkitError::QueryError(format!(
278 "replace() takes 3 arguments (string, from, to), got {}",
279 args.len()
280 )));
281 }
282 let s = match &args[0] {
283 Value::String(s) => s.clone(),
284 Value::Null => return Ok(Value::Null),
285 v => {
286 return Err(DkitError::QueryError(format!(
287 "replace() first argument must be a string, got {}",
288 value_type_name(v)
289 )))
290 }
291 };
292 let from = match &args[1] {
293 Value::String(s) => s.clone(),
294 v => {
295 return Err(DkitError::QueryError(format!(
296 "replace() second argument must be a string, got {}",
297 value_type_name(v)
298 )))
299 }
300 };
301 let to = match &args[2] {
302 Value::String(s) => s.clone(),
303 v => {
304 return Err(DkitError::QueryError(format!(
305 "replace() third argument must be a string, got {}",
306 value_type_name(v)
307 )))
308 }
309 };
310 Ok(Value::String(s.replace(&*from, &to)))
311 }
312 "split" => {
313 if args.len() != 2 {
314 return Err(DkitError::QueryError(format!(
315 "split() takes 2 arguments (string, separator), got {}",
316 args.len()
317 )));
318 }
319 let s = match &args[0] {
320 Value::String(s) => s.clone(),
321 Value::Null => return Ok(Value::Array(vec![])),
322 v => {
323 return Err(DkitError::QueryError(format!(
324 "split() first argument must be a string, got {}",
325 value_type_name(v)
326 )))
327 }
328 };
329 let sep = match &args[1] {
330 Value::String(s) => s.clone(),
331 v => {
332 return Err(DkitError::QueryError(format!(
333 "split() second argument must be a string, got {}",
334 value_type_name(v)
335 )))
336 }
337 };
338 Ok(Value::Array(
339 s.split(&*sep)
340 .map(|p| Value::String(p.to_string()))
341 .collect(),
342 ))
343 }
344
345 "index_of" => {
346 if args.len() != 2 {
347 return Err(DkitError::QueryError(format!(
348 "index_of() takes 2 arguments (string, substr), got {}",
349 args.len()
350 )));
351 }
352 let s = match &args[0] {
353 Value::String(s) => s.clone(),
354 Value::Null => return Ok(Value::Integer(-1)),
355 v => {
356 return Err(DkitError::QueryError(format!(
357 "index_of() first argument must be a string, got {}",
358 value_type_name(v)
359 )))
360 }
361 };
362 let substr = match &args[1] {
363 Value::String(s) => s.clone(),
364 v => {
365 return Err(DkitError::QueryError(format!(
366 "index_of() second argument must be a string, got {}",
367 value_type_name(v)
368 )))
369 }
370 };
371 match s.find(&*substr) {
372 Some(pos) => {
373 let char_pos = s[..pos].chars().count() as i64;
374 Ok(Value::Integer(char_pos))
375 }
376 None => Ok(Value::Integer(-1)),
377 }
378 }
379 "rindex_of" => {
380 if args.len() != 2 {
381 return Err(DkitError::QueryError(format!(
382 "rindex_of() takes 2 arguments (string, substr), got {}",
383 args.len()
384 )));
385 }
386 let s = match &args[0] {
387 Value::String(s) => s.clone(),
388 Value::Null => return Ok(Value::Integer(-1)),
389 v => {
390 return Err(DkitError::QueryError(format!(
391 "rindex_of() first argument must be a string, got {}",
392 value_type_name(v)
393 )))
394 }
395 };
396 let substr = match &args[1] {
397 Value::String(s) => s.clone(),
398 v => {
399 return Err(DkitError::QueryError(format!(
400 "rindex_of() second argument must be a string, got {}",
401 value_type_name(v)
402 )))
403 }
404 };
405 match s.rfind(&*substr) {
406 Some(pos) => {
407 let char_pos = s[..pos].chars().count() as i64;
408 Ok(Value::Integer(char_pos))
409 }
410 None => Ok(Value::Integer(-1)),
411 }
412 }
413 "starts_with" => {
414 if args.len() != 2 {
415 return Err(DkitError::QueryError(format!(
416 "starts_with() takes 2 arguments (string, prefix), got {}",
417 args.len()
418 )));
419 }
420 let s = match &args[0] {
421 Value::String(s) => s.clone(),
422 Value::Null => return Ok(Value::Bool(false)),
423 v => {
424 return Err(DkitError::QueryError(format!(
425 "starts_with() first argument must be a string, got {}",
426 value_type_name(v)
427 )))
428 }
429 };
430 let prefix = match &args[1] {
431 Value::String(s) => s.clone(),
432 v => {
433 return Err(DkitError::QueryError(format!(
434 "starts_with() second argument must be a string, got {}",
435 value_type_name(v)
436 )))
437 }
438 };
439 Ok(Value::Bool(s.starts_with(&*prefix)))
440 }
441 "ends_with" => {
442 if args.len() != 2 {
443 return Err(DkitError::QueryError(format!(
444 "ends_with() takes 2 arguments (string, suffix), got {}",
445 args.len()
446 )));
447 }
448 let s = match &args[0] {
449 Value::String(s) => s.clone(),
450 Value::Null => return Ok(Value::Bool(false)),
451 v => {
452 return Err(DkitError::QueryError(format!(
453 "ends_with() first argument must be a string, got {}",
454 value_type_name(v)
455 )))
456 }
457 };
458 let suffix = match &args[1] {
459 Value::String(s) => s.clone(),
460 v => {
461 return Err(DkitError::QueryError(format!(
462 "ends_with() second argument must be a string, got {}",
463 value_type_name(v)
464 )))
465 }
466 };
467 Ok(Value::Bool(s.ends_with(&*suffix)))
468 }
469 "reverse" => {
470 let s = require_one_string(name, &args)?;
471 Ok(Value::String(s.chars().rev().collect()))
472 }
473 "repeat" => {
474 if args.len() != 2 {
475 return Err(DkitError::QueryError(format!(
476 "repeat() takes 2 arguments (string, count), got {}",
477 args.len()
478 )));
479 }
480 let s = match &args[0] {
481 Value::String(s) => s.clone(),
482 Value::Null => return Ok(Value::Null),
483 v => {
484 return Err(DkitError::QueryError(format!(
485 "repeat() first argument must be a string, got {}",
486 value_type_name(v)
487 )))
488 }
489 };
490 let n = require_integer_arg("repeat", &args[1], "count")?;
491 if n < 0 {
492 return Err(DkitError::QueryError(
493 "repeat() count must be non-negative".to_string(),
494 ));
495 }
496 Ok(Value::String(s.repeat(n as usize)))
497 }
498 "pad_left" => {
499 if args.len() != 3 {
500 return Err(DkitError::QueryError(format!(
501 "pad_left() takes 3 arguments (string, width, char), got {}",
502 args.len()
503 )));
504 }
505 let s = match &args[0] {
506 Value::String(s) => s.clone(),
507 Value::Null => return Ok(Value::Null),
508 v => {
509 return Err(DkitError::QueryError(format!(
510 "pad_left() first argument must be a string, got {}",
511 value_type_name(v)
512 )))
513 }
514 };
515 let width = require_integer_arg("pad_left", &args[1], "width")? as usize;
516 let pad_char = match &args[2] {
517 Value::String(s) => {
518 let mut chars = s.chars();
519 match (chars.next(), chars.next()) {
520 (Some(c), None) => c,
521 _ => {
522 return Err(DkitError::QueryError(
523 "pad_left() third argument must be a single character".to_string(),
524 ))
525 }
526 }
527 }
528 v => {
529 return Err(DkitError::QueryError(format!(
530 "pad_left() third argument must be a string, got {}",
531 value_type_name(v)
532 )))
533 }
534 };
535 let char_count = s.chars().count();
536 if char_count >= width {
537 Ok(Value::String(s))
538 } else {
539 let padding: String = std::iter::repeat(pad_char)
540 .take(width - char_count)
541 .collect();
542 Ok(Value::String(format!("{}{}", padding, s)))
543 }
544 }
545 "pad_right" => {
546 if args.len() != 3 {
547 return Err(DkitError::QueryError(format!(
548 "pad_right() takes 3 arguments (string, width, char), got {}",
549 args.len()
550 )));
551 }
552 let s = match &args[0] {
553 Value::String(s) => s.clone(),
554 Value::Null => return Ok(Value::Null),
555 v => {
556 return Err(DkitError::QueryError(format!(
557 "pad_right() first argument must be a string, got {}",
558 value_type_name(v)
559 )))
560 }
561 };
562 let width = require_integer_arg("pad_right", &args[1], "width")? as usize;
563 let pad_char = match &args[2] {
564 Value::String(s) => {
565 let mut chars = s.chars();
566 match (chars.next(), chars.next()) {
567 (Some(c), None) => c,
568 _ => {
569 return Err(DkitError::QueryError(
570 "pad_right() third argument must be a single character".to_string(),
571 ))
572 }
573 }
574 }
575 v => {
576 return Err(DkitError::QueryError(format!(
577 "pad_right() third argument must be a string, got {}",
578 value_type_name(v)
579 )))
580 }
581 };
582 let char_count = s.chars().count();
583 if char_count >= width {
584 Ok(Value::String(s))
585 } else {
586 let padding: String = std::iter::repeat(pad_char)
587 .take(width - char_count)
588 .collect();
589 Ok(Value::String(format!("{}{}", s, padding)))
590 }
591 }
592
593 "round" => {
595 let n = require_numeric_arg(name, &args)?;
596 if args.len() == 2 {
597 let decimals = require_integer_arg("round", &args[1], "decimals")?;
598 let factor = 10_f64.powi(decimals as i32);
599 Ok(Value::Float((n * factor).round() / factor))
600 } else if args.len() == 1 {
601 Ok(Value::Integer(n.round() as i64))
602 } else {
603 Err(DkitError::QueryError(format!(
604 "round() takes 1 or 2 arguments, got {}",
605 args.len()
606 )))
607 }
608 }
609 "ceil" => {
610 let n = require_numeric_arg(name, &args)?;
611 Ok(Value::Integer(n.ceil() as i64))
612 }
613 "floor" => {
614 let n = require_numeric_arg(name, &args)?;
615 Ok(Value::Integer(n.floor() as i64))
616 }
617 "abs" => match args.as_slice() {
618 [Value::Integer(n)] => Ok(Value::Integer(n.abs())),
619 [Value::Float(f)] => Ok(Value::Float(f.abs())),
620 [Value::Null] => Ok(Value::Null),
621 [v] => Err(DkitError::QueryError(format!(
622 "abs() requires a numeric argument, got {}",
623 value_type_name(v)
624 ))),
625 _ => Err(DkitError::QueryError(format!(
626 "abs() takes 1 argument, got {}",
627 args.len()
628 ))),
629 },
630 "sqrt" => {
631 let n = require_numeric_arg(name, &args)?;
632 if n < 0.0 {
633 return Err(DkitError::QueryError(
634 "sqrt() requires a non-negative argument".to_string(),
635 ));
636 }
637 Ok(Value::Float(n.sqrt()))
638 }
639 "pow" => {
640 if args.len() != 2 {
641 return Err(DkitError::QueryError(format!(
642 "pow() takes 2 arguments, got {}",
643 args.len()
644 )));
645 }
646 let base = require_numeric_arg("pow base", &args[..1])?;
647 let exp = require_numeric_arg("pow exp", &args[1..])?;
648 Ok(Value::Float(base.powf(exp)))
649 }
650
651 "now" => {
653 if !args.is_empty() {
654 return Err(DkitError::QueryError(
655 "now() takes no arguments".to_string(),
656 ));
657 }
658 Ok(Value::String(current_datetime_utc()))
659 }
660 "date" => {
661 let s = require_one_string(name, &args)?;
662 let normalized = normalize_date_str(&s)?;
664 Ok(Value::String(normalized))
665 }
666 "year" => {
667 let s = require_one_string(name, &args)?;
668 let y = extract_year(&s)?;
669 Ok(Value::Integer(y))
670 }
671 "month" => {
672 let s = require_one_string(name, &args)?;
673 let m = extract_month(&s)?;
674 Ok(Value::Integer(m))
675 }
676 "day" => {
677 let s = require_one_string(name, &args)?;
678 let d = extract_day(&s)?;
679 Ok(Value::Integer(d))
680 }
681
682 "to_int" | "int" => match args.as_slice() {
684 [Value::Integer(n)] => Ok(Value::Integer(*n)),
685 [Value::Float(f)] => Ok(Value::Integer(*f as i64)),
686 [Value::String(s)] => s.trim().parse::<i64>().map(Value::Integer).map_err(|_| {
687 DkitError::QueryError(format!("to_int(): cannot parse '{}' as integer", s))
688 }),
689 [Value::Bool(b)] => Ok(Value::Integer(if *b { 1 } else { 0 })),
690 [Value::Null] => Ok(Value::Null),
691 [v] => Err(DkitError::QueryError(format!(
692 "to_int() cannot convert {}",
693 value_type_name(v)
694 ))),
695 _ => Err(DkitError::QueryError(format!(
696 "to_int() takes 1 argument, got {}",
697 args.len()
698 ))),
699 },
700 "to_float" | "float" => match args.as_slice() {
701 [Value::Float(f)] => Ok(Value::Float(*f)),
702 [Value::Integer(n)] => Ok(Value::Float(*n as f64)),
703 [Value::String(s)] => s.trim().parse::<f64>().map(Value::Float).map_err(|_| {
704 DkitError::QueryError(format!("to_float(): cannot parse '{}' as float", s))
705 }),
706 [Value::Bool(b)] => Ok(Value::Float(if *b { 1.0 } else { 0.0 })),
707 [Value::Null] => Ok(Value::Null),
708 [v] => Err(DkitError::QueryError(format!(
709 "to_float() cannot convert {}",
710 value_type_name(v)
711 ))),
712 _ => Err(DkitError::QueryError(format!(
713 "to_float() takes 1 argument, got {}",
714 args.len()
715 ))),
716 },
717 "to_string" | "str" => match args.as_slice() {
718 [Value::String(s)] => Ok(Value::String(s.clone())),
719 [Value::Integer(n)] => Ok(Value::String(n.to_string())),
720 [Value::Float(f)] => Ok(Value::String(f.to_string())),
721 [Value::Bool(b)] => Ok(Value::String(b.to_string())),
722 [Value::Null] => Ok(Value::String("null".to_string())),
723 [v] => Ok(Value::String(v.to_string())),
724 _ => Err(DkitError::QueryError(format!(
725 "to_string() takes 1 argument, got {}",
726 args.len()
727 ))),
728 },
729 "to_bool" | "bool" => match args.as_slice() {
730 [Value::Bool(b)] => Ok(Value::Bool(*b)),
731 [Value::Integer(n)] => Ok(Value::Bool(*n != 0)),
732 [Value::Float(f)] => Ok(Value::Bool(*f != 0.0)),
733 [Value::String(s)] => match s.trim().to_lowercase().as_str() {
734 "true" | "yes" | "1" | "on" => Ok(Value::Bool(true)),
735 "false" | "no" | "0" | "off" | "" => Ok(Value::Bool(false)),
736 _ => Err(DkitError::QueryError(format!(
737 "to_bool(): cannot parse '{}' as boolean",
738 s
739 ))),
740 },
741 [Value::Null] => Ok(Value::Bool(false)),
742 [v] => Err(DkitError::QueryError(format!(
743 "to_bool() cannot convert {}",
744 value_type_name(v)
745 ))),
746 _ => Err(DkitError::QueryError(format!(
747 "to_bool() takes 1 argument, got {}",
748 args.len()
749 ))),
750 },
751
752 "coalesce" => {
754 for arg in &args {
755 if !matches!(arg, Value::Null) {
756 return Ok(arg.clone());
757 }
758 }
759 Ok(Value::Null)
760 }
761 "if_null" => match args.as_slice() {
762 [Value::Null, default] => Ok(default.clone()),
763 [v, _] => Ok(v.clone()),
764 _ => Err(DkitError::QueryError(format!(
765 "if_null() takes 2 arguments, got {}",
766 args.len()
767 ))),
768 },
769
770 _ => Err(DkitError::QueryError(format!(
771 "unknown function '{}'",
772 name
773 ))),
774 }
775}
776
777fn require_one_string(func: &str, args: &[Value]) -> Result<String, DkitError> {
780 match args {
781 [Value::String(s)] => Ok(s.clone()),
782 [Value::Null] => Err(DkitError::QueryError(format!(
783 "{}() argument is null",
784 func
785 ))),
786 [v] => Err(DkitError::QueryError(format!(
787 "{}() requires a string argument, got {}",
788 func,
789 value_type_name(v)
790 ))),
791 _ => Err(DkitError::QueryError(format!(
792 "{}() takes 1 argument, got {}",
793 func,
794 args.len()
795 ))),
796 }
797}
798
799fn require_numeric_arg(func: &str, args: &[Value]) -> Result<f64, DkitError> {
800 match args.first() {
801 Some(Value::Float(f)) => Ok(*f),
802 Some(Value::Integer(n)) => Ok(*n as f64),
803 Some(Value::Null) => Err(DkitError::QueryError(format!(
804 "{}() argument is null",
805 func
806 ))),
807 Some(v) => Err(DkitError::QueryError(format!(
808 "{}() requires a numeric argument, got {}",
809 func,
810 value_type_name(v)
811 ))),
812 None => Err(DkitError::QueryError(format!(
813 "{}() takes at least 1 argument",
814 func
815 ))),
816 }
817}
818
819fn require_integer_arg(func: &str, val: &Value, param: &str) -> Result<i64, DkitError> {
820 match val {
821 Value::Integer(n) => Ok(*n),
822 Value::Float(f) => Ok(*f as i64),
823 v => Err(DkitError::QueryError(format!(
824 "{}() '{}' argument must be an integer, got {}",
825 func,
826 param,
827 value_type_name(v)
828 ))),
829 }
830}
831
832fn value_type_name(v: &Value) -> &'static str {
833 match v {
834 Value::Null => "null",
835 Value::Bool(_) => "bool",
836 Value::Integer(_) => "integer",
837 Value::Float(_) => "float",
838 Value::String(_) => "string",
839 Value::Array(_) => "array",
840 Value::Object(_) => "object",
841 }
842}
843
844fn extract_year(s: &str) -> Result<i64, DkitError> {
848 let date_part = s.split('T').next().unwrap_or(s);
849 let parts: Vec<&str> = date_part.split('-').collect();
850 if parts.is_empty() {
851 return Err(DkitError::QueryError(format!(
852 "year(): cannot parse date from '{}'",
853 s
854 )));
855 }
856 parts[0]
857 .parse::<i64>()
858 .map_err(|_| DkitError::QueryError(format!("year(): cannot parse year from '{}'", s)))
859}
860
861fn extract_month(s: &str) -> Result<i64, DkitError> {
863 let date_part = s.split('T').next().unwrap_or(s);
864 let parts: Vec<&str> = date_part.split('-').collect();
865 if parts.len() < 2 {
866 return Err(DkitError::QueryError(format!(
867 "month(): cannot parse month from '{}'",
868 s
869 )));
870 }
871 parts[1]
872 .parse::<i64>()
873 .map_err(|_| DkitError::QueryError(format!("month(): cannot parse month from '{}'", s)))
874}
875
876fn extract_day(s: &str) -> Result<i64, DkitError> {
878 let date_part = s.split('T').next().unwrap_or(s);
879 let parts: Vec<&str> = date_part.split('-').collect();
880 if parts.len() < 3 {
881 return Err(DkitError::QueryError(format!(
882 "day(): cannot parse day from '{}'",
883 s
884 )));
885 }
886 let day_str = parts[2].split(&['T', ' '][..]).next().unwrap_or(parts[2]);
888 day_str
889 .parse::<i64>()
890 .map_err(|_| DkitError::QueryError(format!("day(): cannot parse day from '{}'", s)))
891}
892
893fn normalize_date_str(s: &str) -> Result<String, DkitError> {
895 let date_part = s.split('T').next().unwrap_or(s).trim();
896 let parts: Vec<&str> = date_part.split('-').collect();
897 if parts.len() != 3 {
898 return Err(DkitError::QueryError(format!(
899 "date(): expected yyyy-MM-dd format, got '{}'",
900 s
901 )));
902 }
903 let year = parts[0]
904 .parse::<i32>()
905 .map_err(|_| DkitError::QueryError(format!("date(): invalid year in '{}'", s)))?;
906 let month = parts[1]
907 .parse::<u32>()
908 .map_err(|_| DkitError::QueryError(format!("date(): invalid month in '{}'", s)))?;
909 let day = parts[2]
910 .split(&[' ', 'T'][..])
911 .next()
912 .unwrap_or(parts[2])
913 .parse::<u32>()
914 .map_err(|_| DkitError::QueryError(format!("date(): invalid day in '{}'", s)))?;
915 if !(1..=12).contains(&month) {
916 return Err(DkitError::QueryError(format!(
917 "date(): month {} out of range in '{}'",
918 month, s
919 )));
920 }
921 if !(1..=31).contains(&day) {
922 return Err(DkitError::QueryError(format!(
923 "date(): day {} out of range in '{}'",
924 day, s
925 )));
926 }
927 Ok(format!("{:04}-{:02}-{:02}", year, month, day))
928}
929
930fn current_datetime_utc() -> String {
932 use std::time::{SystemTime, UNIX_EPOCH};
933 let secs = SystemTime::now()
934 .duration_since(UNIX_EPOCH)
935 .unwrap_or_default()
936 .as_secs();
937 let days_since_epoch = secs / 86400;
939 let time_of_day = secs % 86400;
940 let (year, month, day) = days_to_ymd(days_since_epoch);
941 let hour = time_of_day / 3600;
942 let minute = (time_of_day % 3600) / 60;
943 let second = time_of_day % 60;
944 format!(
945 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
946 year, month, day, hour, minute, second
947 )
948}
949
950fn days_to_ymd(days: u64) -> (i64, u64, u64) {
952 let z = days as i64 + 719468;
954 let era = if z >= 0 { z } else { z - 146096 } / 146097;
955 let doe = z - era * 146097;
956 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
957 let y = yoe + era * 400;
958 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
959 let mp = (5 * doy + 2) / 153;
960 let d = doy - (153 * mp + 2) / 5 + 1;
961 let m = if mp < 10 { mp + 3 } else { mp - 9 };
962 let y = if m <= 2 { y + 1 } else { y };
963 (y, m as u64, d as u64)
964}
965
966#[cfg(test)]
967mod tests {
968 use super::*;
969 use crate::query::parser::Expr;
970 use indexmap::IndexMap;
971
972 fn obj(fields: &[(&str, Value)]) -> Value {
973 let mut map = IndexMap::new();
974 for (k, v) in fields {
975 map.insert(k.to_string(), v.clone());
976 }
977 Value::Object(map)
978 }
979
980 fn eval(row: &Value, expr: &Expr) -> Result<Value, DkitError> {
981 evaluate_expr(row, expr)
982 }
983
984 fn func(name: &str, args: Vec<Expr>) -> Expr {
985 Expr::FuncCall {
986 name: name.to_string(),
987 args,
988 }
989 }
990
991 fn field(name: &str) -> Expr {
992 Expr::Field(name.to_string())
993 }
994
995 fn lit_str(s: &str) -> Expr {
996 Expr::Literal(LiteralValue::String(s.to_string()))
997 }
998
999 fn lit_int(n: i64) -> Expr {
1000 Expr::Literal(LiteralValue::Integer(n))
1001 }
1002
1003 #[test]
1006 fn test_upper() {
1007 let row = obj(&[("name", Value::String("hello".to_string()))]);
1008 let result = eval(&row, &func("upper", vec![field("name")])).unwrap();
1009 assert_eq!(result, Value::String("HELLO".to_string()));
1010 }
1011
1012 #[test]
1013 fn test_lower() {
1014 let row = obj(&[("name", Value::String("WORLD".to_string()))]);
1015 let result = eval(&row, &func("lower", vec![field("name")])).unwrap();
1016 assert_eq!(result, Value::String("world".to_string()));
1017 }
1018
1019 #[test]
1020 fn test_trim() {
1021 let row = obj(&[("name", Value::String(" hello ".to_string()))]);
1022 let result = eval(&row, &func("trim", vec![field("name")])).unwrap();
1023 assert_eq!(result, Value::String("hello".to_string()));
1024 }
1025
1026 #[test]
1027 fn test_length_string() {
1028 let row = obj(&[("name", Value::String("hello".to_string()))]);
1029 let result = eval(&row, &func("length", vec![field("name")])).unwrap();
1030 assert_eq!(result, Value::Integer(5));
1031 }
1032
1033 #[test]
1034 fn test_substr() {
1035 let row = obj(&[("name", Value::String("hello world".to_string()))]);
1036 let result = eval(
1037 &row,
1038 &func("substr", vec![field("name"), lit_int(0), lit_int(5)]),
1039 )
1040 .unwrap();
1041 assert_eq!(result, Value::String("hello".to_string()));
1042 }
1043
1044 #[test]
1045 fn test_concat() {
1046 let row = obj(&[
1047 ("first", Value::String("hello".to_string())),
1048 ("last", Value::String(" world".to_string())),
1049 ]);
1050 let result = eval(&row, &func("concat", vec![field("first"), field("last")])).unwrap();
1051 assert_eq!(result, Value::String("hello world".to_string()));
1052 }
1053
1054 #[test]
1055 fn test_replace() {
1056 let row = obj(&[("name", Value::String("hello world".to_string()))]);
1057 let result = eval(
1058 &row,
1059 &func(
1060 "replace",
1061 vec![field("name"), lit_str("world"), lit_str("dkit")],
1062 ),
1063 )
1064 .unwrap();
1065 assert_eq!(result, Value::String("hello dkit".to_string()));
1066 }
1067
1068 #[test]
1069 fn test_index_of() {
1070 let row = obj(&[("email", Value::String("user@example.com".to_string()))]);
1071 let result = eval(&row, &func("index_of", vec![field("email"), lit_str("@")])).unwrap();
1072 assert_eq!(result, Value::Integer(4));
1073 }
1074
1075 #[test]
1076 fn test_index_of_not_found() {
1077 let row = obj(&[("s", Value::String("hello".to_string()))]);
1078 let result = eval(&row, &func("index_of", vec![field("s"), lit_str("xyz")])).unwrap();
1079 assert_eq!(result, Value::Integer(-1));
1080 }
1081
1082 #[test]
1083 fn test_rindex_of() {
1084 let row = obj(&[("s", Value::String("abcabc".to_string()))]);
1085 let result = eval(&row, &func("rindex_of", vec![field("s"), lit_str("abc")])).unwrap();
1086 assert_eq!(result, Value::Integer(3));
1087 }
1088
1089 #[test]
1090 fn test_rindex_of_not_found() {
1091 let row = obj(&[("s", Value::String("hello".to_string()))]);
1092 let result = eval(&row, &func("rindex_of", vec![field("s"), lit_str("xyz")])).unwrap();
1093 assert_eq!(result, Value::Integer(-1));
1094 }
1095
1096 #[test]
1097 fn test_starts_with() {
1098 let row = obj(&[("name", Value::String("Dr. Smith".to_string()))]);
1099 let result = eval(
1100 &row,
1101 &func("starts_with", vec![field("name"), lit_str("Dr.")]),
1102 )
1103 .unwrap();
1104 assert_eq!(result, Value::Bool(true));
1105 }
1106
1107 #[test]
1108 fn test_starts_with_false() {
1109 let row = obj(&[("name", Value::String("Mr. Smith".to_string()))]);
1110 let result = eval(
1111 &row,
1112 &func("starts_with", vec![field("name"), lit_str("Dr.")]),
1113 )
1114 .unwrap();
1115 assert_eq!(result, Value::Bool(false));
1116 }
1117
1118 #[test]
1119 fn test_ends_with() {
1120 let row = obj(&[("file", Value::String("data.json".to_string()))]);
1121 let result = eval(
1122 &row,
1123 &func("ends_with", vec![field("file"), lit_str(".json")]),
1124 )
1125 .unwrap();
1126 assert_eq!(result, Value::Bool(true));
1127 }
1128
1129 #[test]
1130 fn test_ends_with_false() {
1131 let row = obj(&[("file", Value::String("data.csv".to_string()))]);
1132 let result = eval(
1133 &row,
1134 &func("ends_with", vec![field("file"), lit_str(".json")]),
1135 )
1136 .unwrap();
1137 assert_eq!(result, Value::Bool(false));
1138 }
1139
1140 #[test]
1141 fn test_reverse() {
1142 let row = obj(&[("s", Value::String("hello".to_string()))]);
1143 let result = eval(&row, &func("reverse", vec![field("s")])).unwrap();
1144 assert_eq!(result, Value::String("olleh".to_string()));
1145 }
1146
1147 #[test]
1148 fn test_repeat() {
1149 let row = obj(&[("s", Value::String("ab".to_string()))]);
1150 let result = eval(&row, &func("repeat", vec![field("s"), lit_int(3)])).unwrap();
1151 assert_eq!(result, Value::String("ababab".to_string()));
1152 }
1153
1154 #[test]
1155 fn test_repeat_zero() {
1156 let row = obj(&[("s", Value::String("ab".to_string()))]);
1157 let result = eval(&row, &func("repeat", vec![field("s"), lit_int(0)])).unwrap();
1158 assert_eq!(result, Value::String(String::new()));
1159 }
1160
1161 #[test]
1162 fn test_pad_left() {
1163 let row = obj(&[("id", Value::String("42".to_string()))]);
1164 let result = eval(
1165 &row,
1166 &func("pad_left", vec![field("id"), lit_int(5), lit_str("0")]),
1167 )
1168 .unwrap();
1169 assert_eq!(result, Value::String("00042".to_string()));
1170 }
1171
1172 #[test]
1173 fn test_pad_left_no_padding_needed() {
1174 let row = obj(&[("id", Value::String("12345".to_string()))]);
1175 let result = eval(
1176 &row,
1177 &func("pad_left", vec![field("id"), lit_int(3), lit_str("0")]),
1178 )
1179 .unwrap();
1180 assert_eq!(result, Value::String("12345".to_string()));
1181 }
1182
1183 #[test]
1184 fn test_pad_right() {
1185 let row = obj(&[("s", Value::String("hi".to_string()))]);
1186 let result = eval(
1187 &row,
1188 &func("pad_right", vec![field("s"), lit_int(5), lit_str(".")]),
1189 )
1190 .unwrap();
1191 assert_eq!(result, Value::String("hi...".to_string()));
1192 }
1193
1194 #[test]
1195 fn test_pad_right_no_padding_needed() {
1196 let row = obj(&[("s", Value::String("hello".to_string()))]);
1197 let result = eval(
1198 &row,
1199 &func("pad_right", vec![field("s"), lit_int(3), lit_str(".")]),
1200 )
1201 .unwrap();
1202 assert_eq!(result, Value::String("hello".to_string()));
1203 }
1204
1205 #[test]
1208 fn test_round_no_decimals() {
1209 let row = obj(&[("price", Value::Float(3.7))]);
1210 let result = eval(&row, &func("round", vec![field("price")])).unwrap();
1211 assert_eq!(result, Value::Integer(4));
1212 }
1213
1214 #[test]
1215 fn test_round_with_decimals() {
1216 let row = obj(&[("price", Value::Float(3.14159))]);
1217 let result = eval(&row, &func("round", vec![field("price"), lit_int(2)])).unwrap();
1218 match result {
1219 Value::Float(v) => assert!((v - 3.14).abs() < 1e-10, "expected ~3.14, got {v}"),
1220 other => panic!("expected Float, got {other:?}"),
1221 }
1222 }
1223
1224 #[test]
1225 fn test_ceil() {
1226 let row = obj(&[("price", Value::Float(3.2))]);
1227 let result = eval(&row, &func("ceil", vec![field("price")])).unwrap();
1228 assert_eq!(result, Value::Integer(4));
1229 }
1230
1231 #[test]
1232 fn test_floor() {
1233 let row = obj(&[("price", Value::Float(3.9))]);
1234 let result = eval(&row, &func("floor", vec![field("price")])).unwrap();
1235 assert_eq!(result, Value::Integer(3));
1236 }
1237
1238 #[test]
1239 fn test_abs_negative() {
1240 let row = obj(&[("score", Value::Integer(-5))]);
1241 let result = eval(&row, &func("abs", vec![field("score")])).unwrap();
1242 assert_eq!(result, Value::Integer(5));
1243 }
1244
1245 #[test]
1248 fn test_year() {
1249 let row = obj(&[("date", Value::String("2024-03-15".to_string()))]);
1250 let result = eval(&row, &func("year", vec![field("date")])).unwrap();
1251 assert_eq!(result, Value::Integer(2024));
1252 }
1253
1254 #[test]
1255 fn test_month() {
1256 let row = obj(&[("date", Value::String("2024-03-15".to_string()))]);
1257 let result = eval(&row, &func("month", vec![field("date")])).unwrap();
1258 assert_eq!(result, Value::Integer(3));
1259 }
1260
1261 #[test]
1262 fn test_day() {
1263 let row = obj(&[("date", Value::String("2024-03-15".to_string()))]);
1264 let result = eval(&row, &func("day", vec![field("date")])).unwrap();
1265 assert_eq!(result, Value::Integer(15));
1266 }
1267
1268 #[test]
1269 fn test_date_normalize() {
1270 let row = obj(&[("d", Value::String("2024-3-5".to_string()))]);
1271 let result = eval(&row, &func("date", vec![field("d")])).unwrap();
1272 assert_eq!(result, Value::String("2024-03-05".to_string()));
1273 }
1274
1275 #[test]
1276 fn test_now_returns_string() {
1277 let row = obj(&[]);
1278 let result = eval(&row, &func("now", vec![])).unwrap();
1279 assert!(matches!(result, Value::String(_)));
1280 if let Value::String(s) = result {
1281 assert!(s.contains('T'), "now() should return ISO 8601 format");
1282 }
1283 }
1284
1285 #[test]
1288 fn test_to_int_from_string() {
1289 let row = obj(&[("n", Value::String("42".to_string()))]);
1290 let result = eval(&row, &func("to_int", vec![field("n")])).unwrap();
1291 assert_eq!(result, Value::Integer(42));
1292 }
1293
1294 #[test]
1295 fn test_to_float_from_int() {
1296 let row = obj(&[("n", Value::Integer(5))]);
1297 let result = eval(&row, &func("to_float", vec![field("n")])).unwrap();
1298 assert_eq!(result, Value::Float(5.0));
1299 }
1300
1301 #[test]
1302 fn test_to_string_from_int() {
1303 let row = obj(&[("n", Value::Integer(42))]);
1304 let result = eval(&row, &func("to_string", vec![field("n")])).unwrap();
1305 assert_eq!(result, Value::String("42".to_string()));
1306 }
1307
1308 #[test]
1309 fn test_to_bool_from_string() {
1310 let row = obj(&[("flag", Value::String("true".to_string()))]);
1311 let result = eval(&row, &func("to_bool", vec![field("flag")])).unwrap();
1312 assert_eq!(result, Value::Bool(true));
1313 }
1314
1315 #[test]
1318 fn test_nested_upper_trim() {
1319 let row = obj(&[("name", Value::String(" hello ".to_string()))]);
1320 let result = eval(
1321 &row,
1322 &func("upper", vec![func("trim", vec![field("name")])]),
1323 )
1324 .unwrap();
1325 assert_eq!(result, Value::String("HELLO".to_string()));
1326 }
1327
1328 #[test]
1331 fn test_default_key_field() {
1332 assert_eq!(expr_default_key(&Expr::Field("name".to_string())), "name");
1333 }
1334
1335 #[test]
1336 fn test_default_key_func() {
1337 let expr = Expr::FuncCall {
1338 name: "upper".to_string(),
1339 args: vec![Expr::Field("name".to_string())],
1340 };
1341 assert_eq!(expr_default_key(&expr), "upper_name");
1342 }
1343
1344 #[test]
1345 fn test_default_key_nested_func() {
1346 let expr = Expr::FuncCall {
1347 name: "upper".to_string(),
1348 args: vec![Expr::FuncCall {
1349 name: "trim".to_string(),
1350 args: vec![Expr::Field("name".to_string())],
1351 }],
1352 };
1353 assert_eq!(expr_default_key(&expr), "upper_trim_name");
1354 }
1355
1356 #[test]
1359 fn test_binary_add_integers() {
1360 let row = Value::Object(indexmap::IndexMap::from([
1361 ("a".to_string(), Value::Integer(10)),
1362 ("b".to_string(), Value::Integer(20)),
1363 ]));
1364 let expr = Expr::BinaryOp {
1365 op: ArithmeticOp::Add,
1366 left: Box::new(Expr::Field("a".to_string())),
1367 right: Box::new(Expr::Field("b".to_string())),
1368 };
1369 assert_eq!(evaluate_expr(&row, &expr).unwrap(), Value::Integer(30));
1370 }
1371
1372 #[test]
1373 fn test_binary_mul_float() {
1374 let row = Value::Object(indexmap::IndexMap::from([(
1375 "price".to_string(),
1376 Value::Float(100.0),
1377 )]));
1378 let expr = Expr::BinaryOp {
1379 op: ArithmeticOp::Mul,
1380 left: Box::new(Expr::Field("price".to_string())),
1381 right: Box::new(Expr::Literal(LiteralValue::Float(0.1))),
1382 };
1383 let result = evaluate_expr(&row, &expr).unwrap();
1384 if let Value::Float(f) = result {
1385 assert!((f - 10.0).abs() < 0.001);
1386 } else {
1387 panic!("expected float");
1388 }
1389 }
1390
1391 #[test]
1392 fn test_binary_string_concat() {
1393 let row = Value::Object(indexmap::IndexMap::from([
1394 ("first".to_string(), Value::String("Hello".to_string())),
1395 ("last".to_string(), Value::String("World".to_string())),
1396 ]));
1397 let expr = Expr::BinaryOp {
1398 op: ArithmeticOp::Add,
1399 left: Box::new(Expr::Field("first".to_string())),
1400 right: Box::new(Expr::BinaryOp {
1401 op: ArithmeticOp::Add,
1402 left: Box::new(Expr::Literal(LiteralValue::String(" ".to_string()))),
1403 right: Box::new(Expr::Field("last".to_string())),
1404 }),
1405 };
1406 assert_eq!(
1407 evaluate_expr(&row, &expr).unwrap(),
1408 Value::String("Hello World".to_string())
1409 );
1410 }
1411
1412 #[test]
1413 fn test_binary_div_by_zero() {
1414 let row = Value::Object(indexmap::IndexMap::from([
1415 ("a".to_string(), Value::Integer(10)),
1416 ("b".to_string(), Value::Integer(0)),
1417 ]));
1418 let expr = Expr::BinaryOp {
1419 op: ArithmeticOp::Div,
1420 left: Box::new(Expr::Field("a".to_string())),
1421 right: Box::new(Expr::Field("b".to_string())),
1422 };
1423 assert!(evaluate_expr(&row, &expr).is_err());
1424 }
1425
1426 #[test]
1427 fn test_binary_null_propagation() {
1428 let row = Value::Object(indexmap::IndexMap::from([
1429 ("a".to_string(), Value::Integer(10)),
1430 ("b".to_string(), Value::Null),
1431 ]));
1432 let expr = Expr::BinaryOp {
1433 op: ArithmeticOp::Add,
1434 left: Box::new(Expr::Field("a".to_string())),
1435 right: Box::new(Expr::Field("b".to_string())),
1436 };
1437 assert_eq!(evaluate_expr(&row, &expr).unwrap(), Value::Null);
1438 }
1439
1440 #[test]
1441 fn test_binary_int_div_non_exact() {
1442 let row = Value::Object(indexmap::IndexMap::from([
1443 ("a".to_string(), Value::Integer(10)),
1444 ("b".to_string(), Value::Integer(3)),
1445 ]));
1446 let expr = Expr::BinaryOp {
1447 op: ArithmeticOp::Div,
1448 left: Box::new(Expr::Field("a".to_string())),
1449 right: Box::new(Expr::Field("b".to_string())),
1450 };
1451 let result = evaluate_expr(&row, &expr).unwrap();
1453 assert!(matches!(result, Value::Float(_)));
1454 }
1455
1456 #[test]
1457 fn test_binary_mixed_string_number_concat() {
1458 let row = Value::Object(indexmap::IndexMap::from([
1459 ("name".to_string(), Value::String("Item".to_string())),
1460 ("id".to_string(), Value::Integer(42)),
1461 ]));
1462 let expr = Expr::BinaryOp {
1463 op: ArithmeticOp::Add,
1464 left: Box::new(Expr::Field("name".to_string())),
1465 right: Box::new(Expr::Field("id".to_string())),
1466 };
1467 assert_eq!(
1468 evaluate_expr(&row, &expr).unwrap(),
1469 Value::String("Item42".to_string())
1470 );
1471 }
1472
1473 #[test]
1476 fn test_eval_if_true_branch() {
1477 use crate::query::parser::{CompareOp, Comparison, Condition};
1478 let row = Value::Object(
1479 vec![
1480 ("age".to_string(), Value::Integer(15)),
1481 ("name".to_string(), Value::String("Alice".to_string())),
1482 ]
1483 .into_iter()
1484 .collect(),
1485 );
1486 let expr = Expr::If {
1487 condition: Condition::Comparison(Comparison {
1488 field: "age".to_string(),
1489 op: CompareOp::Lt,
1490 value: LiteralValue::Integer(18),
1491 }),
1492 then_expr: Box::new(Expr::Literal(LiteralValue::String("minor".to_string()))),
1493 else_expr: Box::new(Expr::Literal(LiteralValue::String("adult".to_string()))),
1494 };
1495 assert_eq!(
1496 evaluate_expr(&row, &expr).unwrap(),
1497 Value::String("minor".to_string())
1498 );
1499 }
1500
1501 #[test]
1502 fn test_eval_if_false_branch() {
1503 use crate::query::parser::{CompareOp, Comparison, Condition};
1504 let row = Value::Object(
1505 vec![("age".to_string(), Value::Integer(30))]
1506 .into_iter()
1507 .collect(),
1508 );
1509 let expr = Expr::If {
1510 condition: Condition::Comparison(Comparison {
1511 field: "age".to_string(),
1512 op: CompareOp::Lt,
1513 value: LiteralValue::Integer(18),
1514 }),
1515 then_expr: Box::new(Expr::Literal(LiteralValue::String("minor".to_string()))),
1516 else_expr: Box::new(Expr::Literal(LiteralValue::String("adult".to_string()))),
1517 };
1518 assert_eq!(
1519 evaluate_expr(&row, &expr).unwrap(),
1520 Value::String("adult".to_string())
1521 );
1522 }
1523
1524 #[test]
1525 fn test_eval_if_nested() {
1526 use crate::query::parser::{CompareOp, Comparison, Condition};
1527 let row = Value::Object(
1528 vec![("age".to_string(), Value::Integer(70))]
1529 .into_iter()
1530 .collect(),
1531 );
1532 let expr = Expr::If {
1533 condition: Condition::Comparison(Comparison {
1534 field: "age".to_string(),
1535 op: CompareOp::Lt,
1536 value: LiteralValue::Integer(18),
1537 }),
1538 then_expr: Box::new(Expr::Literal(LiteralValue::String("minor".to_string()))),
1539 else_expr: Box::new(Expr::If {
1540 condition: Condition::Comparison(Comparison {
1541 field: "age".to_string(),
1542 op: CompareOp::Lt,
1543 value: LiteralValue::Integer(65),
1544 }),
1545 then_expr: Box::new(Expr::Literal(LiteralValue::String("adult".to_string()))),
1546 else_expr: Box::new(Expr::Literal(LiteralValue::String("senior".to_string()))),
1547 }),
1548 };
1549 assert_eq!(
1550 evaluate_expr(&row, &expr).unwrap(),
1551 Value::String("senior".to_string())
1552 );
1553 }
1554
1555 #[test]
1558 fn test_eval_case_first_branch() {
1559 use crate::query::parser::{CompareOp, Comparison, Condition};
1560 let row = Value::Object(
1561 vec![("status".to_string(), Value::String("active".to_string()))]
1562 .into_iter()
1563 .collect(),
1564 );
1565 let expr = Expr::Case {
1566 branches: vec![
1567 (
1568 Condition::Comparison(Comparison {
1569 field: "status".to_string(),
1570 op: CompareOp::Eq,
1571 value: LiteralValue::String("active".to_string()),
1572 }),
1573 Expr::Literal(LiteralValue::String("A".to_string())),
1574 ),
1575 (
1576 Condition::Comparison(Comparison {
1577 field: "status".to_string(),
1578 op: CompareOp::Eq,
1579 value: LiteralValue::String("inactive".to_string()),
1580 }),
1581 Expr::Literal(LiteralValue::String("I".to_string())),
1582 ),
1583 ],
1584 default: Some(Box::new(Expr::Literal(LiteralValue::String(
1585 "U".to_string(),
1586 )))),
1587 };
1588 assert_eq!(
1589 evaluate_expr(&row, &expr).unwrap(),
1590 Value::String("A".to_string())
1591 );
1592 }
1593
1594 #[test]
1595 fn test_eval_case_second_branch() {
1596 use crate::query::parser::{CompareOp, Comparison, Condition};
1597 let row = Value::Object(
1598 vec![("status".to_string(), Value::String("inactive".to_string()))]
1599 .into_iter()
1600 .collect(),
1601 );
1602 let expr = Expr::Case {
1603 branches: vec![
1604 (
1605 Condition::Comparison(Comparison {
1606 field: "status".to_string(),
1607 op: CompareOp::Eq,
1608 value: LiteralValue::String("active".to_string()),
1609 }),
1610 Expr::Literal(LiteralValue::String("A".to_string())),
1611 ),
1612 (
1613 Condition::Comparison(Comparison {
1614 field: "status".to_string(),
1615 op: CompareOp::Eq,
1616 value: LiteralValue::String("inactive".to_string()),
1617 }),
1618 Expr::Literal(LiteralValue::String("I".to_string())),
1619 ),
1620 ],
1621 default: Some(Box::new(Expr::Literal(LiteralValue::String(
1622 "U".to_string(),
1623 )))),
1624 };
1625 assert_eq!(
1626 evaluate_expr(&row, &expr).unwrap(),
1627 Value::String("I".to_string())
1628 );
1629 }
1630
1631 #[test]
1632 fn test_eval_case_default() {
1633 use crate::query::parser::{CompareOp, Comparison, Condition};
1634 let row = Value::Object(
1635 vec![("status".to_string(), Value::String("pending".to_string()))]
1636 .into_iter()
1637 .collect(),
1638 );
1639 let expr = Expr::Case {
1640 branches: vec![(
1641 Condition::Comparison(Comparison {
1642 field: "status".to_string(),
1643 op: CompareOp::Eq,
1644 value: LiteralValue::String("active".to_string()),
1645 }),
1646 Expr::Literal(LiteralValue::String("A".to_string())),
1647 )],
1648 default: Some(Box::new(Expr::Literal(LiteralValue::String(
1649 "other".to_string(),
1650 )))),
1651 };
1652 assert_eq!(
1653 evaluate_expr(&row, &expr).unwrap(),
1654 Value::String("other".to_string())
1655 );
1656 }
1657
1658 #[test]
1659 fn test_eval_case_no_default_returns_null() {
1660 use crate::query::parser::{CompareOp, Comparison, Condition};
1661 let row = Value::Object(
1662 vec![("status".to_string(), Value::String("pending".to_string()))]
1663 .into_iter()
1664 .collect(),
1665 );
1666 let expr = Expr::Case {
1667 branches: vec![(
1668 Condition::Comparison(Comparison {
1669 field: "status".to_string(),
1670 op: CompareOp::Eq,
1671 value: LiteralValue::String("active".to_string()),
1672 }),
1673 Expr::Literal(LiteralValue::String("A".to_string())),
1674 )],
1675 default: None,
1676 };
1677 assert_eq!(evaluate_expr(&row, &expr).unwrap(), Value::Null);
1678 }
1679}