gitql_engine/
engine_ordering.rs

1use std::cmp::Ordering;
2use std::collections::HashMap;
3
4use gitql_ast::statement::NullsOrderPolicy;
5use gitql_ast::statement::OrderByStatement;
6use gitql_ast::statement::SortingOrder;
7use gitql_core::environment::Environment;
8use gitql_core::object::GitQLObject;
9use gitql_core::object::Group;
10use gitql_core::values::null::NullValue;
11use gitql_core::values::Value;
12
13use crate::engine_evaluator::evaluate_expression;
14
15pub(crate) fn execute_order_by_statement(
16    env: &mut Environment,
17    statement: &OrderByStatement,
18    gitql_object: &mut GitQLObject,
19    group_index: usize,
20) -> Result<(), String> {
21    if gitql_object.is_empty() || group_index >= gitql_object.len() {
22        return Ok(());
23    }
24
25    let main_group: &mut Group = &mut gitql_object.groups[group_index];
26    if main_group.is_empty() {
27        return Ok(());
28    }
29
30    let rows_len = main_group.rows.len();
31    let arguments_len = statement.arguments.len();
32    let main_group_rows = &main_group.rows;
33    let titles = &gitql_object.titles;
34
35    let mut eval_map: HashMap<usize, Vec<Box<dyn Value>>> = HashMap::with_capacity(rows_len);
36
37    for row in main_group_rows.iter() {
38        let row_addr = row.values.as_ptr() as usize;
39        let mut arguments_values: Vec<Box<dyn Value>> = Vec::with_capacity(arguments_len);
40        for argument in statement.arguments.iter() {
41            // No need to compare if the ordering argument is constants
42            if argument.is_const() {
43                arguments_values.push(Box::new(NullValue));
44                continue;
45            }
46
47            let value = &evaluate_expression(env, argument, titles, &row.values)?;
48            arguments_values.push(value.to_owned());
49        }
50
51        eval_map.insert(row_addr, arguments_values);
52    }
53
54    main_group.rows.sort_by(|a, b| {
55        for arg_index in 0..arguments_len {
56            let argument = &statement.arguments[arg_index];
57            // No need to compare if the ordering argument is constants
58            if argument.is_const() {
59                continue;
60            }
61
62            // Use the Memory address of A, B as Map keys
63            let a_addr = a.values.as_ptr() as usize;
64            let b_addr = b.values.as_ptr() as usize;
65
66            // Get pre evaluated values from the eval map using addr as key, arg index as offset
67            let a_value = &eval_map.get(&a_addr).unwrap()[arg_index];
68            let b_value = &eval_map.get(&b_addr).unwrap()[arg_index];
69
70            let null_ordering_policy = &statement.nulls_order_policies[arg_index];
71            if a_value.is_null() {
72                return if null_ordering_policy.eq(&NullsOrderPolicy::NullsFirst) {
73                    Ordering::Less
74                } else {
75                    Ordering::Greater
76                };
77            }
78
79            if b_value.is_null() {
80                return if null_ordering_policy.eq(&NullsOrderPolicy::NullsFirst) {
81                    Ordering::Greater
82                } else {
83                    Ordering::Less
84                };
85            }
86
87            // Calculate the ordering
88            if let Some(order) = a_value.compare(b_value) {
89                if order == Ordering::Equal {
90                    continue;
91                }
92
93                // Reverse the order if DESC order
94                return if statement.sorting_orders[arg_index] == SortingOrder::Descending {
95                    order.reverse()
96                } else {
97                    order
98                };
99            }
100        }
101
102        Ordering::Equal
103    });
104
105    Ok(())
106}