1use std::collections::HashMap;
2use std::vec;
3
4use gitql_ast::statement::DescribeStatement;
5use gitql_ast::statement::Distinct;
6use gitql_ast::statement::DoStatement;
7use gitql_ast::statement::GQLQuery;
8use gitql_ast::statement::GlobalVariableStatement;
9use gitql_ast::statement::Query;
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_global_variable_statement;
22use crate::engine_executor::execute_statement;
23
24const FIXED_LOGICAL_PLAN_LEN: usize = 9;
26const FIXED_LOGICAL_PLAN: [&str; FIXED_LOGICAL_PLAN_LEN] = [
27 "select",
28 "where",
29 "group",
30 "aggregation",
31 "having",
32 "window_functions",
33 "order",
34 "offset",
35 "limit",
36];
37
38pub enum EvaluationResult {
39 Do(Box<dyn Value>),
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![];
52 for query in queries {
53 let evaluation_result = match query {
54 Query::Do(do_statement) => evaluate_do_query(env, &do_statement),
55 Query::Select(gql_query) => evaluate_select_query(env, data_provider, gql_query),
56 Query::GlobalVariableDeclaration(global) => {
57 evaluate_global_declaration_query(env, &global)
58 }
59 Query::Describe(describe_statement) => evaluate_describe_query(env, describe_statement),
60 Query::ShowTables => evaluate_show_tables_query(env),
61 }?;
62 evaluations_results.push(evaluation_result);
63 }
64 Ok(evaluations_results)
65}
66
67fn evaluate_do_query(
68 env: &mut Environment,
69 do_statement: &DoStatement,
70) -> Result<EvaluationResult, String> {
71 Ok(EvaluationResult::Do(evaluate_expression(
72 env,
73 &do_statement.expression,
74 &[],
75 &vec![],
76 )?))
77}
78
79#[allow(clippy::borrowed_box)]
80fn evaluate_select_query(
81 env: &mut Environment,
82 data_provider: &Box<dyn DataProvider>,
83 query: GQLQuery,
84) -> Result<EvaluationResult, String> {
85 let mut gitql_object = GitQLObject::default();
86 let mut alias_table: HashMap<String, String> = query.alias_table;
87
88 let hidden_selections_map = query.hidden_selections;
89 let hidden_selections: Vec<String> =
90 hidden_selections_map.values().flatten().cloned().collect();
91 let mut statements_map = query.statements;
92 let has_group_by_statement = statements_map.contains_key("group");
93
94 let mut distinct: Option<Distinct> = None;
95 for logical_node_name in FIXED_LOGICAL_PLAN {
96 if let Some(statement) = statements_map.get_mut(logical_node_name) {
97 match logical_node_name {
98 "select" => {
99 let select_statement = statement
101 .as_any()
102 .downcast_ref::<SelectStatement>()
103 .unwrap();
104
105 execute_statement(
106 env,
107 statement,
108 data_provider,
109 &mut gitql_object,
110 &mut alias_table,
111 &hidden_selections_map,
112 has_group_by_statement,
113 )?;
114
115 if gitql_object.is_empty() || gitql_object.groups[0].is_empty() {
117 return Ok(EvaluationResult::SelectedGroups(gitql_object));
118 }
119
120 distinct = Some(select_statement.distinct.to_owned());
121 }
122 _ => {
123 execute_statement(
124 env,
125 statement,
126 data_provider,
127 &mut gitql_object,
128 &mut alias_table,
129 &hidden_selections_map,
130 has_group_by_statement,
131 )?;
132 }
133 }
134 }
135 }
136
137 if let Some(distinct) = distinct {
139 apply_distinct_operator(&distinct, &mut gitql_object, &hidden_selections);
140 }
141
142 remove_hidden_selected_from_groups(
144 &mut gitql_object.titles,
145 &mut gitql_object.groups,
146 &hidden_selections,
147 );
148
149 let number_of_groups = gitql_object.groups.len();
150 let main_group: &mut Group = &mut gitql_object.groups[0];
151
152 if number_of_groups > 1 {
155 for group in gitql_object.groups.iter_mut() {
156 if group.len() > 1 {
157 group.rows.drain(1..);
158 }
159 }
160 gitql_object.flat();
161 }
162 else if number_of_groups == 1
165 && !query.has_group_by_statement
166 && query.has_aggregation_function
167 && main_group.len() > 1
168 {
169 main_group.rows.drain(1..);
170 }
171
172 if let Some(into_statement) = statements_map.get_mut("into") {
174 execute_statement(
175 env,
176 into_statement,
177 data_provider,
178 &mut gitql_object,
179 &mut alias_table,
180 &hidden_selections_map,
181 has_group_by_statement,
182 )?;
183
184 return Ok(EvaluationResult::SelectedInfo);
185 }
186
187 Ok(EvaluationResult::SelectedGroups(gitql_object))
188}
189
190fn evaluate_global_declaration_query(
191 env: &mut Environment,
192 statement: &GlobalVariableStatement,
193) -> Result<EvaluationResult, String> {
194 execute_global_variable_statement(env, statement)?;
195 Ok(EvaluationResult::SetGlobalVariable)
196}
197
198fn evaluate_describe_query(
199 env: &mut Environment,
200 stmt: DescribeStatement,
201) -> Result<EvaluationResult, String> {
202 let table_fields = env
203 .schema
204 .tables_fields_names
205 .get(&stmt.table_name.as_str())
206 .unwrap();
207
208 let mut gitql_object = GitQLObject::default();
209 gitql_object.titles.push("Field".to_owned());
210 gitql_object.titles.push("Type".to_owned());
211
212 let mut rows: Vec<Row> = Vec::with_capacity(table_fields.len());
213 for field in table_fields {
214 let value = env.schema.tables_fields_types.get(field).unwrap();
215 rows.push(Row {
216 values: vec![
217 Box::new(TextValue {
218 value: field.to_owned().to_owned(),
219 }),
220 Box::new(TextValue {
221 value: value.literal(),
222 }),
223 ],
224 })
225 }
226
227 gitql_object.groups.push(Group { rows });
228 Ok(EvaluationResult::SelectedGroups(gitql_object))
229}
230
231fn evaluate_show_tables_query(env: &mut Environment) -> Result<EvaluationResult, String> {
232 let tables = env.schema.tables_fields_names.keys();
233
234 let mut rows: Vec<Row> = Vec::with_capacity(tables.len());
235 for table in env.schema.tables_fields_names.keys() {
236 let values: Vec<Box<dyn Value>> = vec![Box::new(TextValue {
237 value: table.to_owned().to_owned(),
238 })];
239
240 rows.push(Row { values });
241 }
242
243 let mut gitql_object = GitQLObject::default();
244 gitql_object.titles.push("Tables".to_owned());
245 gitql_object.groups.push(Group { rows });
246
247 Ok(EvaluationResult::SelectedGroups(gitql_object))
248}
249
250fn remove_hidden_selected_from_groups(
251 titles: &mut Vec<String>,
252 groups: &mut [Group],
253 hidden_selections: &[String],
254) {
255 let titles_count = titles.len();
256 let mut index_list: Vec<usize> = vec![];
257 for i in (0..titles_count).rev() {
258 if hidden_selections.contains(&titles[i]) {
259 titles.remove(i);
260 index_list.push(i);
261 }
262 }
263
264 for group in groups.iter_mut() {
265 for index_to_delete in index_list.iter() {
266 for row in group.rows.iter_mut() {
267 row.values.remove(*index_to_delete);
268 }
269 }
270 }
271}