alopex_core/sql/
subquery.rs1use std::collections::HashMap;
8use std::hash::Hash;
9
10pub fn nested_scan<T, E>(execute_subplan: impl FnOnce() -> Result<Vec<T>, E>) -> Result<Vec<T>, E> {
14 execute_subplan()
15}
16
17pub fn semi_join_probe<T, E>(
22 candidates: &[T],
23 mut is_match: impl FnMut(&T) -> Result<bool, E>,
24) -> Result<bool, E> {
25 for candidate in candidates {
26 if is_match(candidate)? {
27 return Ok(true);
28 }
29 }
30 Ok(false)
31}
32
33#[derive(Debug, Clone)]
38pub struct MaterializeCache<K, V> {
39 values: HashMap<K, V>,
40}
41
42impl<K, V> Default for MaterializeCache<K, V> {
43 fn default() -> Self {
44 Self {
45 values: HashMap::new(),
46 }
47 }
48}
49
50impl<K: Hash + Eq, V: Clone> MaterializeCache<K, V> {
51 pub fn new() -> Self {
53 Self::default()
54 }
55
56 pub fn get_or_try_insert_with<E>(
58 &mut self,
59 key: K,
60 materialize: impl FnOnce() -> Result<V, E>,
61 ) -> Result<V, E> {
62 if let Some(value) = self.values.get(&key) {
63 return Ok(value.clone());
64 }
65
66 let value = materialize()?;
67 self.values.insert(key, value.clone());
68 Ok(value)
69 }
70}
71
72pub fn materialize_cache<K: Hash + Eq, V: Clone>() -> MaterializeCache<K, V> {
74 MaterializeCache::new()
75}
76
77#[cfg(test)]
78mod tests {
79 use super::{materialize_cache, nested_scan, semi_join_probe};
80
81 #[test]
82 fn nested_scan_delegates_to_executor() {
83 let rows = nested_scan(|| Ok::<_, ()>(vec![1, 2, 3])).unwrap();
84
85 assert_eq!(rows, vec![1, 2, 3]);
86 }
87
88 #[test]
89 fn semi_join_probe_short_circuits_on_match() {
90 let mut visited = Vec::new();
91 let matched = semi_join_probe(&[1, 2, 3], |value| {
92 visited.push(*value);
93 Ok::<_, ()>(*value == 2)
94 })
95 .unwrap();
96
97 assert!(matched);
98 assert_eq!(visited, vec![1, 2]);
99 }
100
101 #[test]
102 fn materialize_cache_runs_uncached_materializer_once() {
103 let mut cache = materialize_cache();
104 let mut calls = 0;
105
106 let first = cache
107 .get_or_try_insert_with("subquery", || {
108 calls += 1;
109 Ok::<_, ()>(vec![1, 2])
110 })
111 .unwrap();
112 let second = cache
113 .get_or_try_insert_with("subquery", || {
114 calls += 1;
115 Ok::<_, ()>(vec![3, 4])
116 })
117 .unwrap();
118
119 assert_eq!(first, vec![1, 2]);
120 assert_eq!(second, vec![1, 2]);
121 assert_eq!(calls, 1);
122 }
123}