1use std::cmp::Ordering;
2use std::time::SystemTime;
3
4use teaql_core::{
5 Aggregate, AggregateFunction, BinaryOp, Expr, OrderBy, Record, SelectQuery, SortDirection,
6 Value,
7};
8use teaql_data_service::{DataServiceOperation, ExecutionMetadata, QueryResult};
9
10pub struct InMemoryQueryEngine;
13
14impl InMemoryQueryEngine {
15 pub fn execute(query: &SelectQuery, mut rows: Vec<Record>) -> QueryResult {
19 let started_at = SystemTime::now();
20
21 if let Some(filter) = &query.filter {
23 Self::filter(&mut rows, filter);
24 }
25
26 if !query.aggregates.is_empty() {
28 let mut result = Self::aggregate(query, rows);
29 result.metadata.started_at = started_at;
30 result.metadata.ended_at = SystemTime::now();
31 return result;
32 }
33
34 if !query.order_by.is_empty() {
36 Self::sort(&mut rows, &query.order_by);
37 }
38
39 if let Some(slice) = &query.slice {
41 rows = Self::paginate(rows, slice);
42 }
43
44 if !query.projection.is_empty() {
46 rows = Self::project(rows, &query.projection);
47 }
48
49 let count = rows.len();
50 QueryResult {
51 rows,
52 metadata: ExecutionMetadata {
53 debug_query: None,
54 backend: "memory".to_owned(),
55 operation: DataServiceOperation::Query,
56 started_at,
57 ended_at: SystemTime::now(),
58 affected_rows: None,
59 result_count: Some(count),
60 trace_chain: Vec::new(),
61 comment: None,
62 backend_request_id: None,
63 parameterized_query: None,
64 params: Vec::new(),
65 },
66 }
67 }
68
69 fn filter(rows: &mut Vec<Record>, expr: &Expr) {
71 rows.retain(|row| ExprEvaluator::eval(expr, row));
72 }
73
74 fn sort(rows: &mut Vec<Record>, order_by: &[OrderBy]) {
76 rows.sort_by(|a, b| {
77 for ob in order_by {
78 let va = a.get(&ob.field).unwrap_or(&Value::Null);
79 let vb = b.get(&ob.field).unwrap_or(&Value::Null);
80 let ord = compare_values(va, vb);
81 let ord = match ob.direction {
82 SortDirection::Asc => ord,
83 SortDirection::Desc => ord.reverse(),
84 };
85 if ord != Ordering::Equal {
86 return ord;
87 }
88 }
89 Ordering::Equal
90 });
91 }
92
93 fn paginate(rows: Vec<Record>, slice: &teaql_core::Slice) -> Vec<Record> {
95 let offset = slice.offset as usize;
96 let iter = rows.into_iter().skip(offset);
97 match slice.limit {
98 Some(limit) => iter.take(limit as usize).collect(),
99 None => iter.collect(),
100 }
101 }
102
103 fn project(rows: Vec<Record>, projection: &[String]) -> Vec<Record> {
105 rows.into_iter()
106 .map(|row| {
107 row.into_iter()
108 .filter(|(key, _)| projection.contains(key))
109 .collect()
110 })
111 .collect()
112 }
113
114 fn aggregate(query: &SelectQuery, rows: Vec<Record>) -> QueryResult {
117 let started_at = SystemTime::now();
118
119 if !query.group_by.is_empty() {
121 return Self::aggregate_grouped(query, rows, started_at);
122 }
123
124 let mut result_row = Record::new();
126 for agg in &query.aggregates {
127 let value = compute_aggregate(agg, &rows);
128 result_row.insert(agg.alias.clone(), value);
129 }
130
131 let result_rows = vec![result_row];
132 let count = result_rows.len();
133 QueryResult {
134 rows: result_rows,
135 metadata: ExecutionMetadata {
136 debug_query: None,
137 backend: "memory".to_owned(),
138 operation: DataServiceOperation::Query,
139 started_at,
140 ended_at: SystemTime::now(),
141 affected_rows: None,
142 result_count: Some(count),
143 trace_chain: Vec::new(),
144 comment: None,
145 backend_request_id: None,
146 parameterized_query: None,
147 params: Vec::new(),
148 },
149 }
150 }
151
152 fn aggregate_grouped(
154 query: &SelectQuery,
155 rows: Vec<Record>,
156 started_at: SystemTime,
157 ) -> QueryResult {
158 let mut groups: Vec<(Vec<Value>, Vec<Record>)> = Vec::new();
160
161 for row in rows {
162 let key: Vec<Value> = query
163 .group_by
164 .iter()
165 .map(|gb| row.get(gb).cloned().unwrap_or(Value::Null))
166 .collect();
167
168 match groups.iter_mut().find(|(k, _)| k == &key) {
169 Some((_k, group)) => group.push(row),
170 None => groups.push((key, vec![row])),
171 }
172 }
173
174 let mut result_rows = Vec::with_capacity(groups.len());
175 for (key_values, group_rows) in &groups {
176 let mut result_row = Record::new();
177
178 for (i, gb) in query.group_by.iter().enumerate() {
180 result_row.insert(gb.clone(), key_values[i].clone());
181 }
182
183 for agg in &query.aggregates {
185 let value = compute_aggregate(agg, group_rows);
186 result_row.insert(agg.alias.clone(), value);
187 }
188
189 result_rows.push(result_row);
190 }
191
192 let count = result_rows.len();
193 QueryResult {
194 rows: result_rows,
195 metadata: ExecutionMetadata {
196 debug_query: None,
197 backend: "memory".to_owned(),
198 operation: DataServiceOperation::Query,
199 started_at,
200 ended_at: SystemTime::now(),
201 affected_rows: None,
202 result_count: Some(count),
203 trace_chain: Vec::new(),
204 comment: None,
205 backend_request_id: None,
206 parameterized_query: None,
207 params: Vec::new(),
208 },
209 }
210 }
211}
212
213pub struct ExprEvaluator;
215
216impl ExprEvaluator {
217 pub fn eval(expr: &Expr, row: &Record) -> bool {
219 match expr {
220 Expr::Binary { left, op, right } => {
221 let lv = Self::resolve(left, row);
222 let rv = Self::resolve(right, row);
223 Self::compare_op(&lv, op, &rv)
224 }
225 Expr::And(parts) => parts.iter().all(|p| Self::eval(p, row)),
226 Expr::Or(parts) => parts.iter().any(|p| Self::eval(p, row)),
227 Expr::Not(inner) => !Self::eval(inner, row),
228 Expr::IsNull(inner) => Self::resolve(inner, row) == Value::Null,
229 Expr::IsNotNull(inner) => Self::resolve(inner, row) != Value::Null,
230 Expr::Between {
231 expr: inner,
232 lower,
233 upper,
234 } => {
235 let v = Self::resolve(inner, row);
236 let lo = Self::resolve(lower, row);
237 let hi = Self::resolve(upper, row);
238 compare_values(&v, &lo) != Ordering::Less
239 && compare_values(&v, &hi) != Ordering::Greater
240 }
241 Expr::SubQuery { .. } => false,
243 Expr::Function { .. } => false,
245 Expr::Column(_) | Expr::Value(_) => {
247 matches!(Self::resolve(expr, row), Value::Bool(true))
248 }
249 }
250 }
251
252 pub fn resolve(expr: &Expr, row: &Record) -> Value {
254 match expr {
255 Expr::Column(name) => row.get(name).cloned().unwrap_or(Value::Null),
256 Expr::Value(v) => v.clone(),
257 Expr::Binary { left, op, right } => {
258 let lv = Self::resolve(left, row);
259 let rv = Self::resolve(right, row);
260 Value::Bool(Self::compare_op(&lv, op, &rv))
261 }
262 Expr::And(parts) => Value::Bool(parts.iter().all(|p| Self::eval(p, row))),
263 Expr::Or(parts) => Value::Bool(parts.iter().any(|p| Self::eval(p, row))),
264 Expr::Not(inner) => Value::Bool(!Self::eval(inner, row)),
265 Expr::IsNull(inner) => Value::Bool(Self::resolve(inner, row) == Value::Null),
266 Expr::IsNotNull(inner) => Value::Bool(Self::resolve(inner, row) != Value::Null),
267 Expr::Between {
268 expr: inner,
269 lower,
270 upper,
271 } => {
272 let v = Self::resolve(inner, row);
273 let lo = Self::resolve(lower, row);
274 let hi = Self::resolve(upper, row);
275 Value::Bool(
276 compare_values(&v, &lo) != Ordering::Less
277 && compare_values(&v, &hi) != Ordering::Greater,
278 )
279 }
280 Expr::SubQuery { .. } => Value::Null,
281 Expr::Function { .. } => Value::Null,
282 }
283 }
284
285 fn compare_op(left: &Value, op: &BinaryOp, right: &Value) -> bool {
287 match op {
288 BinaryOp::Eq => left == right,
289 BinaryOp::Ne => left != right,
290 BinaryOp::Gt => compare_values(left, right) == Ordering::Greater,
291 BinaryOp::Gte => matches!(
292 compare_values(left, right),
293 Ordering::Greater | Ordering::Equal
294 ),
295 BinaryOp::Lt => compare_values(left, right) == Ordering::Less,
296 BinaryOp::Lte => matches!(
297 compare_values(left, right),
298 Ordering::Less | Ordering::Equal
299 ),
300 BinaryOp::Like => match (left, right) {
301 (Value::Text(text), Value::Text(pattern)) => Self::like_match(text, pattern),
302 _ => false,
303 },
304 BinaryOp::NotLike => match (left, right) {
305 (Value::Text(text), Value::Text(pattern)) => !Self::like_match(text, pattern),
306 _ => true,
307 },
308 BinaryOp::In | BinaryOp::InLarge => match right {
309 Value::List(items) => items.contains(left),
310 _ => left == right,
311 },
312 BinaryOp::NotIn | BinaryOp::NotInLarge => match right {
313 Value::List(items) => !items.contains(left),
314 _ => left != right,
315 },
316 }
317 }
318
319 fn like_match(text: &str, pattern: &str) -> bool {
324 let text_chars: Vec<char> = text.chars().collect();
325 let pattern_chars: Vec<char> = pattern.chars().collect();
326 like_match_recursive(&text_chars, 0, &pattern_chars, 0)
327 }
328}
329
330fn like_match_recursive(text: &[char], ti: usize, pattern: &[char], pi: usize) -> bool {
333 let mut ti = ti;
334 let mut pi = pi;
335
336 loop {
337 if pi == pattern.len() {
338 return ti == text.len();
339 }
340
341 match pattern[pi] {
342 '%' => {
343 while pi < pattern.len() && pattern[pi] == '%' {
345 pi += 1;
346 }
347 if pi == pattern.len() {
349 return true;
350 }
351 for start in ti..=text.len() {
353 if like_match_recursive(text, start, pattern, pi) {
354 return true;
355 }
356 }
357 return false;
358 }
359 '_' => {
360 if ti >= text.len() {
361 return false;
362 }
363 ti += 1;
364 pi += 1;
365 }
366 ch => {
367 if ti >= text.len() || text[ti] != ch {
368 return false;
369 }
370 ti += 1;
371 pi += 1;
372 }
373 }
374 }
375}
376
377fn compare_i64_u64(a: i64, b: u64) -> Ordering {
379 match a < 0 {
380 true => Ordering::Less,
381 false => (a as u64).cmp(&b),
382 }
383}
384
385fn compare_values(a: &Value, b: &Value) -> Ordering {
387 match (a, b) {
388 (Value::Null, Value::Null) => Ordering::Equal,
389 (Value::Null, _) => Ordering::Less,
390 (_, Value::Null) => Ordering::Greater,
391 (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
392 (Value::I64(a), Value::I64(b)) => a.cmp(b),
393 (Value::U64(a), Value::U64(b)) => a.cmp(b),
394 (Value::I64(a), Value::U64(b)) => compare_i64_u64(*a, *b),
395 (Value::U64(a), Value::I64(b)) => compare_i64_u64(*b, *a).reverse(),
396 (Value::F64(a), Value::F64(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
397 (Value::Decimal(a), Value::Decimal(b)) => a.cmp(b),
398 (Value::Text(a), Value::Text(b)) => a.cmp(b),
399 (Value::Date(a), Value::Date(b)) => a.cmp(b),
400 (Value::Timestamp(a), Value::Timestamp(b)) => a.cmp(b),
401 _ => value_to_f64(a)
403 .zip(value_to_f64(b))
404 .and_then(|(fa, fb)| fa.partial_cmp(&fb))
405 .unwrap_or(Ordering::Equal),
406 }
407}
408
409fn value_to_f64(v: &Value) -> Option<f64> {
411 v.try_f64()
412}
413
414fn count_rows(rows: &[Record], field: &str) -> Value {
416 let count = match field {
417 "*" => rows.len(),
418 _ => rows
419 .iter()
420 .filter(|r| r.get(field).map(|v| v != &Value::Null).unwrap_or(false))
421 .count(),
422 };
423 Value::I64(count as i64)
424}
425
426fn compute_aggregate(agg: &Aggregate, rows: &[Record]) -> Value {
428 match agg.function {
429 AggregateFunction::Count => count_rows(rows, &agg.field),
430 AggregateFunction::Sum => {
431 let mut sum: f64 = 0.0;
432 let mut found = false;
433 for row in rows {
434 if let Some(v) = row.get(&agg.field) {
435 if let Some(f) = v.try_f64() {
436 sum += f;
437 found = true;
438 }
439 }
440 }
441 found.then(|| Value::F64(sum)).unwrap_or(Value::Null)
442 }
443 AggregateFunction::Avg => {
444 let mut sum: f64 = 0.0;
445 let mut count: u64 = 0;
446 for row in rows {
447 if let Some(v) = row.get(&agg.field) {
448 if let Some(f) = v.try_f64() {
449 sum += f;
450 count += 1;
451 }
452 }
453 }
454 (count > 0)
455 .then(|| Value::F64(sum / count as f64))
456 .unwrap_or(Value::Null)
457 }
458 AggregateFunction::Max => {
459 let mut max: Option<&Value> = None;
460 for row in rows {
461 if let Some(v) = row.get(&agg.field) {
462 if v == &Value::Null {
463 continue;
464 }
465 max = Some(match max {
466 Some(current) if compare_values(v, current) == Ordering::Greater => v,
467 Some(current) => current,
468 None => v,
469 });
470 }
471 }
472 max.cloned().unwrap_or(Value::Null)
473 }
474 AggregateFunction::Min => {
475 let mut min: Option<&Value> = None;
476 for row in rows {
477 if let Some(v) = row.get(&agg.field) {
478 if v == &Value::Null {
479 continue;
480 }
481 min = Some(match min {
482 Some(current) if compare_values(v, current) == Ordering::Less => v,
483 Some(current) => current,
484 None => v,
485 });
486 }
487 }
488 min.cloned().unwrap_or(Value::Null)
489 }
490 AggregateFunction::Stddev
492 | AggregateFunction::StddevPop
493 | AggregateFunction::VarSamp
494 | AggregateFunction::VarPop
495 | AggregateFunction::BitAnd
496 | AggregateFunction::BitOr
497 | AggregateFunction::BitXor => Value::Null,
498 }
499}
500
501#[cfg(test)]
502mod tests {
503 use super::*;
504 use teaql_core::{Aggregate, AggregateFunction, Record, SelectQuery, Value};
505
506 fn make_row(pairs: Vec<(&str, Value)>) -> Record {
507 pairs.into_iter().map(|(k, v)| (k.to_owned(), v)).collect()
508 }
509
510 fn sample_rows() -> Vec<Record> {
511 vec![
512 make_row(vec![
513 ("id", Value::U64(1)),
514 ("name", Value::Text("Alice".to_owned())),
515 ("age", Value::I64(30)),
516 ]),
517 make_row(vec![
518 ("id", Value::U64(2)),
519 ("name", Value::Text("Bob".to_owned())),
520 ("age", Value::I64(25)),
521 ]),
522 make_row(vec![
523 ("id", Value::U64(3)),
524 ("name", Value::Text("Charlie".to_owned())),
525 ("age", Value::I64(35)),
526 ]),
527 ]
528 }
529
530 #[test]
531 fn test_execute_no_filter() {
532 let query = SelectQuery::new("User");
533 let result = InMemoryQueryEngine::execute(&query, sample_rows());
534 assert_eq!(result.rows.len(), 3);
535 assert_eq!(result.metadata.backend, "memory");
536 }
537
538 #[test]
539 fn test_execute_with_eq_filter() {
540 let query = SelectQuery::new("User").filter(Expr::eq("name", "Bob"));
541 let result = InMemoryQueryEngine::execute(&query, sample_rows());
542 assert_eq!(result.rows.len(), 1);
543 assert_eq!(
544 result.rows[0].get("name"),
545 Some(&Value::Text("Bob".to_owned()))
546 );
547 }
548
549 #[test]
550 fn test_execute_with_gt_filter() {
551 let query = SelectQuery::new("User").filter(Expr::gt("age", 28_i64));
552 let result = InMemoryQueryEngine::execute(&query, sample_rows());
553 assert_eq!(result.rows.len(), 2); }
555
556 #[test]
557 fn test_sort_ascending() {
558 let query = SelectQuery::new("User").order_by(teaql_core::OrderBy::asc("age"));
559 let result = InMemoryQueryEngine::execute(&query, sample_rows());
560 let ages: Vec<_> = result
561 .rows
562 .iter()
563 .map(|r| r.get("age").unwrap().clone())
564 .collect();
565 assert_eq!(ages, vec![Value::I64(25), Value::I64(30), Value::I64(35)]);
566 }
567
568 #[test]
569 fn test_sort_descending() {
570 let query = SelectQuery::new("User").order_by(teaql_core::OrderBy::desc("age"));
571 let result = InMemoryQueryEngine::execute(&query, sample_rows());
572 let ages: Vec<_> = result
573 .rows
574 .iter()
575 .map(|r| r.get("age").unwrap().clone())
576 .collect();
577 assert_eq!(ages, vec![Value::I64(35), Value::I64(30), Value::I64(25)]);
578 }
579
580 #[test]
581 fn test_paginate() {
582 let query = SelectQuery::new("User").page(1, 1);
583 let result = InMemoryQueryEngine::execute(&query, sample_rows());
584 assert_eq!(result.rows.len(), 1);
585 assert_eq!(
586 result.rows[0].get("name"),
587 Some(&Value::Text("Bob".to_owned()))
588 );
589 }
590
591 #[test]
592 fn test_projection() {
593 let query = SelectQuery::new("User").projects(["name"]);
594 let result = InMemoryQueryEngine::execute(&query, sample_rows());
595 for row in &result.rows {
596 assert!(row.contains_key("name"));
597 assert!(!row.contains_key("id"));
598 assert!(!row.contains_key("age"));
599 }
600 }
601
602 #[test]
603 fn test_count_aggregate() {
604 let query = SelectQuery::new("User").aggregate(Aggregate::count("total"));
605 let result = InMemoryQueryEngine::execute(&query, sample_rows());
606 assert_eq!(result.rows.len(), 1);
607 assert_eq!(result.rows[0].get("total"), Some(&Value::I64(3)));
608 }
609
610 #[test]
611 fn test_sum_aggregate() {
612 let query = SelectQuery::new("User").aggregate(Aggregate::sum("age", "age_sum"));
613 let result = InMemoryQueryEngine::execute(&query, sample_rows());
614 assert_eq!(result.rows[0].get("age_sum"), Some(&Value::F64(90.0)));
615 }
616
617 #[test]
618 fn test_avg_aggregate() {
619 let query = SelectQuery::new("User").aggregate(Aggregate::avg("age", "age_avg"));
620 let result = InMemoryQueryEngine::execute(&query, sample_rows());
621 assert_eq!(result.rows[0].get("age_avg"), Some(&Value::F64(30.0)));
622 }
623
624 #[test]
625 fn test_max_aggregate() {
626 let query = SelectQuery::new("User").aggregate(Aggregate::max("age", "age_max"));
627 let result = InMemoryQueryEngine::execute(&query, sample_rows());
628 assert_eq!(result.rows[0].get("age_max"), Some(&Value::I64(35)));
629 }
630
631 #[test]
632 fn test_min_aggregate() {
633 let query = SelectQuery::new("User").aggregate(Aggregate::min("age", "age_min"));
634 let result = InMemoryQueryEngine::execute(&query, sample_rows());
635 assert_eq!(result.rows[0].get("age_min"), Some(&Value::I64(25)));
636 }
637
638 #[test]
639 fn test_like_match_percent() {
640 assert!(ExprEvaluator::like_match("hello world", "%world"));
641 assert!(ExprEvaluator::like_match("hello world", "hello%"));
642 assert!(ExprEvaluator::like_match("hello world", "%lo wo%"));
643 assert!(ExprEvaluator::like_match("hello world", "%"));
644 assert!(!ExprEvaluator::like_match("hello world", "%xyz%"));
645 }
646
647 #[test]
648 fn test_like_match_underscore() {
649 assert!(ExprEvaluator::like_match("abc", "a_c"));
650 assert!(!ExprEvaluator::like_match("abbc", "a_c"));
651 assert!(ExprEvaluator::like_match("abc", "___"));
652 assert!(!ExprEvaluator::like_match("ab", "___"));
653 }
654
655 #[test]
656 fn test_like_match_combined() {
657 assert!(ExprEvaluator::like_match("foobar", "f%r"));
658 assert!(ExprEvaluator::like_match("foobar", "f__b%"));
659 assert!(!ExprEvaluator::like_match("foobar", "f__x%"));
660 }
661
662 #[test]
663 fn test_and_or_not() {
664 let row = make_row(vec![("a", Value::I64(10)), ("b", Value::I64(20))]);
665 let expr_and = Expr::and([Expr::eq("a", 10_i64), Expr::eq("b", 20_i64)]);
666 assert!(ExprEvaluator::eval(&expr_and, &row));
667
668 let expr_or = Expr::or([Expr::eq("a", 99_i64), Expr::eq("b", 20_i64)]);
669 assert!(ExprEvaluator::eval(&expr_or, &row));
670
671 let expr_not = Expr::negate(Expr::eq("a", 99_i64));
672 assert!(ExprEvaluator::eval(&expr_not, &row));
673 }
674
675 #[test]
676 fn test_is_null_is_not_null() {
677 let row = make_row(vec![("x", Value::Null), ("y", Value::I64(1))]);
678 assert!(ExprEvaluator::eval(&Expr::is_null("x"), &row));
679 assert!(!ExprEvaluator::eval(&Expr::is_not_null("x"), &row));
680 assert!(ExprEvaluator::eval(&Expr::is_not_null("y"), &row));
681 }
682
683 #[test]
684 fn test_between() {
685 let row = make_row(vec![("age", Value::I64(30))]);
686 assert!(ExprEvaluator::eval(
687 &Expr::between("age", Value::I64(25), Value::I64(35)),
688 &row
689 ));
690 assert!(!ExprEvaluator::eval(
691 &Expr::between("age", Value::I64(31), Value::I64(35)),
692 &row
693 ));
694 }
695
696 #[test]
697 fn test_in_list() {
698 let row = make_row(vec![("status", Value::Text("active".to_owned()))]);
699 let expr = Expr::in_list(
700 "status",
701 vec![
702 Value::Text("active".to_owned()),
703 Value::Text("pending".to_owned()),
704 ],
705 );
706 assert!(ExprEvaluator::eval(&expr, &row));
707
708 let expr_miss = Expr::in_list("status", vec![Value::Text("closed".to_owned())]);
709 assert!(!ExprEvaluator::eval(&expr_miss, &row));
710 }
711}