Skip to main content

teaql_runtime/
memory.rs

1use std::cmp::Ordering;
2use std::collections::BTreeMap;
3use std::sync::{Arc, Mutex};
4
5use rust_decimal::Decimal;
6use rust_decimal::prelude::ToPrimitive;
7use teaql_core::{
8    Aggregate, AggregateFunction, BinaryOp, DeleteCommand, Entity, Expr, ExprFunction,
9    InsertCommand, Record, RecoverCommand, RelationAggregate, SelectQuery, SmartList,
10    SortDirection, UpdateCommand, Value,
11};
12
13use crate::{DataServiceError, InMemoryMetadataStore, MetadataStore, RuntimeError};
14
15#[derive(Debug)]
16pub enum MemoryDataServiceError {
17    Poisoned,
18    UnsupportedExpression(String),
19    UnsupportedAggregate(String),
20}
21
22impl std::fmt::Display for MemoryDataServiceError {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        match self {
25            Self::Poisoned => write!(f, "memory data service lock poisoned"),
26            Self::UnsupportedExpression(message) => {
27                write!(f, "unsupported memory expression: {message}")
28            }
29            Self::UnsupportedAggregate(message) => {
30                write!(f, "unsupported memory aggregate: {message}")
31            }
32        }
33    }
34}
35
36impl std::error::Error for MemoryDataServiceError {}
37
38#[derive(Debug, Clone)]
39pub struct MemoryDataService<M = InMemoryMetadataStore> {
40    metadata: M,
41    data: Arc<Mutex<BTreeMap<String, Vec<Record>>>>,
42}
43
44impl<M> MemoryDataService<M>
45where
46    M: MetadataStore,
47{
48    pub fn new(metadata: M) -> Self {
49        Self {
50            metadata,
51            data: Arc::new(Mutex::new(BTreeMap::new())),
52        }
53    }
54
55    pub fn with_rows(mut self, entity: impl Into<String>, rows: Vec<Record>) -> Self {
56        self.seed(entity, rows);
57        self
58    }
59
60    pub fn seed(&mut self, entity: impl Into<String>, rows: Vec<Record>) {
61        if let Ok(mut data) = self.data.lock() {
62            data.insert(entity.into(), rows);
63        }
64    }
65
66    pub fn fetch_all(
67        &self,
68        query: &SelectQuery,
69    ) -> Result<Vec<Record>, DataServiceError<MemoryDataServiceError>> {
70        self.require_entity(&query.entity)?;
71        let data = self
72            .data
73            .lock()
74            .map_err(|_| DataServiceError::Executor(MemoryDataServiceError::Poisoned))?;
75        let mut rows = data.get(&query.entity).cloned().unwrap_or_default();
76        drop(data);
77
78        if let Some(filter) = &query.filter {
79            rows = rows
80                .into_iter()
81                .filter_map(|row| match eval_filter(filter, &row) {
82                    Ok(true) => Some(Ok(row)),
83                    Ok(false) => None,
84                    Err(err) => Some(Err(err)),
85                })
86                .collect::<Result<Vec<_>, _>>()
87                .map_err(DataServiceError::Executor)?;
88        }
89
90        if !query.aggregates.is_empty() {
91            return aggregate_rows(query, &rows).map_err(DataServiceError::Executor);
92        }
93
94        apply_ordering(&mut rows, query);
95        rows = apply_slice(rows, query);
96        if !query.projection.is_empty() || !query.expr_projection.is_empty() {
97            rows = rows
98                .into_iter()
99                .map(|row| project_row(row, query))
100                .collect::<Result<Vec<_>, _>>()
101                .map_err(DataServiceError::Executor)?;
102        }
103        Ok(rows)
104    }
105
106    pub fn fetch_smart_list(
107        &self,
108        query: &SelectQuery,
109    ) -> Result<SmartList<Record>, DataServiceError<MemoryDataServiceError>> {
110        self.fetch_all(query).map(SmartList::from)
111    }
112
113    pub fn fetch_entities<T>(
114        &self,
115        query: &SelectQuery,
116    ) -> Result<SmartList<T>, DataServiceError<MemoryDataServiceError>>
117    where
118        T: Entity,
119    {
120        self.fetch_all(query)?
121            .into_iter()
122            .map(T::from_record)
123            .collect::<Result<Vec<_>, _>>()
124            .map(SmartList::from)
125            .map_err(DataServiceError::Entity)
126    }
127
128    pub fn fetch_all_with_relation_aggregates(
129        &self,
130        query: &SelectQuery,
131        relation_aggregates: &[RelationAggregate],
132    ) -> Result<Vec<Record>, DataServiceError<MemoryDataServiceError>> {
133        let mut rows = self.fetch_all(query)?;
134        self.enhance_relation_aggregates(&query.entity, &mut rows, relation_aggregates)?;
135        Ok(rows)
136    }
137
138    pub fn fetch_smart_list_with_relation_aggregates(
139        &self,
140        query: &SelectQuery,
141        relation_aggregates: &[RelationAggregate],
142    ) -> Result<SmartList<Record>, DataServiceError<MemoryDataServiceError>> {
143        self.fetch_all_with_relation_aggregates(query, relation_aggregates)
144            .map(SmartList::from)
145    }
146
147    pub fn fetch_entities_with_relation_aggregates<T>(
148        &self,
149        query: &SelectQuery,
150        relation_aggregates: &[RelationAggregate],
151    ) -> Result<SmartList<T>, DataServiceError<MemoryDataServiceError>>
152    where
153        T: Entity,
154    {
155        self.fetch_all_with_relation_aggregates(query, relation_aggregates)?
156            .into_iter()
157            .map(T::from_record)
158            .collect::<Result<Vec<_>, _>>()
159            .map(SmartList::from)
160            .map_err(DataServiceError::Entity)
161    }
162
163    pub fn enhance_relation_aggregates(
164        &self,
165        parent_entity: &str,
166        parent_rows: &mut [Record],
167        relation_aggregates: &[RelationAggregate],
168    ) -> Result<(), DataServiceError<MemoryDataServiceError>> {
169        for aggregate in relation_aggregates {
170            self.enhance_relation_aggregate(parent_entity, parent_rows, aggregate)?;
171        }
172        Ok(())
173    }
174
175    fn enhance_relation_aggregate(
176        &self,
177        parent_entity: &str,
178        parent_rows: &mut [Record],
179        aggregate: &RelationAggregate,
180    ) -> Result<(), DataServiceError<MemoryDataServiceError>> {
181        let descriptor = self.metadata.entity(parent_entity).ok_or_else(|| {
182            DataServiceError::Runtime(RuntimeError::MissingEntity(parent_entity.to_owned()))
183        })?;
184
185        let relation = descriptor
186            .relation_by_name(&aggregate.relation_name)
187            .ok_or_else(|| {
188                DataServiceError::Runtime(RuntimeError::MissingRelation {
189                    entity: parent_entity.to_owned(),
190                    relation: aggregate.relation_name.clone(),
191                })
192            })?;
193
194        let ids = parent_rows
195            .iter()
196            .filter_map(|row| row.get(&relation.local_key).cloned())
197            .collect::<Vec<_>>();
198
199        if ids.is_empty() {
200            let value = default_aggregate_value(aggregate.single_result);
201            for parent in parent_rows.iter_mut() {
202                parent.insert(aggregate.alias.clone(), value.clone());
203            }
204            return Ok(());
205        }
206
207        let mut query = aggregate.query.clone();
208        query.entity = relation.target_entity.clone();
209        query.projection.clear();
210        query.expr_projection.clear();
211        query.order_by.clear();
212        query.slice = None;
213        query.relations.clear();
214        if query.aggregates.is_empty() {
215            let alias = aggregate_alias(aggregate.single_result, &aggregate.alias);
216            query = query.aggregate(Aggregate::count(alias));
217        }
218        if !query
219            .group_by
220            .iter()
221            .any(|field| field == &relation.foreign_key)
222        {
223            query = query.group_by(relation.foreign_key.clone());
224        }
225        query = query.and_filter(Expr::in_list(relation.foreign_key.clone(), ids));
226
227        let aggregate_rows = self.fetch_all(&query)?;
228
229        let mut buckets: BTreeMap<String, Vec<Record>> = BTreeMap::new();
230        for mut row in aggregate_rows {
231            if let Some(key) = row.remove(&relation.foreign_key) {
232                let bucket_key = local_graph_identity_key(&key);
233                buckets.entry(bucket_key).or_default().push(row);
234            }
235        }
236
237        for parent in parent_rows {
238            let value = parent
239                .get(&relation.local_key)
240                .and_then(|local_value| buckets.get(&local_graph_identity_key(local_value)))
241                .map(|rows| local_relation_aggregate_value(rows, aggregate.single_result))
242                .unwrap_or_else(|| default_aggregate_value(aggregate.single_result));
243            parent.insert(aggregate.alias.clone(), value);
244        }
245
246        Ok(())
247    }
248
249    pub fn insert(
250        &self,
251        command: &InsertCommand,
252    ) -> Result<u64, DataServiceError<MemoryDataServiceError>> {
253        self.require_entity(&command.entity)?;
254        let mut data = self
255            .data
256            .lock()
257            .map_err(|_| DataServiceError::Executor(MemoryDataServiceError::Poisoned))?;
258        data.entry(command.entity.clone())
259            .or_default()
260            .push(command.values.clone());
261        Ok(1)
262    }
263
264    pub fn update(
265        &self,
266        command: &UpdateCommand,
267    ) -> Result<u64, DataServiceError<MemoryDataServiceError>> {
268        let (id_property, version_property) = self.id_and_version_properties(&command.entity)?;
269        let mut data = self
270            .data
271            .lock()
272            .map_err(|_| DataServiceError::Executor(MemoryDataServiceError::Poisoned))?;
273        let rows = data.entry(command.entity.clone()).or_default();
274        let Some(row) = rows
275            .iter_mut()
276            .find(|row| row.get(id_property) == Some(&command.id))
277        else {
278            return self.maybe_optimistic_conflict(
279                command.expected_version,
280                &command.entity,
281                &command.id,
282            );
283        };
284
285        if let Some(expected) = command.expected_version {
286            if row.get(version_property) != Some(&Value::I64(expected)) {
287                println!(
288                    "OptimisticLockConflict in memory.rs update! entity={}, id={:?}, expected={}, existing={:?}",
289                    command.entity,
290                    command.id,
291                    expected,
292                    row.get(version_property)
293                );
294                return Err(DataServiceError::Runtime(
295                    RuntimeError::OptimisticLockConflict {
296                        entity: command.entity.clone(),
297                        id: format!("{:?}", command.id),
298                    },
299                ));
300            }
301            row.insert(version_property.to_owned(), Value::I64(expected + 1));
302        }
303
304        for (key, value) in &command.values {
305            row.insert(key.clone(), value.clone());
306        }
307        Ok(1)
308    }
309
310    pub fn delete(
311        &self,
312        command: &DeleteCommand,
313    ) -> Result<u64, DataServiceError<MemoryDataServiceError>> {
314        let (id_property, version_property) = self.id_and_version_properties(&command.entity)?;
315        let mut data = self
316            .data
317            .lock()
318            .map_err(|_| DataServiceError::Executor(MemoryDataServiceError::Poisoned))?;
319        let rows = data.entry(command.entity.clone()).or_default();
320        let Some(index) = rows
321            .iter()
322            .position(|row| row.get(id_property) == Some(&command.id))
323        else {
324            return self.maybe_optimistic_conflict(
325                command.expected_version,
326                &command.entity,
327                &command.id,
328            );
329        };
330
331        if let Some(expected_version) = command.expected_version {
332            if rows[index].get(version_property) != Some(&Value::I64(expected_version)) {
333                return Err(DataServiceError::Runtime(
334                    RuntimeError::OptimisticLockConflict {
335                        entity: command.entity.clone(),
336                        id: format!("{:?}", command.id),
337                    },
338                ));
339            }
340        }
341
342        match command.soft_delete {
343            true => {
344                let next_version = command
345                    .expected_version
346                    .or_else(|| read_i64(rows[index].get(version_property)))
347                    .map(|version| -(version.abs() + 1))
348                    .unwrap_or(-1);
349                rows[index].insert(version_property.to_owned(), Value::I64(next_version));
350            }
351            false => { rows.remove(index); }
352        }
353        Ok(1)
354    }
355
356    pub fn recover(
357        &self,
358        command: &RecoverCommand,
359    ) -> Result<u64, DataServiceError<MemoryDataServiceError>> {
360        let (id_property, version_property) = self.id_and_version_properties(&command.entity)?;
361        let mut data = self
362            .data
363            .lock()
364            .map_err(|_| DataServiceError::Executor(MemoryDataServiceError::Poisoned))?;
365        let rows = data.entry(command.entity.clone()).or_default();
366        let Some(row) = rows
367            .iter_mut()
368            .find(|row| row.get(id_property) == Some(&command.id))
369        else {
370            return Err(DataServiceError::Runtime(
371                RuntimeError::OptimisticLockConflict {
372                    entity: command.entity.clone(),
373                    id: format!("{:?}", command.id),
374                },
375            ));
376        };
377
378        if row.get(version_property) != Some(&Value::I64(command.expected_version)) {
379            return Err(DataServiceError::Runtime(
380                RuntimeError::OptimisticLockConflict {
381                    entity: command.entity.clone(),
382                    id: format!("{:?}", command.id),
383                },
384            ));
385        }
386
387        row.insert(
388            version_property.to_owned(),
389            Value::I64(command.expected_version.abs() + 1),
390        );
391        Ok(1)
392    }
393
394    fn require_entity(&self, entity: &str) -> Result<(), DataServiceError<MemoryDataServiceError>> {
395        self.metadata.entity(entity).map(|_| ()).ok_or_else(|| {
396            DataServiceError::Runtime(RuntimeError::MissingEntity(entity.to_owned()))
397        })
398    }
399
400    fn id_and_version_properties(
401        &self,
402        entity: &str,
403    ) -> Result<(&str, &str), DataServiceError<MemoryDataServiceError>> {
404        let descriptor = self.metadata.entity(entity).ok_or_else(|| {
405            DataServiceError::Runtime(RuntimeError::MissingEntity(entity.to_owned()))
406        })?;
407        let id = descriptor
408            .id_property()
409            .map(|property| property.name.as_str())
410            .unwrap_or("id");
411        let version = descriptor
412            .version_property()
413            .map(|property| property.name.as_str())
414            .unwrap_or("version");
415        Ok((id, version))
416    }
417
418    fn maybe_optimistic_conflict(
419        &self,
420        expected_version: Option<i64>,
421        entity: &str,
422        id: &Value,
423    ) -> Result<u64, DataServiceError<MemoryDataServiceError>> {
424        let Some(_) = expected_version else {
425            return Ok(0);
426        };
427        Err(DataServiceError::Runtime(
428            RuntimeError::OptimisticLockConflict {
429                entity: entity.to_owned(),
430                id: format!("{id:?}"),
431            },
432        ))
433    }
434}
435
436fn default_aggregate_value(single_result: bool) -> Value {
437    match single_result {
438        true => Value::U64(0),
439        false => Value::List(Vec::new()),
440    }
441}
442
443fn aggregate_alias(single_result: bool, alias: &str) -> String {
444    match single_result {
445        true => alias.to_owned(),
446        false => "count".to_owned(),
447    }
448}
449
450fn local_relation_aggregate_value(rows: &[Record], single_result: bool) -> Value {
451    match single_result {
452        true => rows
453            .first()
454            .map(local_single_relation_aggregate_value)
455            .unwrap_or(Value::U64(0)),
456        false => Value::List(rows.iter().cloned().map(Value::object).collect()),
457    }
458}
459
460fn local_single_relation_aggregate_value(row: &Record) -> Value {
461    match row.len() {
462        1 => row.values().next().cloned().unwrap_or(Value::Null),
463        _ => Value::object(row.clone()),
464    }
465}
466
467fn eval_filter(expr: &Expr, row: &Record) -> Result<bool, MemoryDataServiceError> {
468    match expr {
469        Expr::Column(_) | Expr::Value(_) | Expr::Function { .. } => {
470            value_truthy(&eval_value(expr, row)?)
471        }
472        Expr::Binary { left, op, right } => {
473            let left = eval_value(left, row)?;
474            let right = eval_value(right, row)?;
475            eval_binary(&left, *op, &right)
476        }
477        Expr::SubQuery { .. } => Err(MemoryDataServiceError::UnsupportedExpression(
478            "subquery filters require a SQL executor".to_owned(),
479        )),
480        Expr::Between { expr, lower, upper } => {
481            let value = eval_value(expr, row)?;
482            let lower = eval_value(lower, row)?;
483            let upper = eval_value(upper, row)?;
484            Ok(compare_values(&value, &lower) != Some(Ordering::Less)
485                && compare_values(&value, &upper) != Some(Ordering::Greater))
486        }
487        Expr::IsNull(expr) => Ok(matches!(eval_value(expr, row)?, Value::Null)),
488        Expr::IsNotNull(expr) => Ok(!matches!(eval_value(expr, row)?, Value::Null)),
489        Expr::And(parts) => {
490            for part in parts {
491                if !eval_filter(part, row)? {
492                    return Ok(false);
493                }
494            }
495            Ok(true)
496        }
497        Expr::Or(parts) => {
498            for part in parts {
499                if eval_filter(part, row)? {
500                    return Ok(true);
501                }
502            }
503            Ok(false)
504        }
505        Expr::Not(expr) => Ok(!eval_filter(expr, row)?),
506    }
507}
508
509fn eval_value(expr: &Expr, row: &Record) -> Result<Value, MemoryDataServiceError> {
510    match expr {
511        Expr::Column(column) => Ok(row.get(column).cloned().unwrap_or(Value::Null)),
512        Expr::Value(value) => Ok(value.clone()),
513        Expr::Function { function, args } => eval_function(*function, args, row),
514        other => Err(MemoryDataServiceError::UnsupportedExpression(format!(
515            "cannot evaluate {other:?} as a scalar value"
516        ))),
517    }
518}
519
520fn eval_function(
521    function: ExprFunction,
522    args: &[Expr],
523    row: &Record,
524) -> Result<Value, MemoryDataServiceError> {
525    match function {
526        ExprFunction::Soundex => {
527            let [arg] = args else {
528                return Err(MemoryDataServiceError::UnsupportedExpression(
529                    "SOUNDEX expects exactly one argument".to_owned(),
530                ));
531            };
532            match eval_value(arg, row)? {
533                Value::Text(value) => Ok(Value::Text(soundex(&value))),
534                Value::Null => Ok(Value::Null),
535                other => Err(MemoryDataServiceError::UnsupportedExpression(format!(
536                    "SOUNDEX expects text, got {other:?}"
537                ))),
538            }
539        }
540        ExprFunction::Gbk => {
541            let [arg] = args else {
542                return Err(MemoryDataServiceError::UnsupportedExpression(
543                    "GBK expects exactly one argument".to_owned(),
544                ));
545            };
546            eval_value(arg, row)
547        }
548        other => Err(MemoryDataServiceError::UnsupportedExpression(format!(
549            "function {other:?} is only supported by SQL execution"
550        ))),
551    }
552}
553
554fn eval_binary(left: &Value, op: BinaryOp, right: &Value) -> Result<bool, MemoryDataServiceError> {
555    match op {
556        BinaryOp::Eq => Ok(values_equal(left, right)),
557        BinaryOp::Ne => Ok(!values_equal(left, right)),
558        BinaryOp::Gt => Ok(compare_values(left, right) == Some(Ordering::Greater)),
559        BinaryOp::Gte => Ok(matches!(
560            compare_values(left, right),
561            Some(Ordering::Greater | Ordering::Equal)
562        )),
563        BinaryOp::Lt => Ok(compare_values(left, right) == Some(Ordering::Less)),
564        BinaryOp::Lte => Ok(matches!(
565            compare_values(left, right),
566            Some(Ordering::Less | Ordering::Equal)
567        )),
568        BinaryOp::Like => match (left, right) {
569            (Value::Text(value), Value::Text(pattern)) => Ok(like_matches(value, pattern)),
570            _ => Ok(false),
571        },
572        BinaryOp::NotLike => match (left, right) {
573            (Value::Text(value), Value::Text(pattern)) => Ok(!like_matches(value, pattern)),
574            _ => Ok(true),
575        },
576        BinaryOp::In | BinaryOp::InLarge => match right {
577            Value::List(values) => Ok(values.iter().any(|value| values_equal(left, value))),
578            _ => Err(MemoryDataServiceError::UnsupportedExpression(
579                "IN expects a list value".to_owned(),
580            )),
581        },
582        BinaryOp::NotIn | BinaryOp::NotInLarge => match right {
583            Value::List(values) => Ok(!values.iter().any(|value| values_equal(left, value))),
584            _ => Err(MemoryDataServiceError::UnsupportedExpression(
585                "NOT IN expects a list value".to_owned(),
586            )),
587        },
588    }
589}
590
591fn value_truthy(value: &Value) -> Result<bool, MemoryDataServiceError> {
592    match value {
593        Value::Bool(value) => Ok(*value),
594        Value::Null => Ok(false),
595        other => Err(MemoryDataServiceError::UnsupportedExpression(format!(
596            "non-boolean expression result: {other:?}"
597        ))),
598    }
599}
600
601fn values_equal(left: &Value, right: &Value) -> bool {
602    match (left, right) {
603        (Value::I64(left), Value::U64(right)) if *left >= 0 => *left as u64 == *right,
604        (Value::U64(left), Value::I64(right)) if *right >= 0 => *left == *right as u64,
605        _ => left == right,
606    }
607}
608
609fn compare_values(left: &Value, right: &Value) -> Option<Ordering> {
610    match (left, right) {
611        (Value::I64(left), Value::I64(right)) => Some(left.cmp(right)),
612        (Value::U64(left), Value::U64(right)) => Some(left.cmp(right)),
613        (Value::I64(left), Value::U64(right)) if *left >= 0 => Some((*left as u64).cmp(right)),
614        (Value::U64(left), Value::I64(right)) if *right >= 0 => Some(left.cmp(&(*right as u64))),
615        (Value::F64(left), Value::F64(right)) => left.partial_cmp(right),
616        (Value::Decimal(left), Value::Decimal(right)) => Some(left.cmp(right)),
617        (Value::Text(left), Value::Text(right)) => Some(left.cmp(right)),
618        (Value::Date(left), Value::Date(right)) => Some(left.cmp(right)),
619        (Value::Timestamp(left), Value::Timestamp(right)) => Some(left.cmp(right)),
620        _ => None,
621    }
622}
623
624fn like_matches(value: &str, pattern: &str) -> bool {
625    if pattern == "%" {
626        return true;
627    }
628    match (pattern.strip_prefix('%'), pattern.strip_suffix('%')) {
629        (Some(inner), Some(_)) if pattern.len() >= 2 => value.contains(&inner[..inner.len() - 1]),
630        (Some(suffix), None) => value.ends_with(suffix),
631        (None, Some(prefix)) => value.starts_with(prefix),
632        _ => value == pattern,
633    }
634}
635
636fn soundex(value: &str) -> String {
637    let mut letters = value
638        .chars()
639        .filter(|ch| ch.is_ascii_alphabetic())
640        .map(|ch| ch.to_ascii_uppercase());
641    let Some(first) = letters.next() else {
642        return "0000".to_owned();
643    };
644
645    let mut output = String::with_capacity(4);
646    output.push(first);
647    let mut previous_code = soundex_code(first);
648
649    for ch in letters {
650        let code = soundex_code(ch);
651        if code != '0' && code != previous_code {
652            output.push(code);
653            if output.len() == 4 {
654                return output;
655            }
656        }
657        previous_code = code;
658    }
659
660    while output.len() < 4 {
661        output.push('0');
662    }
663    output
664}
665
666fn soundex_code(ch: char) -> char {
667    match ch {
668        'B' | 'F' | 'P' | 'V' => '1',
669        'C' | 'G' | 'J' | 'K' | 'Q' | 'S' | 'X' | 'Z' => '2',
670        'D' | 'T' => '3',
671        'L' => '4',
672        'M' | 'N' => '5',
673        'R' => '6',
674        _ => '0',
675    }
676}
677
678fn apply_ordering(rows: &mut [Record], query: &SelectQuery) {
679    for order in query.order_by.iter().rev() {
680        rows.sort_by(|left, right| {
681            let resolve = |row: &Record| -> Option<Value> {
682                match &order.expr {
683                    Some(expr) => eval_value(expr, row).ok(),
684                    None => row.get(&order.field).cloned(),
685                }
686            };
687            let left_value = resolve(left);
688            let right_value = resolve(right);
689            let ordering = match (left_value.as_ref(), right_value.as_ref()) {
690                (Some(left), Some(right)) => compare_values(left, right).unwrap_or(Ordering::Equal),
691                (None, Some(_)) => Ordering::Less,
692                (Some(_), None) => Ordering::Greater,
693                (None, None) => Ordering::Equal,
694            };
695            match order.direction {
696                SortDirection::Asc => ordering,
697                SortDirection::Desc => ordering.reverse(),
698            }
699        });
700    }
701}
702
703fn apply_slice(rows: Vec<Record>, query: &SelectQuery) -> Vec<Record> {
704    let Some(slice) = query.slice else {
705        return rows;
706    };
707    let offset = usize::try_from(slice.offset).unwrap_or(usize::MAX);
708    let limit = slice
709        .limit
710        .and_then(|limit| usize::try_from(limit).ok())
711        .unwrap_or(usize::MAX);
712    rows.into_iter().skip(offset).take(limit).collect()
713}
714
715fn project_row(row: Record, query: &SelectQuery) -> Result<Record, MemoryDataServiceError> {
716    let mut output: Record = query
717        .projection
718        .iter()
719        .filter_map(|field| row.get(field).cloned().map(|value| (field.clone(), value)))
720        .collect();
721    for projection in &query.expr_projection {
722        output.insert(
723            projection.alias.clone(),
724            eval_value(&projection.expr, &row)?,
725        );
726    }
727    Ok(output)
728}
729
730fn aggregate_rows(
731    query: &SelectQuery,
732    rows: &[Record],
733) -> Result<Vec<Record>, MemoryDataServiceError> {
734    let mut groups: BTreeMap<Vec<String>, Vec<&Record>> = BTreeMap::new();
735    match query.group_by.is_empty() {
736        true => { groups.insert(Vec::new(), rows.iter().collect()); }
737        false => {
738            for row in rows {
739                let key = query
740                    .group_by
741                    .iter()
742                    .map(|field| row.get(field).map(value_key).unwrap_or_default())
743                    .collect::<Vec<_>>();
744                groups.entry(key).or_default().push(row);
745            }
746        }
747    }
748
749    let rows = groups
750        .into_values()
751        .map(|rows| {
752            let mut output = Record::new();
753            if let Some(first) = rows.first() {
754                for field in &query.group_by {
755                    if let Some(value) = first.get(field) {
756                        output.insert(field.clone(), value.clone());
757                    }
758                }
759            }
760            for aggregate in &query.aggregates {
761                let value = match aggregate.function {
762                    AggregateFunction::Count => count_rows(&rows, &aggregate.field),
763                    AggregateFunction::Sum => numeric_sum(&rows, &aggregate.field)?,
764                    AggregateFunction::Avg => numeric_avg(&rows, &aggregate.field)?,
765                    AggregateFunction::Min => min_max(&rows, &aggregate.field, false)?,
766                    AggregateFunction::Max => min_max(&rows, &aggregate.field, true)?,
767                    AggregateFunction::Stddev => numeric_stddev(&rows, &aggregate.field, true)?,
768                    AggregateFunction::StddevPop => numeric_stddev(&rows, &aggregate.field, false)?,
769                    AggregateFunction::VarSamp => numeric_variance(&rows, &aggregate.field, true)?,
770                    AggregateFunction::VarPop => numeric_variance(&rows, &aggregate.field, false)?,
771                    AggregateFunction::BitAnd => {
772                        bit_aggregate(&rows, &aggregate.field, BitOp::And)?
773                    }
774                    AggregateFunction::BitOr => bit_aggregate(&rows, &aggregate.field, BitOp::Or)?,
775                    AggregateFunction::BitXor => {
776                        bit_aggregate(&rows, &aggregate.field, BitOp::Xor)?
777                    }
778                };
779                output.insert(aggregate.alias.clone(), value);
780            }
781            for projection in &query.expr_projection {
782                output.insert(
783                    projection.alias.clone(),
784                    eval_value(&projection.expr, &output)?,
785                );
786            }
787            Ok(output)
788        })
789        .collect::<Result<Vec<_>, _>>()?;
790    let Some(having) = &query.having else {
791        return Ok(rows);
792    };
793    rows.into_iter()
794        .filter_map(|row| match eval_filter(having, &row) {
795            Ok(true) => Some(Ok(row)),
796            Ok(false) => None,
797            Err(err) => Some(Err(err)),
798        })
799        .collect()
800}
801
802/// Count rows, treating `"*"` as counting all rows and other fields as counting non-null values.
803fn count_rows(rows: &[&Record], field: &str) -> Value {
804    let count = match field {
805        "*" => rows.len(),
806        _ => rows
807            .iter()
808            .filter(|row| !matches!(row.get(field), None | Some(Value::Null)))
809            .count(),
810    };
811    Value::U64(count as u64)
812}
813
814fn numeric_sum(rows: &[&Record], field: &str) -> Result<Value, MemoryDataServiceError> {
815    let mut decimal_sum = Decimal::ZERO;
816    let mut integer_sum: i128 = 0;
817    let mut saw_decimal = false;
818    for value in rows.iter().filter_map(|row| row.get(field)) {
819        match value {
820            Value::I64(value) => {
821                integer_sum += i128::from(*value);
822                decimal_sum += Decimal::from(*value);
823            }
824            Value::U64(value) => {
825                integer_sum += i128::from(*value);
826                decimal_sum += Decimal::from(*value);
827            }
828            Value::F64(value) => {
829                saw_decimal = true;
830                decimal_sum += decimal_from_f64(*value);
831            }
832            Value::Decimal(value) => {
833                saw_decimal = true;
834                decimal_sum += *value;
835            }
836            Value::Null => {}
837            other => {
838                return Err(MemoryDataServiceError::UnsupportedAggregate(format!(
839                    "SUM does not support {other:?}"
840                )));
841            }
842        }
843    }
844    match (saw_decimal, integer_sum >= 0) {
845        (true, _) => Ok(Value::Decimal(decimal_sum)),
846        (_, true) => Ok(Value::U64(integer_sum as u64)),
847        _ => Ok(Value::I64(integer_sum as i64)),
848    }
849}
850
851fn numeric_avg(rows: &[&Record], field: &str) -> Result<Value, MemoryDataServiceError> {
852    let mut sum = Decimal::ZERO;
853    let mut count: u64 = 0;
854    for value in rows.iter().filter_map(|row| row.get(field)) {
855        match value {
856            Value::I64(value) => {
857                sum += Decimal::from(*value);
858                count += 1;
859            }
860            Value::U64(value) => {
861                sum += Decimal::from(*value);
862                count += 1;
863            }
864            Value::F64(value) => {
865                sum += decimal_from_f64(*value);
866                count += 1;
867            }
868            Value::Decimal(value) => {
869                sum += *value;
870                count += 1;
871            }
872            Value::Null => {}
873            other => {
874                return Err(MemoryDataServiceError::UnsupportedAggregate(format!(
875                    "AVG does not support {other:?}"
876                )));
877            }
878        }
879    }
880    Ok((count > 0)
881        .then(|| Value::Decimal(sum / Decimal::from(count)))
882        .unwrap_or(Value::Null))
883}
884
885fn decimal_from_f64(value: f64) -> Decimal {
886    Decimal::from_f64_retain(value).unwrap_or(Decimal::ZERO)
887}
888
889fn numeric_values(rows: &[&Record], field: &str) -> Result<Vec<f64>, MemoryDataServiceError> {
890    rows.iter()
891        .filter_map(|row| row.get(field))
892        .filter(|value| !matches!(value, Value::Null))
893        .map(|value| match value {
894            Value::I64(value) => Ok(*value as f64),
895            Value::U64(value) => Ok(*value as f64),
896            Value::F64(value) => Ok(*value),
897            Value::Decimal(value) => value.to_f64().ok_or_else(|| {
898                MemoryDataServiceError::UnsupportedAggregate(format!(
899                    "cannot convert decimal {value} to f64 for statistical aggregate"
900                ))
901            }),
902            other => Err(MemoryDataServiceError::UnsupportedAggregate(format!(
903                "numeric aggregate does not support {other:?}"
904            ))),
905        })
906        .collect()
907}
908
909fn numeric_variance(
910    rows: &[&Record],
911    field: &str,
912    sample: bool,
913) -> Result<Value, MemoryDataServiceError> {
914    let values = numeric_values(rows, field)?;
915    let count = values.len();
916    if count == 0 || (sample && count < 2) {
917        return Ok(Value::Null);
918    }
919    let mean = values.iter().sum::<f64>() / count as f64;
920    let sum = values
921        .iter()
922        .map(|value| {
923            let diff = value - mean;
924            diff * diff
925        })
926        .sum::<f64>();
927    let denominator = match sample { true => count - 1, false => count } as f64;
928    Ok(Value::Decimal(decimal_from_f64(sum / denominator)))
929}
930
931fn numeric_stddev(
932    rows: &[&Record],
933    field: &str,
934    sample: bool,
935) -> Result<Value, MemoryDataServiceError> {
936    Ok(match numeric_variance(rows, field, sample)? {
937        Value::Decimal(value) => {
938            Value::Decimal(decimal_from_f64(value.to_f64().unwrap_or(0.0).sqrt()))
939        }
940        Value::Null => Value::Null,
941        other => other,
942    })
943}
944
945#[derive(Debug, Clone, Copy)]
946enum BitOp {
947    And,
948    Or,
949    Xor,
950}
951
952fn bit_aggregate(
953    rows: &[&Record],
954    field: &str,
955    op: BitOp,
956) -> Result<Value, MemoryDataServiceError> {
957    let mut selected: Option<i64> = None;
958    for value in rows.iter().filter_map(|row| row.get(field)) {
959        let value = match value {
960            Value::I64(value) => *value,
961            Value::U64(value) => i64::try_from(*value).map_err(|_| {
962                MemoryDataServiceError::UnsupportedAggregate(format!(
963                    "BIT aggregate u64 {value} exceeds i64 range"
964                ))
965            })?,
966            Value::Null => continue,
967            other => {
968                return Err(MemoryDataServiceError::UnsupportedAggregate(format!(
969                    "BIT aggregate does not support {other:?}"
970                )));
971            }
972        };
973        selected = Some(match (selected, op) {
974            (None, _) => value,
975            (Some(current), BitOp::And) => current & value,
976            (Some(current), BitOp::Or) => current | value,
977            (Some(current), BitOp::Xor) => current ^ value,
978        });
979    }
980    Ok(selected.map(Value::I64).unwrap_or(Value::Null))
981}
982
983fn min_max(rows: &[&Record], field: &str, max: bool) -> Result<Value, MemoryDataServiceError> {
984    let mut selected: Option<Value> = None;
985    for value in rows.iter().filter_map(|row| row.get(field)) {
986        if matches!(value, Value::Null) {
987            continue;
988        }
989        match &selected {
990            None => selected = Some(value.clone()),
991            Some(current) => {
992                let Some(ordering) = compare_values(value, current) else {
993                    return Err(MemoryDataServiceError::UnsupportedAggregate(format!(
994                        "MIN/MAX does not support {value:?}"
995                    )));
996                };
997                if (max && ordering == Ordering::Greater) || (!max && ordering == Ordering::Less) {
998                    selected = Some(value.clone());
999                }
1000            }
1001        }
1002    }
1003    Ok(selected.unwrap_or(Value::Null))
1004}
1005
1006fn value_key(value: &Value) -> String {
1007    match value {
1008        Value::Null => "null".to_owned(),
1009        Value::Bool(value) => format!("b:{value}"),
1010        Value::I64(value) => format!("i:{value}"),
1011        Value::U64(value) => format!("u:{value}"),
1012        Value::F64(value) => format!("f:{value}"),
1013        Value::Decimal(value) => format!("d:{value}"),
1014        Value::Text(value) => format!("t:{value}"),
1015        Value::Json(value) => format!("j:{value}"),
1016        Value::Date(value) => format!("d:{value}"),
1017        Value::Timestamp(value) => format!("ts:{}", value.to_rfc3339()),
1018        Value::Object(_) => "object".to_owned(),
1019        Value::List(_) => "list".to_owned(),
1020    }
1021}
1022
1023fn read_i64(value: Option<&Value>) -> Option<i64> {
1024    match value {
1025        Some(Value::I64(value)) => Some(*value),
1026        _ => None,
1027    }
1028}
1029
1030fn local_graph_identity_key(value: &Value) -> String {
1031    match value {
1032        Value::I64(val) if *val >= 0 => format!("u:{}", *val as u64),
1033        Value::U64(val) => format!("u:{val}"),
1034        Value::Null => "null".to_owned(),
1035        Value::Bool(v) => format!("b:{v}"),
1036        Value::I64(v) => format!("i:{v}"),
1037        Value::F64(v) => format!("f:{v}"),
1038        Value::Decimal(v) => format!("d:{v}"),
1039        Value::Text(v) => format!("t:{v}"),
1040        Value::Json(v) => format!("j:{v}"),
1041        Value::Date(v) => format!("d:{v}"),
1042        Value::Timestamp(v) => format!("ts:{}", v.to_rfc3339()),
1043        Value::Object(_) => "o".to_owned(),
1044        Value::List(_) => "l".to_owned(),
1045    }
1046}