Optimizes the logical plan by applying optimizer rules, and invoking observer function after each call
(
&self,
plan: LogicalPlan,
config: &dyn OptimizerConfig,
mut observer: F,
)
| 363 | /// Optimizes the logical plan by applying optimizer rules, and |
| 364 | /// invoking observer function after each call |
| 365 | pub fn optimize<F>( |
| 366 | &self, |
| 367 | plan: LogicalPlan, |
| 368 | config: &dyn OptimizerConfig, |
| 369 | mut observer: F, |
| 370 | ) -> Result<LogicalPlan> |
| 371 | where |
| 372 | F: FnMut(&LogicalPlan, &dyn OptimizerRule), |
| 373 | { |
| 374 | // verify LP is valid, before the first LP optimizer pass. |
| 375 | plan.check_invariants(InvariantLevel::Executable) |
| 376 | .map_err(|e| e.context("Invalid input plan before LP Optimizers"))?; |
| 377 | |
| 378 | let start_time = Instant::now(); |
| 379 | let options = config.options(); |
| 380 | let mut new_plan = plan; |
| 381 | |
| 382 | let mut previous_plans = HashSet::with_capacity(16); |
| 383 | previous_plans.insert(LogicalPlanSignature::new(&new_plan)); |
| 384 | |
| 385 | let starting_schema = Arc::clone(new_plan.schema()); |
| 386 | |
| 387 | let mut i = 0; |
| 388 | while i < options.optimizer.max_passes { |
| 389 | log_plan(&format!("Optimizer input (pass {i})"), &new_plan); |
| 390 | |
| 391 | for rule in &self.rules { |
| 392 | // If skipping failed rules, copy plan before attempting to rewrite |
| 393 | // as rewriting is destructive |
| 394 | let prev_plan = options |
| 395 | .optimizer |
| 396 | .skip_failed_rules |
| 397 | .then(|| new_plan.clone()); |
| 398 | |
| 399 | let starting_schema = Arc::clone(new_plan.schema()); |
| 400 | |
| 401 | let result = match rule.apply_order() { |
| 402 | // optimizer handles recursion |
| 403 | Some(apply_order) => new_plan.rewrite_with_subqueries( |
| 404 | &mut Rewriter::new(apply_order, rule.as_ref(), config), |
| 405 | ), |
| 406 | // rule handles recursion itself |
| 407 | None => { |
| 408 | rule.rewrite(new_plan, config) |
| 409 | }, |
| 410 | } |
| 411 | .and_then(|tnr| { |
| 412 | // run checks optimizer invariant checks, per optimizer rule applied |
| 413 | assert_valid_optimization(&tnr.data, &starting_schema) |
| 414 | .map_err(|e| e.context(format!("Check optimizer-specific invariants after optimizer rule: {}", rule.name())))?; |
| 415 | |
| 416 | // run LP invariant checks only in debug mode for performance reasons |
| 417 | #[cfg(debug_assertions)] |
| 418 | tnr.data.check_invariants(InvariantLevel::Executable) |
| 419 | .map_err(|e| e.context(format!("Invalid (non-executable) plan after Optimizer rule: {}", rule.name())))?; |
| 420 | |
| 421 | Ok(tnr) |
| 422 | }); |