use crate::LogicalPlan;
use super::OptPass;
pub struct LimitPushThrough;
impl OptPass for LimitPushThrough {
fn name(&self) -> &'static str {
"LimitPushThrough"
}
fn apply(&self, plan: LogicalPlan) -> LogicalPlan {
push_limit(plan)
}
}
pub(super) fn push_limit(plan: LogicalPlan) -> LogicalPlan {
match plan {
LogicalPlan::Limit {
input,
count,
offset,
} => {
let inner = push_limit(*input);
if let Some(n) = count {
if offset.is_none() && can_push_limit_into(&inner) {
return inject_limit(inner, n);
}
}
LogicalPlan::Limit {
input: Box::new(inner),
count,
offset,
}
}
LogicalPlan::Project { input, columns } => LogicalPlan::Project {
input: Box::new(push_limit(*input)),
columns,
},
LogicalPlan::Filter { input, predicate } => LogicalPlan::Filter {
input: Box::new(push_limit(*input)),
predicate,
},
LogicalPlan::Sort { input, order_by } => LogicalPlan::Sort {
input: Box::new(push_limit(*input)),
order_by,
},
other => other,
}
}
fn can_push_limit_into(plan: &LogicalPlan) -> bool {
match plan {
LogicalPlan::Scan { .. } => true,
LogicalPlan::Filter { input, .. }
| LogicalPlan::Project { input, .. }
| LogicalPlan::Sort { input, .. }
| LogicalPlan::Limit { input, .. } => can_push_limit_into(input),
LogicalPlan::Aggregate { .. }
| LogicalPlan::Join { .. }
| LogicalPlan::Values { .. }
| LogicalPlan::Empty
| LogicalPlan::SetOp { .. }
| LogicalPlan::Cte { .. }
| LogicalPlan::CteRef { .. }
| LogicalPlan::Window { .. }
| LogicalPlan::Subquery { .. }
| LogicalPlan::Exists { .. }
| LogicalPlan::InSubquery { .. }
| LogicalPlan::Compute { .. } => false,
}
}
fn inject_limit(plan: LogicalPlan, limit: u64) -> LogicalPlan {
match plan {
LogicalPlan::Scan {
table,
alias,
limit: existing,
} => {
let new_limit = match existing {
Some(e) => Some(e.min(limit as usize)),
None => Some(limit as usize),
};
LogicalPlan::Scan {
table,
alias,
limit: new_limit,
}
}
LogicalPlan::Filter { input, predicate } => LogicalPlan::Filter {
input: Box::new(inject_limit(*input, limit)),
predicate,
},
LogicalPlan::Project { input, columns } => LogicalPlan::Project {
input: Box::new(inject_limit(*input, limit)),
columns,
},
LogicalPlan::Sort { input, order_by } => LogicalPlan::Sort {
input: Box::new(inject_limit(*input, limit)),
order_by,
},
other => other,
}
}