dbx_core/sql/optimizer/
predicate_pushdown.rs1use crate::error::DbxResult;
6use crate::sql::planner::{BinaryOperator, Expr, LogicalPlan};
7
8use super::OptimizationRule;
9
10pub struct PredicatePushdownRule;
12
13impl OptimizationRule for PredicatePushdownRule {
14 fn name(&self) -> &str {
15 "PredicatePushdown"
16 }
17
18 fn apply(&self, plan: LogicalPlan) -> DbxResult<LogicalPlan> {
19 self.push_down(plan)
20 }
21}
22
23impl PredicatePushdownRule {
24 fn push_down(&self, plan: LogicalPlan) -> DbxResult<LogicalPlan> {
25 match plan {
26 LogicalPlan::Filter { input, predicate } => {
27 let optimized_input = self.push_down(*input)?;
28 match optimized_input {
29 LogicalPlan::Project {
30 input: project_input,
31 projections: columns,
32 } if self.can_push_through_project(&predicate, &columns) => {
33 let pushed = self.push_down(LogicalPlan::Filter {
34 input: project_input,
35 predicate,
36 })?;
37 Ok(LogicalPlan::Project {
38 input: Box::new(pushed),
39 projections: columns,
40 })
41 }
42 LogicalPlan::Scan {
43 table,
44 columns,
45 filter: None,
46 } => Ok(LogicalPlan::Scan {
47 table,
48 columns,
49 filter: Some(predicate),
50 }),
51 LogicalPlan::Scan {
52 table,
53 columns,
54 filter: Some(existing),
55 } => Ok(LogicalPlan::Scan {
56 table,
57 columns,
58 filter: Some(Expr::BinaryOp {
59 left: Box::new(existing),
60 op: BinaryOperator::And,
61 right: Box::new(predicate),
62 }),
63 }),
64 other => Ok(LogicalPlan::Filter {
65 input: Box::new(other),
66 predicate,
67 }),
68 }
69 }
70 LogicalPlan::Project { input, projections } => Ok(LogicalPlan::Project {
71 input: Box::new(self.push_down(*input)?),
72 projections,
73 }),
74 LogicalPlan::Sort { input, order_by } => Ok(LogicalPlan::Sort {
75 input: Box::new(self.push_down(*input)?),
76 order_by,
77 }),
78 LogicalPlan::Limit {
79 input,
80 count,
81 offset,
82 } => Ok(LogicalPlan::Limit {
83 input: Box::new(self.push_down(*input)?),
84 count,
85 offset,
86 }),
87 LogicalPlan::Aggregate {
88 input,
89 group_by,
90 aggregates,
91 } => Ok(LogicalPlan::Aggregate {
92 input: Box::new(self.push_down(*input)?),
93 group_by,
94 aggregates,
95 }),
96 other => Ok(other),
97 }
98 }
99
100 fn can_push_through_project(
102 &self,
103 predicate: &Expr,
104 _projections: &[(Expr, Option<String>)],
105 ) -> bool {
106 self.is_simple_column_predicate(predicate)
107 }
108
109 fn is_simple_column_predicate(&self, expr: &Expr) -> bool {
110 match expr {
111 Expr::Column(_) | Expr::Literal(_) => true,
112 Expr::BinaryOp { left, right, .. } => {
113 self.is_simple_column_predicate(left) && self.is_simple_column_predicate(right)
114 }
115 Expr::IsNull(inner) | Expr::IsNotNull(inner) => self.is_simple_column_predicate(inner),
116 _ => false,
117 }
118 }
119}