Skip to main content

gitql_engine/
engine.rs

1use std::collections::HashMap;
2use std::vec;
3
4use gitql_ast::query::DescribeQuery;
5use gitql_ast::query::DoQuery;
6use gitql_ast::query::GlobalVariableDeclQuery;
7use gitql_ast::query::Query;
8use gitql_ast::query::SelectQuery;
9use gitql_ast::statement::Distinct;
10use gitql_ast::statement::Statement;
11use gitql_core::environment::Environment;
12use gitql_core::object::GitQLObject;
13use gitql_core::object::Group;
14use gitql_core::object::Row;
15use gitql_core::values::text::TextValue;
16use gitql_core::values::Value;
17
18use crate::data_provider::DataProvider;
19use crate::engine_distinct::apply_distinct_operator;
20use crate::engine_evaluator::evaluate_expression;
21use crate::engine_executor::execute_statement;
22
23/// Static Logical Plan, later must be replaced by a Plan from the Logical query Planner
24const FIXED_LOGICAL_PLAN_LEN: usize = 10;
25const FIXED_LOGICAL_PLAN: [&str; FIXED_LOGICAL_PLAN_LEN] = [
26    "select",
27    "where",
28    "group",
29    "aggregation",
30    "having",
31    "window_functions",
32    "qualify",
33    "order",
34    "offset",
35    "limit",
36];
37
38pub enum EvaluationResult {
39    Do,
40    SelectedGroups(GitQLObject),
41    SelectedInfo,
42    SetGlobalVariable,
43}
44
45#[allow(clippy::borrowed_box)]
46pub fn evaluate(
47    env: &mut Environment,
48    data_provider: &Box<dyn DataProvider>,
49    queries: Vec<Query>,
50) -> Result<Vec<EvaluationResult>, String> {
51    let mut evaluations_results: Vec<EvaluationResult> = Vec::with_capacity(queries.len());
52    for query in queries {
53        let evaluation_result = match query {
54            Query::Do(do_query) => evaluate_do_query(env, &do_query),
55            Query::Select(select_query) => evaluate_select_query(env, data_provider, select_query),
56            Query::GlobalVariableDecl(global) => evaluate_global_declaration_query(env, &global),
57            Query::DescribeTable(describe_query) => evaluate_describe_query(env, describe_query),
58            Query::ShowTables => evaluate_show_tables_query(env),
59        }?;
60        evaluations_results.push(evaluation_result);
61    }
62    Ok(evaluations_results)
63}
64
65fn evaluate_do_query(
66    env: &mut Environment,
67    do_query: &DoQuery,
68) -> Result<EvaluationResult, String> {
69    for expr in do_query.exprs.iter() {
70        evaluate_expression(env, expr, &[], &vec![])?;
71    }
72    Ok(EvaluationResult::Do)
73}
74
75#[allow(clippy::borrowed_box)]
76fn evaluate_select_query(
77    env: &mut Environment,
78    data_provider: &Box<dyn DataProvider>,
79    select_query: SelectQuery,
80) -> Result<EvaluationResult, String> {
81    let mut gitql_object = GitQLObject::default();
82    let mut alias_table: HashMap<String, String> = select_query.alias_table;
83
84    let hidden_selections_map = select_query.hidden_selections;
85    let hidden_selections: Vec<String> =
86        hidden_selections_map.values().flatten().cloned().collect();
87    let mut statements_map = select_query.statements;
88    let has_group_by_statement = statements_map.contains_key("group");
89
90    let mut distinct: Option<Distinct> = None;
91    for logical_node_name in FIXED_LOGICAL_PLAN {
92        if let Some(statement) = statements_map.get_mut(logical_node_name) {
93            execute_statement(
94                env,
95                statement,
96                data_provider,
97                &mut gitql_object,
98                &mut alias_table,
99                &hidden_selections_map,
100                has_group_by_statement,
101            )?;
102
103            if let Statement::Select(select_statement) = statement {
104                // If the main group is empty, no need to perform other statements
105                if gitql_object.is_empty() || gitql_object.groups[0].is_empty() {
106                    return Ok(EvaluationResult::SelectedGroups(gitql_object));
107                }
108
109                distinct = Some(select_statement.distinct.to_owned());
110            }
111        }
112    }
113
114    // Apply the distinct operation after executing statements
115    if let Some(distinct) = distinct {
116        apply_distinct_operator(&distinct, &mut gitql_object, &hidden_selections);
117    }
118
119    // Remove Hidden Selection from the rows after executing the query plan
120    remove_hidden_selected_from_groups(
121        &mut gitql_object.titles,
122        &mut gitql_object.groups,
123        &hidden_selections,
124    );
125
126    let number_of_groups = gitql_object.groups.len();
127    if number_of_groups > 0 {
128        let main_group: &mut Group = &mut gitql_object.groups[0];
129
130        // If there are many groups that mean group by is executed before.
131        // must merge each group into only one element
132        if number_of_groups > 1 {
133            for group in gitql_object.groups.iter_mut() {
134                if group.len() > 1 {
135                    group.rows.drain(1..);
136                }
137            }
138            gitql_object.flat();
139        }
140        // If it a single group but it select only aggregations function,
141        // should return only first element in the group
142        else if number_of_groups == 1
143            && !select_query.has_group_by_statement
144            && select_query.has_aggregation_function
145            && main_group.len() > 1
146        {
147            main_group.rows.drain(1..);
148        }
149    }
150
151    // Into statement must be executed last after flatted and remove hidden selections
152    if let Some(into_statement) = statements_map.get_mut("into") {
153        execute_statement(
154            env,
155            into_statement,
156            data_provider,
157            &mut gitql_object,
158            &mut alias_table,
159            &hidden_selections_map,
160            has_group_by_statement,
161        )?;
162
163        return Ok(EvaluationResult::SelectedInfo);
164    }
165
166    Ok(EvaluationResult::SelectedGroups(gitql_object))
167}
168
169fn evaluate_global_declaration_query(
170    env: &mut Environment,
171    global_decl_query: &GlobalVariableDeclQuery,
172) -> Result<EvaluationResult, String> {
173    let value = evaluate_expression(env, &global_decl_query.value, &[], &vec![])?;
174    env.globals
175        .insert(global_decl_query.name.to_string(), value);
176    Ok(EvaluationResult::SetGlobalVariable)
177}
178
179fn evaluate_describe_query(
180    env: &mut Environment,
181    describe_query: DescribeQuery,
182) -> Result<EvaluationResult, String> {
183    let table_fields = env
184        .schema
185        .tables_fields_names
186        .get(&describe_query.table_name.as_str())
187        .unwrap();
188
189    let mut gitql_object = GitQLObject::default();
190    gitql_object.titles.push("field".to_owned());
191    gitql_object.titles.push("type".to_owned());
192
193    let mut rows: Vec<Row> = Vec::with_capacity(table_fields.len());
194    for field in table_fields {
195        let value = env.schema.tables_fields_types.get(field).unwrap();
196        rows.push(Row {
197            values: vec![
198                Box::new(TextValue::new(field.to_owned().to_owned())),
199                Box::new(TextValue::new(value.literal())),
200            ],
201        })
202    }
203
204    gitql_object.groups.push(Group { rows });
205    Ok(EvaluationResult::SelectedGroups(gitql_object))
206}
207
208fn evaluate_show_tables_query(env: &mut Environment) -> Result<EvaluationResult, String> {
209    let tables = env.schema.tables_fields_names.keys();
210
211    let mut rows: Vec<Row> = Vec::with_capacity(tables.len());
212    for table in env.schema.tables_fields_names.keys() {
213        let values: Vec<Box<dyn Value>> =
214            vec![Box::new(TextValue::new(table.to_owned().to_owned()))];
215        rows.push(Row { values });
216    }
217
218    let mut gitql_object = GitQLObject::default();
219    gitql_object.titles.push("Tables".to_owned());
220    gitql_object.groups.push(Group { rows });
221
222    Ok(EvaluationResult::SelectedGroups(gitql_object))
223}
224
225fn remove_hidden_selected_from_groups(
226    titles: &mut Vec<String>,
227    groups: &mut [Group],
228    hidden_selections: &[String],
229) {
230    let titles_count = titles.len();
231    let mut index_list: Vec<usize> = vec![];
232    for i in (0..titles_count).rev() {
233        if hidden_selections.contains(&titles[i]) {
234            titles.remove(i);
235            index_list.push(i);
236        }
237    }
238
239    for group in groups.iter_mut() {
240        for index_to_delete in index_list.iter() {
241            for row in group.rows.iter_mut() {
242                row.values.remove(*index_to_delete);
243            }
244        }
245    }
246}