linkrs_query/optimizer/analysis/
reference_count.rs1use std::collections::HashMap;
6
7use crate::planning::plan::core::nodes::base::plan_node_traits::{
8 MultipleInputNode, SingleInputNode,
9};
10use crate::planning::plan::core::nodes::PlanNodeEnum;
11
12use super::fingerprint::FingerprintCalculator;
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
16pub struct SubplanId(pub u64);
17
18impl SubplanId {
19 pub fn new(value: u64) -> Self {
21 Self(value)
22 }
23
24 pub fn value(&self) -> u64 {
26 self.0
27 }
28}
29
30#[derive(Debug, Clone)]
32pub struct SubplanReferenceInfo {
33 pub subplan_id: SubplanId,
35 pub root_node_id: i64,
37 pub reference_count: usize,
39 pub reference_locations: Vec<i64>,
41 pub estimated_output_rows: u64,
43 pub node_count: usize,
45}
46
47impl SubplanReferenceInfo {
48 pub fn new(subplan_id: SubplanId, root_node_id: i64) -> Self {
50 Self {
51 subplan_id,
52 root_node_id,
53 reference_count: 0,
54 reference_locations: Vec::new(),
55 estimated_output_rows: 0,
56 node_count: 0,
57 }
58 }
59
60 pub fn add_reference(&mut self, location: i64) {
62 self.reference_count += 1;
63 if !self.reference_locations.contains(&location) {
64 self.reference_locations.push(location);
65 }
66 }
67}
68
69#[derive(Debug, Clone)]
71pub struct ReferenceCountAnalysis {
72 pub repeated_subplans: Vec<SubplanReferenceInfo>,
74 pub node_reference_map: HashMap<i64, SubplanReferenceInfo>,
76}
77
78impl ReferenceCountAnalysis {
79 pub fn new() -> Self {
81 Self {
82 repeated_subplans: Vec::new(),
83 node_reference_map: HashMap::new(),
84 }
85 }
86
87 pub fn get_node_info(&self, node_id: i64) -> Option<&SubplanReferenceInfo> {
89 self.node_reference_map.get(&node_id)
90 }
91
92 pub fn is_repeated(&self, node_id: i64) -> bool {
94 self.node_reference_map
95 .get(&node_id)
96 .map(|info| info.reference_count >= 2)
97 .unwrap_or(false)
98 }
99
100 pub fn repeated_count(&self) -> usize {
102 self.repeated_subplans.len()
103 }
104}
105
106impl Default for ReferenceCountAnalysis {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112struct AnalysisContext {
114 fingerprint_map: HashMap<u64, SubplanReferenceInfo>,
116 node_fingerprint_map: HashMap<i64, u64>,
118 node_count_map: HashMap<i64, usize>,
120}
121
122impl AnalysisContext {
123 fn new() -> Self {
125 Self {
126 fingerprint_map: HashMap::new(),
127 node_fingerprint_map: HashMap::new(),
128 node_count_map: HashMap::new(),
129 }
130 }
131
132 fn record_reference(&mut self, fingerprint: u64, node_id: i64, parent_id: Option<i64>) {
134 self.node_fingerprint_map.insert(node_id, fingerprint);
136
137 let info = self
139 .fingerprint_map
140 .entry(fingerprint)
141 .or_insert_with(|| SubplanReferenceInfo::new(SubplanId::new(fingerprint), node_id));
142
143 if let Some(parent) = parent_id {
145 info.add_reference(parent);
146 } else {
147 info.reference_count = info.reference_count.max(1);
149 }
150 }
151
152 fn record_node_count(&mut self, node_id: i64, count: usize) {
154 self.node_count_map.insert(node_id, count);
155 }
156
157 fn into_analysis_result(self) -> ReferenceCountAnalysis {
159 let mut repeated_subplans = Vec::new();
160 let mut node_reference_map = HashMap::new();
161
162 for (fingerprint, mut info) in self.fingerprint_map {
163 if info.reference_count >= 2 {
165 if let Some(&count) = self.node_count_map.get(&info.root_node_id) {
167 info.node_count = count;
168 }
169
170 for (node_id, fp) in &self.node_fingerprint_map {
172 if *fp == fingerprint {
173 node_reference_map.insert(*node_id, info.clone());
174 }
175 }
176
177 repeated_subplans.push(info);
178 }
179 }
180
181 ReferenceCountAnalysis {
182 repeated_subplans,
183 node_reference_map,
184 }
185 }
186}
187
188#[derive(Debug, Clone)]
192pub struct ReferenceCountAnalyzer {
193 fingerprint_calculator: FingerprintCalculator,
195}
196
197impl ReferenceCountAnalyzer {
198 pub fn new() -> Self {
200 Self {
201 fingerprint_calculator: FingerprintCalculator::new(),
202 }
203 }
204
205 pub fn analyze(&self, plan: &PlanNodeEnum) -> ReferenceCountAnalysis {
219 let mut context = AnalysisContext::new();
220 self.analyze_recursive(plan, &mut context, None);
221 context.into_analysis_result()
222 }
223
224 fn analyze_recursive(
234 &self,
235 node: &PlanNodeEnum,
236 context: &mut AnalysisContext,
237 parent_id: Option<i64>,
238 ) -> usize {
239 let fingerprint = self.fingerprint_calculator.calculate_fingerprint(node);
241 let node_id = node.id();
242
243 context.record_reference(fingerprint.value(), node_id, parent_id);
245
246 let child_count = match node {
248 PlanNodeEnum::Filter(n) => {
250 let mut total = 1;
251 for dep in n.dependencies() {
252 let count = self.analyze_recursive(dep, context, Some(node_id));
253 total += count;
254 }
255 total
256 }
257 PlanNodeEnum::Project(n) => {
258 let mut total = 1;
259 for dep in n.dependencies() {
260 let count = self.analyze_recursive(dep, context, Some(node_id));
261 total += count;
262 }
263 total
264 }
265 PlanNodeEnum::Sort(n) => {
266 let mut total = 1;
267 for dep in n.dependencies() {
268 let count = self.analyze_recursive(dep, context, Some(node_id));
269 total += count;
270 }
271 total
272 }
273 PlanNodeEnum::Limit(n) => {
274 let mut total = 1;
275 for dep in n.dependencies() {
276 let count = self.analyze_recursive(dep, context, Some(node_id));
277 total += count;
278 }
279 total
280 }
281 PlanNodeEnum::TopN(n) => {
282 let mut total = 1;
283 for dep in n.dependencies() {
284 let count = self.analyze_recursive(dep, context, Some(node_id));
285 total += count;
286 }
287 total
288 }
289 PlanNodeEnum::Sample(n) => {
290 let mut total = 1;
291 for dep in n.dependencies() {
292 let count = self.analyze_recursive(dep, context, Some(node_id));
293 total += count;
294 }
295 total
296 }
297 PlanNodeEnum::Aggregate(n) => {
298 let mut total = 1;
299 for dep in n.dependencies() {
300 let count = self.analyze_recursive(dep, context, Some(node_id));
301 total += count;
302 }
303 total
304 }
305 PlanNodeEnum::Dedup(n) => {
306 let mut total = 1;
307 for dep in n.dependencies() {
308 let count = self.analyze_recursive(dep, context, Some(node_id));
309 total += count;
310 }
311 total
312 }
313 PlanNodeEnum::Unwind(n) => {
314 let mut total = 1;
315 for dep in n.dependencies() {
316 let count = self.analyze_recursive(dep, context, Some(node_id));
317 total += count;
318 }
319 total
320 }
321 PlanNodeEnum::DataCollect(n) => {
322 let mut total = 1;
323 for dep in n.dependencies() {
324 let count = self.analyze_recursive(dep, context, Some(node_id));
325 total += count;
326 }
327 total
328 }
329 PlanNodeEnum::Traverse(n) => {
330 let mut total = 1;
333 for dep in n.dependencies() {
334 let count = self.analyze_recursive(dep, context, Some(node_id));
335 total += count;
336 }
337 total
338 }
339 PlanNodeEnum::Expand(n) => {
340 let mut total = 1;
342 for dep in n.inputs() {
343 let count = self.analyze_recursive(dep, context, Some(node_id));
344 total += count;
345 }
346 total
347 }
348 PlanNodeEnum::ExpandAll(n) => {
349 let mut total = 1;
351 for dep in n.inputs() {
352 let count = self.analyze_recursive(dep, context, Some(node_id));
353 total += count;
354 }
355 total
356 }
357 PlanNodeEnum::AppendVertices(n) => {
358 let mut total = 1;
360 for dep in n.inputs() {
361 let count = self.analyze_recursive(dep, context, Some(node_id));
362 total += count;
363 }
364 total
365 }
366 PlanNodeEnum::Argument(_) => 1,
368 PlanNodeEnum::PassThrough(_) => 1,
369 PlanNodeEnum::PatternApply(n) => {
370 let left_count = self.analyze_recursive(n.left_input(), context, Some(node_id));
371 let right_count = self.analyze_recursive(n.right_input(), context, Some(node_id));
372 1 + left_count + right_count
373 }
374 PlanNodeEnum::CorrelatedApply(n) => {
375 let left_count = self.analyze_recursive(n.left_input(), context, Some(node_id));
376 let right_count = self.analyze_recursive(n.right_input(), context, Some(node_id));
377 1 + left_count + right_count
378 }
379 PlanNodeEnum::RollUpApply(n) => {
380 let left_count = self.analyze_recursive(n.left_input(), context, Some(node_id));
381 let right_count = self.analyze_recursive(n.right_input(), context, Some(node_id));
382 1 + left_count + right_count
383 }
384 PlanNodeEnum::Assign(n) => {
385 let mut total = 1;
386 for dep in n.dependencies() {
387 let count = self.analyze_recursive(dep, context, Some(node_id));
388 total += count;
389 }
390 total
391 }
392 PlanNodeEnum::Minus(n) => {
393 let main_count = self.analyze_recursive(n.input(), context, Some(node_id));
394 let minus_count = self.analyze_recursive(n.minus_input(), context, Some(node_id));
395 1 + main_count + minus_count
396 }
397 PlanNodeEnum::Intersect(n) => {
398 let main_count = self.analyze_recursive(n.input(), context, Some(node_id));
399 let intersect_count =
400 self.analyze_recursive(n.intersect_input(), context, Some(node_id));
401 1 + main_count + intersect_count
402 }
403
404 PlanNodeEnum::InnerJoin(n) => {
406 let left_count = self.analyze_recursive(n.left_input(), context, Some(node_id));
407 let right_count = self.analyze_recursive(n.right_input(), context, Some(node_id));
408 1 + left_count + right_count
409 }
410 PlanNodeEnum::LeftJoin(n) => {
411 let left_count = self.analyze_recursive(n.left_input(), context, Some(node_id));
412 let right_count = self.analyze_recursive(n.right_input(), context, Some(node_id));
413 1 + left_count + right_count
414 }
415 PlanNodeEnum::CrossJoin(n) => {
416 let left_count = self.analyze_recursive(n.left_input(), context, Some(node_id));
417 let right_count = self.analyze_recursive(n.right_input(), context, Some(node_id));
418 1 + left_count + right_count
419 }
420 PlanNodeEnum::FullOuterJoin(n) => {
421 let left_count = self.analyze_recursive(n.left_input(), context, Some(node_id));
422 let right_count = self.analyze_recursive(n.right_input(), context, Some(node_id));
423 1 + left_count + right_count
424 }
425 PlanNodeEnum::Union(n) => {
426 let mut total = 1;
427 for dep in n.dependencies() {
428 let count = self.analyze_recursive(dep, context, Some(node_id));
429 total += count;
430 }
431 total
432 }
433
434 PlanNodeEnum::Select(n) => {
436 let mut total = 1; if let Some(ref branch) = n.if_branch() {
438 let count = self.analyze_recursive(branch, context, Some(node_id));
439 total += count;
440 }
441 if let Some(ref branch) = n.else_branch() {
442 let count = self.analyze_recursive(branch, context, Some(node_id));
443 total += count;
444 }
445 total
446 }
447 PlanNodeEnum::Loop(n) => {
448 let mut total = 1; if let Some(ref body) = n.body() {
450 let count = self.analyze_recursive(body, context, Some(node_id));
451 total += count;
452 }
453 total
454 }
455 PlanNodeEnum::RecursiveCte(n) => {
456 let mut total = 1; total += self.analyze_recursive(n.anchor(), context, Some(node_id));
458 if let Some(step) = n.step() {
459 total += self.analyze_recursive(step, context, Some(node_id));
460 }
461 total
462 }
463
464 _ => 1, };
467
468 context.record_node_count(node_id, child_count);
470
471 child_count
472 }
473}
474
475impl Default for ReferenceCountAnalyzer {
476 fn default() -> Self {
477 Self::new()
478 }
479}
480
481#[cfg(test)]
482mod tests {
483 use super::*;
484
485 #[test]
486 fn test_reference_count_analyzer_new() {
487 let _analyzer = ReferenceCountAnalyzer::new();
488 }
490
491 #[test]
492 fn test_subplan_id() {
493 let id = SubplanId::new(12345);
494 assert_eq!(id.value(), 12345);
495 }
496
497 #[test]
498 fn test_subplan_reference_info() {
499 let mut info = SubplanReferenceInfo::new(SubplanId::new(1), 100);
500 assert_eq!(info.reference_count, 0);
501
502 info.add_reference(200);
503 assert_eq!(info.reference_count, 1);
504 assert!(info.reference_locations.contains(&200));
505
506 info.add_reference(200); assert_eq!(info.reference_count, 2);
508 assert_eq!(info.reference_locations.len(), 1); }
510
511 #[test]
512 fn test_reference_count_analysis() {
513 let analysis = ReferenceCountAnalysis::new();
514 assert_eq!(analysis.repeated_count(), 0);
515 assert!(!analysis.is_repeated(1));
516 }
517}