vortex_layout/plan/optimizer/
rules.rs1use std::any::type_name;
7use std::fmt::Debug;
8use std::marker::PhantomData;
9
10use vortex_error::VortexResult;
11
12use crate::plan::Plan;
13use crate::plan::PlanRef;
14use crate::plan::PlanVTable;
15
16pub trait PlanReduceRule<P: PlanVTable>: Debug + Send + Sync + 'static {
21 fn reduce(&self, plan: &Plan<P>) -> VortexResult<Option<PlanRef>>;
23}
24
25pub trait DynPlanReduceRule: Debug + Send + Sync + 'static {
27 fn matches(&self, plan: &PlanRef) -> bool;
29
30 fn reduce(&self, plan: &PlanRef) -> VortexResult<Option<PlanRef>>;
32}
33
34pub struct PlanReduceRuleAdapter<P, R> {
36 rule: R,
37 _plan: PhantomData<fn() -> P>,
38}
39
40impl<P, R> PlanReduceRuleAdapter<P, R> {
41 pub const fn new(rule: R) -> Self {
43 Self {
44 rule,
45 _plan: PhantomData,
46 }
47 }
48}
49
50impl<P, R> Debug for PlanReduceRuleAdapter<P, R>
51where
52 P: PlanVTable,
53 R: PlanReduceRule<P>,
54{
55 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 formatter
57 .debug_struct("PlanReduceRuleAdapter")
58 .field("plan", &type_name::<P>())
59 .field("rule", &self.rule)
60 .finish()
61 }
62}
63
64impl<P, R> DynPlanReduceRule for PlanReduceRuleAdapter<P, R>
65where
66 P: PlanVTable,
67 R: PlanReduceRule<P>,
68{
69 fn matches(&self, plan: &PlanRef) -> bool {
70 plan.is::<P>()
71 }
72
73 fn reduce(&self, plan: &PlanRef) -> VortexResult<Option<PlanRef>> {
74 let Some(plan) = plan.as_opt::<P>() else {
75 return Ok(None);
76 };
77 self.rule.reduce(plan)
78 }
79}
80
81pub struct PlanRuleSet {
83 rules: &'static [&'static dyn DynPlanReduceRule],
84}
85
86impl PlanRuleSet {
87 pub const fn new(rules: &'static [&'static dyn DynPlanReduceRule]) -> Self {
89 Self { rules }
90 }
91
92 pub fn evaluate(&self, plan: &PlanRef) -> VortexResult<Option<PlanRef>> {
94 for rule in self.rules {
95 if !rule.matches(plan) {
96 continue;
97 }
98 let Some(reduced) = rule.reduce(plan)? else {
99 continue;
100 };
101
102 #[cfg(debug_assertions)]
103 {
104 vortex_error::vortex_ensure!(
105 reduced.row_count() == plan.row_count(),
106 "Plan rewrite from {rule:?} changed row count from {} to {}",
107 plan.row_count(),
108 reduced.row_count()
109 );
110 vortex_error::vortex_ensure!(
111 reduced.dtype() == plan.dtype(),
112 "Plan rewrite from {rule:?} changed dtype from {} to {}",
113 plan.dtype(),
114 reduced.dtype()
115 );
116 }
117
118 return Ok(Some(reduced));
119 }
120 Ok(None)
121 }
122}
123
124pub trait PlanParentReduceRule<C: PlanVTable>: Debug + Send + Sync + 'static {
129 type Parent: PlanVTable;
131
132 fn reduce_parent(
134 &self,
135 child: &Plan<C>,
136 parent: &Plan<Self::Parent>,
137 child_idx: usize,
138 ) -> VortexResult<Option<PlanRef>>;
139}
140
141pub trait DynPlanParentReduceRule: Debug + Send + Sync + 'static {
143 fn matches(&self, child: &PlanRef, parent: &PlanRef) -> bool;
145
146 fn reduce_parent(
148 &self,
149 child: &PlanRef,
150 parent: &PlanRef,
151 child_idx: usize,
152 ) -> VortexResult<Option<PlanRef>>;
153}
154
155pub struct PlanParentReduceRuleAdapter<C, R> {
157 rule: R,
158 _child: PhantomData<fn() -> C>,
159}
160
161impl<C, R> PlanParentReduceRuleAdapter<C, R> {
162 pub const fn new(rule: R) -> Self {
164 Self {
165 rule,
166 _child: PhantomData,
167 }
168 }
169}
170
171impl<C, R> Debug for PlanParentReduceRuleAdapter<C, R>
172where
173 C: PlanVTable,
174 R: PlanParentReduceRule<C>,
175{
176 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 formatter
178 .debug_struct("PlanParentReduceRuleAdapter")
179 .field("parent", &type_name::<R::Parent>())
180 .field("child", &type_name::<C>())
181 .field("rule", &self.rule)
182 .finish()
183 }
184}
185
186impl<C, R> DynPlanParentReduceRule for PlanParentReduceRuleAdapter<C, R>
187where
188 C: PlanVTable,
189 R: PlanParentReduceRule<C>,
190{
191 fn matches(&self, child: &PlanRef, parent: &PlanRef) -> bool {
192 child.is::<C>() && parent.is::<R::Parent>()
193 }
194
195 fn reduce_parent(
196 &self,
197 child: &PlanRef,
198 parent: &PlanRef,
199 child_idx: usize,
200 ) -> VortexResult<Option<PlanRef>> {
201 let Some(child) = child.as_opt::<C>() else {
202 return Ok(None);
203 };
204 let Some(parent) = parent.as_opt::<R::Parent>() else {
205 return Ok(None);
206 };
207 self.rule.reduce_parent(child, parent, child_idx)
208 }
209}
210
211pub struct PlanParentRuleSet {
213 rules: &'static [&'static dyn DynPlanParentReduceRule],
214}
215
216impl PlanParentRuleSet {
217 pub const fn new(rules: &'static [&'static dyn DynPlanParentReduceRule]) -> Self {
219 Self { rules }
220 }
221
222 pub fn evaluate(
224 &self,
225 child: &PlanRef,
226 parent: &PlanRef,
227 child_idx: usize,
228 ) -> VortexResult<Option<PlanRef>> {
229 for rule in self.rules {
230 if !rule.matches(child, parent) {
231 continue;
232 }
233 let Some(reduced) = rule.reduce_parent(child, parent, child_idx)? else {
234 continue;
235 };
236
237 #[cfg(debug_assertions)]
238 {
239 vortex_error::vortex_ensure!(
240 reduced.row_count() == parent.row_count(),
241 "Plan rewrite from {rule:?} changed row count from {} to {}",
242 parent.row_count(),
243 reduced.row_count()
244 );
245 vortex_error::vortex_ensure!(
246 reduced.dtype() == parent.dtype(),
247 "Plan rewrite from {rule:?} changed dtype from {} to {}",
248 parent.dtype(),
249 reduced.dtype()
250 );
251 }
252
253 return Ok(Some(reduced));
254 }
255 Ok(None)
256 }
257}