elif_orm/loading/optimizer/
plan.rs1use crate::{
2 error::{OrmError, OrmResult},
3 relationships::{RelationshipType, metadata::RelationshipMetadata},
4};
5use std::collections::{HashMap, HashSet};
6
7#[derive(Debug, Clone)]
9pub struct QueryNode {
10 pub id: String,
12 pub table: String,
14 pub relationship_type: Option<RelationshipType>,
16 pub relationship_metadata: Option<RelationshipMetadata>,
18 pub parent_id: Option<String>,
20 pub children: Vec<String>,
22 pub depth: usize,
24 pub estimated_rows: usize,
26 pub parallel_safe: bool,
28 pub foreign_key: Option<String>,
30 pub constraints: Vec<String>,
32 pub available_columns: Vec<String>,
34 pub index_hints: Vec<String>,
36}
37
38impl QueryNode {
39 pub fn new(id: String, table: String) -> Self {
41 Self {
42 id,
43 table,
44 relationship_type: None,
45 relationship_metadata: None,
46 parent_id: None,
47 children: Vec::new(),
48 depth: 0,
49 estimated_rows: 1000, parallel_safe: true,
51 foreign_key: None,
52 constraints: Vec::new(),
53 available_columns: Vec::new(),
54 index_hints: Vec::new(),
55 }
56 }
57
58 pub fn root(id: String, table: String) -> Self {
60 let mut node = Self::new(id, table);
61 node.depth = 0;
62 node
63 }
64
65 pub fn child(
67 id: String,
68 table: String,
69 parent_id: String,
70 relationship_type: RelationshipType,
71 foreign_key: String
72 ) -> Self {
73 let mut node = Self::new(id, table);
74 node.parent_id = Some(parent_id);
75 node.relationship_type = Some(relationship_type);
76 node.foreign_key = Some(foreign_key);
77 node
78 }
79
80 pub fn child_with_metadata(
82 id: String,
83 table: String,
84 parent_id: String,
85 metadata: RelationshipMetadata
86 ) -> Self {
87 let mut node = Self::new(id, table);
88 node.parent_id = Some(parent_id);
89 node.relationship_type = Some(metadata.relationship_type);
90 node.relationship_metadata = Some(metadata.clone());
91 node.foreign_key = Some(metadata.foreign_key.primary_column().to_string());
92
93 node.estimated_rows = match metadata.relationship_type {
95 RelationshipType::HasMany | RelationshipType::ManyToMany | RelationshipType::MorphMany => 10000,
96 _ => 1, };
98
99 node.parallel_safe = !metadata.relationship_type.requires_pivot();
101
102 node
103 }
104
105 pub fn add_child(&mut self, child_id: String) {
107 if !self.children.contains(&child_id) {
108 self.children.push(child_id);
109 }
110 }
111
112 pub fn set_depth(&mut self, depth: usize) {
114 self.depth = depth;
115 }
116
117 pub fn set_estimated_rows(&mut self, rows: usize) {
119 self.estimated_rows = rows;
120 }
121
122 pub fn set_parallel_safe(&mut self, safe: bool) {
124 self.parallel_safe = safe;
125 }
126
127 pub fn add_constraint(&mut self, constraint: String) {
129 self.constraints.push(constraint);
130 }
131
132 pub fn is_root(&self) -> bool {
134 self.parent_id.is_none()
135 }
136
137 pub fn is_leaf(&self) -> bool {
139 self.children.is_empty()
140 }
141
142 pub fn set_metadata(&mut self, metadata: RelationshipMetadata) {
144 self.relationship_type = Some(metadata.relationship_type);
145 self.relationship_metadata = Some(metadata.clone());
146 self.foreign_key = Some(metadata.foreign_key.primary_column().to_string());
147
148 self.estimated_rows = match metadata.relationship_type {
150 RelationshipType::HasMany | RelationshipType::ManyToMany | RelationshipType::MorphMany => 10000,
151 _ => 1,
152 };
153
154 self.parallel_safe = !metadata.relationship_type.requires_pivot();
155 }
156
157 pub fn set_columns(&mut self, columns: Vec<String>) {
159 self.available_columns = columns;
160 }
161
162 pub fn add_index_hint(&mut self, index: String) {
164 if !self.index_hints.contains(&index) {
165 self.index_hints.push(index);
166 }
167 }
168
169 pub fn primary_key(&self) -> &str {
171 if let Some(metadata) = &self.relationship_metadata {
172 &metadata.local_key
173 } else {
174 "id"
175 }
176 }
177
178 pub fn get_foreign_key(&self) -> Option<&str> {
180 self.foreign_key.as_deref()
181 }
182
183 pub fn is_collection(&self) -> bool {
185 self.relationship_type
186 .map(|rt| rt.is_collection())
187 .unwrap_or(false)
188 }
189
190 pub fn requires_pivot(&self) -> bool {
192 self.relationship_type
193 .map(|rt| rt.requires_pivot())
194 .unwrap_or(false)
195 }
196}
197
198#[derive(Debug)]
200pub struct QueryPlan {
201 pub nodes: HashMap<String, QueryNode>,
203 pub roots: Vec<String>,
205 pub execution_phases: Vec<Vec<String>>,
207 pub max_depth: usize,
209 pub total_estimated_rows: usize,
211 pub metadata: HashMap<String, serde_json::Value>,
213}
214
215impl QueryPlan {
216 pub fn new() -> Self {
218 Self {
219 nodes: HashMap::new(),
220 roots: Vec::new(),
221 execution_phases: Vec::new(),
222 max_depth: 0,
223 total_estimated_rows: 0,
224 metadata: HashMap::new(),
225 }
226 }
227
228 pub fn add_node(&mut self, node: QueryNode) {
230 self.max_depth = self.max_depth.max(node.depth);
231 self.total_estimated_rows += node.estimated_rows;
232
233 if node.parent_id.is_none() {
234 self.roots.push(node.id.clone());
235 }
236
237 if let Some(parent_id) = &node.parent_id {
239 if let Some(parent) = self.nodes.get_mut(parent_id) {
240 parent.add_child(node.id.clone());
241 }
242 }
243
244 self.nodes.insert(node.id.clone(), node);
245 }
246
247 pub fn build_execution_phases(&mut self) -> OrmResult<()> {
249 let mut phases = Vec::new();
250 let mut visited = HashSet::new();
251 let mut current_depth = 0;
252
253 self.validate()?;
255
256 while visited.len() < self.nodes.len() && current_depth <= self.max_depth {
257 let mut phase_nodes = Vec::new();
258
259 for (id, node) in &self.nodes {
261 if !visited.contains(id) && node.depth == current_depth {
262 let ready = if let Some(parent_id) = &node.parent_id {
264 visited.contains(parent_id)
265 } else {
266 true };
268
269 if ready && node.parallel_safe {
270 phase_nodes.push(id.clone());
271 }
272 }
273 }
274
275 if phase_nodes.is_empty() {
277 for (id, node) in &self.nodes {
278 if !visited.contains(id) && node.depth == current_depth {
279 let ready = if let Some(parent_id) = &node.parent_id {
280 visited.contains(parent_id)
281 } else {
282 true
283 };
284
285 if ready {
286 phase_nodes.push(id.clone());
287 break; }
289 }
290 }
291 }
292
293 if phase_nodes.is_empty() {
294 current_depth += 1;
295 continue;
296 }
297
298 for id in &phase_nodes {
300 visited.insert(id.clone());
301 }
302
303 if !phase_nodes.is_empty() {
304 phases.push(phase_nodes);
305 }
306
307 current_depth += 1;
308 }
309
310 self.execution_phases = phases;
311 Ok(())
312 }
313
314 pub fn validate(&self) -> OrmResult<()> {
316 for root_id in &self.roots {
318 let mut visited = HashSet::new();
319 let mut path = Vec::new();
320
321 if self.has_cycle_from(root_id, &mut visited, &mut path) {
322 return Err(OrmError::Query("Circular dependency detected in query plan".into()));
323 }
324 }
325
326 for (id, node) in &self.nodes {
328 if let Some(parent_id) = &node.parent_id {
329 if !self.nodes.contains_key(parent_id) {
330 return Err(OrmError::Query(format!("Parent node '{}' not found for node '{}'", parent_id, id)));
331 }
332 }
333
334 for child_id in &node.children {
335 if !self.nodes.contains_key(child_id) {
336 return Err(OrmError::Query(format!("Child node '{}' not found for node '{}'", child_id, id)));
337 }
338 }
339 }
340
341 Ok(())
342 }
343
344 fn has_cycle_from(
346 &self,
347 node_id: &str,
348 visited: &mut HashSet<String>,
349 path: &mut Vec<String>,
350 ) -> bool {
351 if path.contains(&node_id.to_string()) {
352 return true; }
354
355 if visited.contains(node_id) {
356 return false; }
358
359 path.push(node_id.to_string());
360 visited.insert(node_id.to_string());
361
362 if let Some(node) = self.nodes.get(node_id) {
363 for child_id in &node.children {
364 if self.has_cycle_from(child_id, visited, path) {
365 return true;
366 }
367 }
368 }
369
370 path.pop();
371 false
372 }
373
374 pub fn nodes_at_depth(&self, depth: usize) -> Vec<&QueryNode> {
376 self.nodes
377 .values()
378 .filter(|node| node.depth == depth)
379 .collect()
380 }
381
382 pub fn leaf_nodes(&self) -> Vec<&QueryNode> {
384 self.nodes
385 .values()
386 .filter(|node| node.is_leaf())
387 .collect()
388 }
389
390 pub fn complexity_score(&self) -> f64 {
392 let depth_penalty = self.max_depth as f64 * 1.5;
393 let node_penalty = self.nodes.len() as f64 * 0.5;
394 let row_penalty = (self.total_estimated_rows as f64).log10() * 2.0;
395
396 depth_penalty + node_penalty + row_penalty
397 }
398
399 pub fn add_metadata(&mut self, key: String, value: serde_json::Value) {
401 self.metadata.insert(key, value);
402 }
403
404 pub fn statistics(&self) -> PlanStatistics {
406 PlanStatistics {
407 total_nodes: self.nodes.len(),
408 root_nodes: self.roots.len(),
409 leaf_nodes: self.leaf_nodes().len(),
410 max_depth: self.max_depth,
411 total_estimated_rows: self.total_estimated_rows,
412 execution_phases: self.execution_phases.len(),
413 complexity_score: self.complexity_score(),
414 parallel_nodes: self.nodes.values().filter(|n| n.parallel_safe).count(),
415 }
416 }
417}
418
419impl Default for QueryPlan {
420 fn default() -> Self {
421 Self::new()
422 }
423}
424
425#[derive(Debug, Clone)]
427pub struct PlanStatistics {
428 pub total_nodes: usize,
429 pub root_nodes: usize,
430 pub leaf_nodes: usize,
431 pub max_depth: usize,
432 pub total_estimated_rows: usize,
433 pub execution_phases: usize,
434 pub complexity_score: f64,
435 pub parallel_nodes: usize,
436}