gitql_std/
aggregation.rs

1use std::cmp::Ordering;
2use std::collections::HashMap;
3use std::sync::OnceLock;
4
5use gitql_ast::types::any::AnyType;
6use gitql_ast::types::boolean::BoolType;
7use gitql_ast::types::date::DateType;
8use gitql_ast::types::datetime::DateTimeType;
9use gitql_ast::types::dynamic::DynamicType;
10use gitql_ast::types::float::FloatType;
11use gitql_ast::types::integer::IntType;
12use gitql_ast::types::null::NullType;
13use gitql_ast::types::optional::OptionType;
14use gitql_ast::types::text::TextType;
15use gitql_ast::types::time::TimeType;
16use gitql_ast::types::varargs::VarargsType;
17use gitql_ast::types::variant::VariantType;
18use gitql_core::signature::AggregationFunction;
19use gitql_core::signature::Signature;
20use gitql_core::values::array::ArrayValue;
21use gitql_core::values::boolean::BoolValue;
22use gitql_core::values::integer::IntValue;
23use gitql_core::values::null::NullValue;
24use gitql_core::values::text::TextValue;
25use gitql_core::values::Value;
26
27use crate::meta_types::array_of_type;
28use crate::meta_types::first_element_type;
29
30pub fn aggregation_functions() -> &'static HashMap<&'static str, AggregationFunction> {
31    static HASHMAP: OnceLock<HashMap<&'static str, AggregationFunction>> = OnceLock::new();
32    HASHMAP.get_or_init(|| {
33        let mut map: HashMap<&'static str, AggregationFunction> = HashMap::new();
34        map.insert("max", aggregation_max);
35        map.insert("min", aggregation_min);
36        map.insert("sum", aggregation_sum);
37        map.insert("avg", aggregation_average);
38        map.insert("count", aggregation_count);
39        map.insert("group_concat", aggregation_group_concat);
40        map.insert("bool_and", aggregation_bool_and);
41        map.insert("bool_or", aggregation_bool_or);
42        map.insert("bit_and", aggregation_bit_and);
43        map.insert("bit_or", aggregation_bit_or);
44        map.insert("bit_xor", aggregation_bit_xor);
45        map.insert("array_agg", aggregation_array_agg);
46        map
47    })
48}
49
50pub fn aggregation_function_signatures() -> HashMap<&'static str, Signature> {
51    let mut map: HashMap<&'static str, Signature> = HashMap::new();
52    map.insert(
53        "max",
54        Signature {
55            parameters: vec![Box::new(VariantType {
56                variants: vec![
57                    Box::new(IntType),
58                    Box::new(FloatType),
59                    Box::new(TextType),
60                    Box::new(DateType),
61                    Box::new(TimeType),
62                    Box::new(DateTimeType),
63                ],
64            })],
65            return_type: Box::new(DynamicType {
66                function: first_element_type,
67            }),
68        },
69    );
70    map.insert(
71        "min",
72        Signature {
73            parameters: vec![Box::new(VariantType {
74                variants: vec![
75                    Box::new(IntType),
76                    Box::new(FloatType),
77                    Box::new(TextType),
78                    Box::new(DateType),
79                    Box::new(TimeType),
80                    Box::new(DateTimeType),
81                ],
82            })],
83            return_type: Box::new(DynamicType {
84                function: first_element_type,
85            }),
86        },
87    );
88    map.insert(
89        "sum",
90        Signature {
91            parameters: vec![Box::new(IntType)],
92            return_type: Box::new(IntType),
93        },
94    );
95    map.insert(
96        "avg",
97        Signature {
98            parameters: vec![Box::new(IntType)],
99            return_type: Box::new(IntType),
100        },
101    );
102    map.insert(
103        "count",
104        Signature {
105            parameters: vec![Box::new(OptionType {
106                base: Some(Box::new(AnyType)),
107            })],
108            return_type: Box::new(IntType),
109        },
110    );
111    map.insert(
112        "group_concat",
113        Signature {
114            parameters: vec![Box::new(VarargsType {
115                base: Box::new(AnyType),
116            })],
117            return_type: Box::new(TextType),
118        },
119    );
120    map.insert(
121        "bool_and",
122        Signature {
123            parameters: vec![Box::new(BoolType)],
124            return_type: Box::new(BoolType),
125        },
126    );
127    map.insert(
128        "bool_or",
129        Signature {
130            parameters: vec![Box::new(BoolType)],
131            return_type: Box::new(BoolType),
132        },
133    );
134    map.insert(
135        "bit_and",
136        Signature {
137            parameters: vec![Box::new(IntType)],
138            return_type: Box::new(IntType),
139        },
140    );
141    map.insert(
142        "bit_or",
143        Signature {
144            parameters: vec![Box::new(IntType)],
145            return_type: Box::new(IntType),
146        },
147    );
148    map.insert(
149        "bit_xor",
150        Signature {
151            parameters: vec![Box::new(IntType)],
152            return_type: Box::new(IntType),
153        },
154    );
155    map.insert(
156        "array_agg",
157        Signature {
158            parameters: vec![Box::new(AnyType)],
159            return_type: Box::new(DynamicType {
160                function: |elements| array_of_type(first_element_type(elements)),
161            }),
162        },
163    );
164    map
165}
166
167pub fn aggregation_max(group_values: &[Vec<Box<dyn Value>>]) -> Box<dyn Value> {
168    let mut max_value = &group_values[0][0];
169    for row_values in group_values {
170        let single_value = &row_values[0];
171        if max_value.compare(single_value) == Some(Ordering::Less) {
172            max_value = single_value;
173        }
174    }
175    max_value.clone()
176}
177
178pub fn aggregation_min(group_values: &[Vec<Box<dyn Value>>]) -> Box<dyn Value> {
179    let mut min_value = &group_values[0][0];
180    for row_values in group_values {
181        let single_value = &row_values[0];
182        if min_value.compare(single_value) == Some(Ordering::Greater) {
183            min_value = single_value;
184        }
185    }
186    min_value.clone()
187}
188
189pub fn aggregation_sum(group_values: &[Vec<Box<dyn Value>>]) -> Box<dyn Value> {
190    let mut sum: i64 = 0;
191    for row_values in group_values {
192        if let Some(int_value) = row_values[0].as_any().downcast_ref::<IntValue>() {
193            sum += int_value.value;
194        }
195    }
196    Box::new(IntValue { value: sum })
197}
198
199pub fn aggregation_average(group_values: &[Vec<Box<dyn Value>>]) -> Box<dyn Value> {
200    let mut sum: i64 = 0;
201    for row_values in group_values {
202        if let Some(int_value) = row_values[0].as_any().downcast_ref::<IntValue>() {
203            sum += int_value.value;
204        }
205    }
206    let count: i64 = group_values[0].len().try_into().unwrap();
207    Box::new(IntValue { value: sum / count })
208}
209
210pub fn aggregation_count(group_values: &[Vec<Box<dyn Value>>]) -> Box<dyn Value> {
211    Box::new(IntValue {
212        value: group_values.len() as i64,
213    })
214}
215
216pub fn aggregation_group_concat(group_values: &[Vec<Box<dyn Value>>]) -> Box<dyn Value> {
217    let mut string_values: Vec<String> = vec![];
218    for row_values in group_values {
219        for value in row_values {
220            string_values.push(value.literal());
221        }
222    }
223    Box::new(TextValue {
224        value: string_values.concat(),
225    })
226}
227
228pub fn aggregation_bool_and(group_values: &[Vec<Box<dyn Value>>]) -> Box<dyn Value> {
229    for row_values in group_values {
230        if let Some(bool_value) = row_values[0].as_any().downcast_ref::<BoolValue>() {
231            if !bool_value.value {
232                return Box::new(BoolValue { value: false });
233            }
234        }
235    }
236    Box::new(BoolValue { value: true })
237}
238
239pub fn aggregation_bool_or(group_values: &[Vec<Box<dyn Value>>]) -> Box<dyn Value> {
240    for row_values in group_values {
241        if let Some(bool_value) = row_values[0].as_any().downcast_ref::<BoolValue>() {
242            if bool_value.value {
243                return Box::new(BoolValue { value: true });
244            }
245        }
246    }
247    Box::new(BoolValue { value: false })
248}
249
250pub fn aggregation_bit_and(group_values: &[Vec<Box<dyn Value>>]) -> Box<dyn Value> {
251    let mut value: i64 = 1;
252    let mut has_non_null = false;
253    for row_values in group_values {
254        if row_values[0].data_type().is_null() {
255            continue;
256        }
257
258        if let Some(int_value) = row_values[0].as_any().downcast_ref::<IntValue>() {
259            value &= int_value.value;
260            has_non_null = true;
261        }
262    }
263
264    if has_non_null {
265        Box::new(IntValue { value })
266    } else {
267        Box::new(NullValue)
268    }
269}
270
271pub fn aggregation_bit_or(group_values: &[Vec<Box<dyn Value>>]) -> Box<dyn Value> {
272    let mut value: i64 = 0;
273    let mut has_non_null = false;
274    for row_values in group_values {
275        if row_values[0].data_type().is_null() {
276            continue;
277        }
278
279        if let Some(int_value) = row_values[0].as_any().downcast_ref::<IntValue>() {
280            value |= int_value.value;
281            has_non_null = true;
282        }
283    }
284
285    if has_non_null {
286        Box::new(IntValue { value })
287    } else {
288        Box::new(NullValue)
289    }
290}
291
292pub fn aggregation_bit_xor(group_values: &[Vec<Box<dyn Value>>]) -> Box<dyn Value> {
293    let mut value: i64 = 0;
294    let mut has_non_null = false;
295    for row_values in group_values {
296        if row_values[0].data_type().is_null() {
297            continue;
298        }
299
300        if let Some(int_value) = row_values[0].as_any().downcast_ref::<IntValue>() {
301            value ^= int_value.value;
302            has_non_null = true;
303        }
304    }
305
306    if has_non_null {
307        Box::new(IntValue { value })
308    } else {
309        Box::new(NullValue)
310    }
311}
312
313pub fn aggregation_array_agg(group_values: &[Vec<Box<dyn Value>>]) -> Box<dyn Value> {
314    let mut array: Vec<Box<dyn Value>> = vec![];
315    for row_values in group_values {
316        array.push(row_values[0].clone());
317    }
318
319    let element_type = if array.is_empty() {
320        Box::new(NullType)
321    } else {
322        array[0].data_type()
323    };
324
325    Box::new(ArrayValue {
326        values: array,
327        base_type: element_type,
328    })
329}