risingwave_frontend/optimizer/plan_node/
merge_eq_nodes.rs1use std::collections::HashMap;
16use std::hash::Hash;
17
18use super::{
19 EndoPlan, LogicalPlanRef as PlanRef, LogicalShare, PlanTreeNodeUnary, ShareNode, VisitPlan,
20};
21use crate::optimizer::{ShareId, plan_visitor};
22use crate::utils::{Endo, Visit};
23
24pub trait Semantics<V: Hash + Eq> {
25 fn semantics(&self) -> V;
26}
27
28impl Semantics<PlanRef> for PlanRef {
29 fn semantics(&self) -> PlanRef {
30 self.clone()
31 }
32}
33
34impl PlanRef {
35 pub fn common_subplan_sharing<V: Hash + Eq>(self) -> PlanRef
36 where
37 PlanRef: Semantics<V>,
38 {
39 Merger::default().apply(self)
40 }
41}
42
43struct Merger<V: Hash + Eq> {
44 cache: HashMap<V, LogicalShare>,
45}
46
47impl<V: Hash + Eq> Default for Merger<V> {
48 fn default() -> Self {
49 Merger {
50 cache: Default::default(),
51 }
52 }
53}
54
55impl<V: Hash + Eq> Endo<PlanRef> for Merger<V>
56where
57 PlanRef: Semantics<V>,
58{
59 fn apply(&mut self, t: PlanRef) -> PlanRef {
60 let semantics = t.semantics();
61 let share = self.cache.get(&semantics).cloned().unwrap_or_else(|| {
62 let share = LogicalShare::new(self.tree_apply(t));
63 self.cache.entry(semantics).or_insert(share).clone()
64 });
65 share.into()
66 }
67}
68
69impl PlanRef {
70 pub fn prune_share(&self) -> PlanRef {
71 let mut counter = Counter::default();
72 counter.visit(self);
73 counter.to_pruner().apply(self.clone())
74 }
75}
76
77#[derive(Default)]
78struct Counter {
79 counts: HashMap<ShareId, u64>,
80}
81
82impl Counter {
83 fn to_pruner(&self) -> Pruner<'_> {
84 Pruner {
85 counts: &self.counts,
86 cache: HashMap::new(),
87 }
88 }
89}
90
91impl VisitPlan for Counter {
92 fn visited<F>(&mut self, plan: &PlanRef, mut f: F)
93 where
94 F: FnMut(&mut Self),
95 {
96 let share_id = plan
97 .as_logical_share()
98 .expect("dag cache is only used for shares")
99 .share_id();
100 if self.counts.get(&share_id).is_none_or(|c| *c <= 1) {
101 f(self);
102 }
103 }
104}
105
106impl Visit<PlanRef> for Counter {
107 fn visit(&mut self, t: &PlanRef) {
108 if let Some(s) = t.as_logical_share() {
109 self.counts
110 .entry(s.share_id())
111 .and_modify(|c| *c += 1)
112 .or_insert(1);
113 }
114 self.dag_visit(t);
115 }
116}
117
118struct Pruner<'a> {
119 counts: &'a HashMap<ShareId, u64>,
120 cache: HashMap<ShareId, PlanRef>,
121}
122
123impl EndoPlan for Pruner<'_> {
124 fn cached<F>(&mut self, plan: PlanRef, mut f: F) -> PlanRef
125 where
126 F: FnMut(&mut Self) -> PlanRef,
127 {
128 let share_id = plan
129 .as_logical_share()
130 .expect("dag cache is only used for shares")
131 .share_id();
132 self.cache.get(&share_id).cloned().unwrap_or_else(|| {
133 let res = f(self);
134 self.cache.entry(share_id).or_insert(res).clone()
135 })
136 }
137}
138
139impl Endo<PlanRef> for Pruner<'_> {
140 fn pre(&mut self, t: PlanRef) -> PlanRef {
141 let prunable = |s: &&LogicalShare| {
142 *self
146 .counts
147 .get(&s.share_id())
148 .expect("Unprocessed shared node.")
149 == 1
150 || s.input().as_logical_scan().is_some()
151 || !(plan_visitor::has_logical_scan(s.input())
152 || plan_visitor::has_logical_source(s.input()))
153 };
154 t.as_logical_share()
155 .filter(prunable)
156 .map_or(t.clone(), |s| self.pre(s.input()))
157 }
158
159 fn apply(&mut self, t: PlanRef) -> PlanRef {
160 self.dag_apply(t)
161 }
162}