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::SelectStatement;
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
23const 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 match logical_node_name {
94 "select" => {
95 let select_statement = statement
97 .as_any()
98 .downcast_ref::<SelectStatement>()
99 .unwrap();
100
101 execute_statement(
102 env,
103 statement,
104 data_provider,
105 &mut gitql_object,
106 &mut alias_table,
107 &hidden_selections_map,
108 has_group_by_statement,
109 )?;
110
111 if gitql_object.is_empty() || gitql_object.groups[0].is_empty() {
113 return Ok(EvaluationResult::SelectedGroups(gitql_object));
114 }
115
116 distinct = Some(select_statement.distinct.to_owned());
117 }
118 _ => {
119 execute_statement(
120 env,
121 statement,
122 data_provider,
123 &mut gitql_object,
124 &mut alias_table,
125 &hidden_selections_map,
126 has_group_by_statement,
127 )?;
128 }
129 }
130 }
131 }
132
133 if let Some(distinct) = distinct {
135 apply_distinct_operator(&distinct, &mut gitql_object, &hidden_selections);
136 }
137
138 remove_hidden_selected_from_groups(
140 &mut gitql_object.titles,
141 &mut gitql_object.groups,
142 &hidden_selections,
143 );
144
145 let number_of_groups = gitql_object.groups.len();
146 let main_group: &mut Group = &mut gitql_object.groups[0];
147
148 if number_of_groups > 1 {
151 for group in gitql_object.groups.iter_mut() {
152 if group.len() > 1 {
153 group.rows.drain(1..);
154 }
155 }
156 gitql_object.flat();
157 }
158 else if number_of_groups == 1
161 && !select_query.has_group_by_statement
162 && select_query.has_aggregation_function
163 && main_group.len() > 1
164 {
165 main_group.rows.drain(1..);
166 }
167
168 if let Some(into_statement) = statements_map.get_mut("into") {
170 execute_statement(
171 env,
172 into_statement,
173 data_provider,
174 &mut gitql_object,
175 &mut alias_table,
176 &hidden_selections_map,
177 has_group_by_statement,
178 )?;
179
180 return Ok(EvaluationResult::SelectedInfo);
181 }
182
183 Ok(EvaluationResult::SelectedGroups(gitql_object))
184}
185
186fn evaluate_global_declaration_query(
187 env: &mut Environment,
188 global_decl_query: &GlobalVariableDeclQuery,
189) -> Result<EvaluationResult, String> {
190 let value = evaluate_expression(env, &global_decl_query.value, &[], &vec![])?;
191 env.globals
192 .insert(global_decl_query.name.to_string(), value);
193 Ok(EvaluationResult::SetGlobalVariable)
194}
195
196fn evaluate_describe_query(
197 env: &mut Environment,
198 describe_query: DescribeQuery,
199) -> Result<EvaluationResult, String> {
200 let table_fields = env
201 .schema
202 .tables_fields_names
203 .get(&describe_query.table_name.as_str())
204 .unwrap();
205
206 let mut gitql_object = GitQLObject::default();
207 gitql_object.titles.push("field".to_owned());
208 gitql_object.titles.push("type".to_owned());
209
210 let mut rows: Vec<Row> = Vec::with_capacity(table_fields.len());
211 for field in table_fields {
212 let value = env.schema.tables_fields_types.get(field).unwrap();
213 rows.push(Row {
214 values: vec![
215 Box::new(TextValue::new(field.to_owned().to_owned())),
216 Box::new(TextValue::new(value.literal())),
217 ],
218 })
219 }
220
221 gitql_object.groups.push(Group { rows });
222 Ok(EvaluationResult::SelectedGroups(gitql_object))
223}
224
225fn evaluate_show_tables_query(env: &mut Environment) -> Result<EvaluationResult, String> {
226 let tables = env.schema.tables_fields_names.keys();
227
228 let mut rows: Vec<Row> = Vec::with_capacity(tables.len());
229 for table in env.schema.tables_fields_names.keys() {
230 let values: Vec<Box<dyn Value>> =
231 vec![Box::new(TextValue::new(table.to_owned().to_owned()))];
232 rows.push(Row { values });
233 }
234
235 let mut gitql_object = GitQLObject::default();
236 gitql_object.titles.push("Tables".to_owned());
237 gitql_object.groups.push(Group { rows });
238
239 Ok(EvaluationResult::SelectedGroups(gitql_object))
240}
241
242fn remove_hidden_selected_from_groups(
243 titles: &mut Vec<String>,
244 groups: &mut [Group],
245 hidden_selections: &[String],
246) {
247 let titles_count = titles.len();
248 let mut index_list: Vec<usize> = vec![];
249 for i in (0..titles_count).rev() {
250 if hidden_selections.contains(&titles[i]) {
251 titles.remove(i);
252 index_list.push(i);
253 }
254 }
255
256 for group in groups.iter_mut() {
257 for index_to_delete in index_list.iter() {
258 for row in group.rows.iter_mut() {
259 row.values.remove(*index_to_delete);
260 }
261 }
262 }
263}