vortex_layout/plan/physical/optimizer/
rules.rs1use std::any::type_name;
7use std::fmt::Debug;
8use std::marker::PhantomData;
9
10use vortex_error::VortexResult;
11#[cfg(debug_assertions)]
12use vortex_error::vortex_ensure_eq;
13
14use crate::plan::physical::Plan;
15use crate::plan::physical::PlanRef;
16use crate::plan::physical::PlanVTable;
17
18pub trait PlanReduceRule<P: PlanVTable>: Debug + Send + Sync + 'static {
23 fn reduce(&self, plan: &Plan<P>) -> VortexResult<Option<PlanRef>>;
25}
26
27pub trait DynPlanReduceRule: Debug + Send + Sync + 'static {
29 fn matches(&self, plan: &PlanRef) -> bool;
31
32 fn reduce(&self, plan: &PlanRef) -> VortexResult<Option<PlanRef>>;
34}
35
36pub struct PlanReduceRuleAdapter<P, R> {
38 rule: R,
39 _plan: PhantomData<fn() -> P>,
40}
41
42impl<P, R> PlanReduceRuleAdapter<P, R> {
43 pub const fn new(rule: R) -> Self {
45 Self {
46 rule,
47 _plan: PhantomData,
48 }
49 }
50}
51
52impl<P, R> Debug for PlanReduceRuleAdapter<P, R>
53where
54 P: PlanVTable,
55 R: PlanReduceRule<P>,
56{
57 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 formatter
59 .debug_struct("PlanReduceRuleAdapter")
60 .field("plan", &type_name::<P>())
61 .field("rule", &self.rule)
62 .finish()
63 }
64}
65
66impl<P, R> DynPlanReduceRule for PlanReduceRuleAdapter<P, R>
67where
68 P: PlanVTable,
69 R: PlanReduceRule<P>,
70{
71 fn matches(&self, plan: &PlanRef) -> bool {
72 plan.is::<P>()
73 }
74
75 fn reduce(&self, plan: &PlanRef) -> VortexResult<Option<PlanRef>> {
76 let Some(plan) = plan.as_opt::<P>() else {
77 return Ok(None);
78 };
79 self.rule.reduce(plan)
80 }
81}
82
83pub struct PlanRuleSet {
85 rules: &'static [&'static dyn DynPlanReduceRule],
86}
87
88impl PlanRuleSet {
89 pub const fn new(rules: &'static [&'static dyn DynPlanReduceRule]) -> Self {
91 Self { rules }
92 }
93
94 pub fn evaluate(&self, plan: &PlanRef) -> VortexResult<Option<PlanRef>> {
96 for rule in self.rules {
97 if !rule.matches(plan) {
98 continue;
99 }
100 let Some(reduced) = rule.reduce(plan)? else {
101 continue;
102 };
103
104 #[cfg(debug_assertions)]
105 {
106 vortex_ensure_eq!(
107 reduced.row_count(),
108 plan.row_count(),
109 "Plan rewrite from {rule:?} changed row count"
110 );
111 vortex_ensure_eq!(
112 reduced.dtype(),
113 plan.dtype(),
114 "Plan rewrite from {rule:?} changed 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_ensure_eq!(
240 reduced.row_count(),
241 parent.row_count(),
242 "Plan rewrite from {rule:?} changed row count"
243 );
244 vortex_ensure_eq!(
245 reduced.dtype(),
246 parent.dtype(),
247 "Plan rewrite from {rule:?} changed dtype"
248 );
249 }
250
251 return Ok(Some(reduced));
252 }
253 Ok(None)
254 }
255}