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