1use std::fmt::Debug;
21use std::sync::Arc;
22
23use chrono::{DateTime, Utc};
24use datafusion_expr::registry::FunctionRegistry;
25use datafusion_expr::{InvariantLevel, assert_expected_schema};
26use log::{debug, warn};
27
28use datafusion_common::alias::AliasGenerator;
29use datafusion_common::config::ConfigOptions;
30use datafusion_common::instant::Instant;
31use datafusion_common::tree_node::{
32 Transformed, TreeNode, TreeNodeRecursion, TreeNodeRewriter,
33};
34use datafusion_common::{DFSchema, DataFusionError, HashSet, Result, internal_err};
35use datafusion_expr::dml::CopyTo;
36use datafusion_expr::logical_plan::LogicalPlan;
37use datafusion_expr::{
38 Aggregate, Analyze, CreateMemoryTable, CreateView, DdlStatement, Distinct,
39 DistinctOn, DmlStatement, Explain, Expr, Extension, Filter, Join, Limit, Projection,
40 RecursiveQuery, Repartition, Sort, Statement, Subquery, SubqueryAlias, Union, Unnest,
41 Window,
42};
43
44use crate::common_subexpr_eliminate::CommonSubexprEliminate;
45use crate::decorrelate_lateral_join::DecorrelateLateralJoin;
46use crate::decorrelate_predicate_subquery::DecorrelatePredicateSubquery;
47use crate::eliminate_cross_join::EliminateCrossJoin;
48use crate::eliminate_duplicated_expr::EliminateDuplicatedExpr;
49use crate::eliminate_filter::EliminateFilter;
50use crate::eliminate_group_by_constant::EliminateGroupByConstant;
51use crate::eliminate_join::EliminateJoin;
52use crate::eliminate_limit::EliminateLimit;
53use crate::eliminate_outer_join::EliminateOuterJoin;
54use crate::extract_equijoin_predicate::ExtractEquijoinPredicate;
55use crate::extract_leaf_expressions::{ExtractLeafExpressions, PushDownLeafProjections};
56use crate::filter_null_join_keys::FilterNullJoinKeys;
57use crate::optimize_projections::OptimizeProjections;
58use crate::optimize_unions::OptimizeUnions;
59use crate::plan_signature::LogicalPlanSignature;
60use crate::propagate_empty_relation::PropagateEmptyRelation;
61use crate::push_down_filter::PushDownFilter;
62use crate::push_down_limit::PushDownLimit;
63use crate::replace_distinct_aggregate::ReplaceDistinctWithAggregate;
64use crate::rewrite_set_comparison::RewriteSetComparison;
65use crate::scalar_subquery_to_join::ScalarSubqueryToJoin;
66use crate::simplify_expressions::SimplifyExpressions;
67use crate::single_distinct_to_groupby::SingleDistinctToGroupBy;
68use crate::unions_to_filter::UnionsToFilter;
69use crate::utils::log_plan;
70
71pub trait OptimizerRule: Debug {
84 fn name(&self) -> &str;
86
87 fn apply_order(&self) -> Option<ApplyOrder> {
92 None
93 }
94
95 #[deprecated(since = "47.0.0", note = "This method is no longer used")]
97 fn supports_rewrite(&self) -> bool {
98 true
99 }
100
101 fn rewrite(
136 &self,
137 _plan: LogicalPlan,
138 _config: &dyn OptimizerConfig,
139 ) -> Result<Transformed<LogicalPlan>, DataFusionError> {
140 internal_err!("rewrite is not implemented for {}", self.name())
141 }
142}
143
144pub trait OptimizerConfig {
146 fn query_execution_start_time(&self) -> Option<DateTime<Utc>>;
150
151 fn alias_generator(&self) -> &Arc<AliasGenerator>;
153
154 fn options(&self) -> Arc<ConfigOptions>;
155
156 fn function_registry(&self) -> Option<&dyn FunctionRegistry> {
157 None
158 }
159}
160
161#[derive(Debug)]
164pub struct OptimizerContext {
165 query_execution_start_time: Option<DateTime<Utc>>,
169
170 alias_generator: Arc<AliasGenerator>,
172
173 options: Arc<ConfigOptions>,
174}
175
176impl OptimizerContext {
177 pub fn new() -> Self {
179 let mut options = ConfigOptions::default();
180 options.optimizer.filter_null_join_keys = true;
181
182 Self::new_with_config_options(Arc::new(options))
183 }
184
185 pub fn new_with_config_options(options: Arc<ConfigOptions>) -> Self {
187 Self {
188 query_execution_start_time: Some(Utc::now()),
189 alias_generator: Arc::new(AliasGenerator::new()),
190 options,
191 }
192 }
193
194 pub fn filter_null_keys(mut self, filter_null_keys: bool) -> Self {
196 Arc::make_mut(&mut self.options)
197 .optimizer
198 .filter_null_join_keys = filter_null_keys;
199 self
200 }
201
202 pub fn with_query_execution_start_time(
204 mut self,
205 query_execution_start_time: DateTime<Utc>,
206 ) -> Self {
207 self.query_execution_start_time = Some(query_execution_start_time);
208 self
209 }
210
211 pub fn without_query_execution_start_time(mut self) -> Self {
214 self.query_execution_start_time = None;
215 self
216 }
217
218 pub fn with_skip_failing_rules(mut self, b: bool) -> Self {
221 Arc::make_mut(&mut self.options).optimizer.skip_failed_rules = b;
222 self
223 }
224
225 pub fn with_max_passes(mut self, v: u8) -> Self {
227 Arc::make_mut(&mut self.options).optimizer.max_passes = v as usize;
228 self
229 }
230}
231
232impl Default for OptimizerContext {
233 fn default() -> Self {
235 Self::new()
236 }
237}
238
239impl OptimizerConfig for OptimizerContext {
240 fn query_execution_start_time(&self) -> Option<DateTime<Utc>> {
241 self.query_execution_start_time
242 }
243
244 fn alias_generator(&self) -> &Arc<AliasGenerator> {
245 &self.alias_generator
246 }
247
248 fn options(&self) -> Arc<ConfigOptions> {
249 Arc::clone(&self.options)
250 }
251}
252
253#[derive(Clone, Debug)]
255pub struct Optimizer {
256 pub rules: Vec<Arc<dyn OptimizerRule + Send + Sync>>,
258}
259
260#[derive(Debug, Clone, Copy, PartialEq)]
265pub enum ApplyOrder {
266 TopDown,
268 BottomUp,
270}
271
272impl Default for Optimizer {
273 fn default() -> Self {
274 Self::new()
275 }
276}
277
278impl Optimizer {
279 pub fn new() -> Self {
281 let rules: Vec<Arc<dyn OptimizerRule + Sync + Send>> = vec![
291 Arc::new(RewriteSetComparison::new()),
292 Arc::new(OptimizeUnions::new()),
293 Arc::new(UnionsToFilter::new()),
294 Arc::new(SimplifyExpressions::new()),
295 Arc::new(ReplaceDistinctWithAggregate::new()),
296 Arc::new(EliminateJoin::new()),
297 Arc::new(DecorrelatePredicateSubquery::new()),
298 Arc::new(ScalarSubqueryToJoin::new()),
299 Arc::new(DecorrelateLateralJoin::new()),
300 Arc::new(ExtractEquijoinPredicate::new()),
301 Arc::new(EliminateDuplicatedExpr::new()),
302 Arc::new(EliminateFilter::new()),
303 Arc::new(EliminateCrossJoin::new()),
304 Arc::new(EliminateLimit::new()),
305 Arc::new(PropagateEmptyRelation::new()),
306 Arc::new(FilterNullJoinKeys::default()),
307 Arc::new(EliminateOuterJoin::new()),
308 Arc::new(PushDownLimit::new()),
310 Arc::new(PushDownFilter::new()),
311 Arc::new(SingleDistinctToGroupBy::new()),
312 Arc::new(EliminateGroupByConstant::new()),
315 Arc::new(CommonSubexprEliminate::new()),
316 Arc::new(ExtractLeafExpressions::new()),
317 Arc::new(PushDownLeafProjections::new()),
318 Arc::new(OptimizeProjections::new()),
319 ];
320
321 Self::with_rules(rules)
322 }
323
324 pub fn with_rules(rules: Vec<Arc<dyn OptimizerRule + Send + Sync>>) -> Self {
326 Self { rules }
327 }
328}
329
330struct Rewriter<'a> {
332 apply_order: ApplyOrder,
333 rule: &'a dyn OptimizerRule,
334 config: &'a dyn OptimizerConfig,
335}
336
337impl<'a> Rewriter<'a> {
338 fn new(
339 apply_order: ApplyOrder,
340 rule: &'a dyn OptimizerRule,
341 config: &'a dyn OptimizerConfig,
342 ) -> Self {
343 Self {
344 apply_order,
345 rule,
346 config,
347 }
348 }
349}
350
351impl TreeNodeRewriter for Rewriter<'_> {
352 type Node = LogicalPlan;
353
354 fn f_down(&mut self, node: LogicalPlan) -> Result<Transformed<LogicalPlan>> {
355 if self.apply_order == ApplyOrder::TopDown {
356 self.rule.rewrite(node, self.config)
357 } else {
358 Ok(Transformed::no(node))
359 }
360 }
361
362 fn f_up(&mut self, node: LogicalPlan) -> Result<Transformed<LogicalPlan>> {
363 if self.apply_order == ApplyOrder::BottomUp {
364 self.rule.rewrite(node, self.config)
365 } else {
366 Ok(Transformed::no(node))
367 }
368 }
369}
370
371fn map_children_mut<F: FnMut(&mut LogicalPlan) -> Result<bool>>(
392 plan: &mut LogicalPlan,
393 mut f: F,
394) -> Result<bool> {
395 Ok(match plan {
396 LogicalPlan::Projection(Projection { input, .. })
397 | LogicalPlan::Filter(Filter { input, .. })
398 | LogicalPlan::Repartition(Repartition { input, .. })
399 | LogicalPlan::Window(Window { input, .. })
400 | LogicalPlan::Aggregate(Aggregate { input, .. })
401 | LogicalPlan::Sort(Sort { input, .. })
402 | LogicalPlan::Limit(Limit { input, .. })
403 | LogicalPlan::SubqueryAlias(SubqueryAlias { input, .. })
404 | LogicalPlan::Analyze(Analyze { input, .. })
405 | LogicalPlan::Dml(DmlStatement { input, .. })
406 | LogicalPlan::Copy(CopyTo { input, .. })
407 | LogicalPlan::Unnest(Unnest { input, .. }) => f(Arc::make_mut(input))?,
408 LogicalPlan::Subquery(Subquery { subquery, .. }) => f(Arc::make_mut(subquery))?,
409 LogicalPlan::Join(Join { left, right, .. }) => {
410 let l = f(Arc::make_mut(left))?;
411 let r = f(Arc::make_mut(right))?;
412 l || r
413 }
414 LogicalPlan::Union(Union { inputs, .. }) => {
415 let mut changed = false;
416 for input in inputs {
417 changed |= f(Arc::make_mut(input))?;
418 }
419 changed
420 }
421 LogicalPlan::Distinct(Distinct::All(input)) => f(Arc::make_mut(input))?,
422 LogicalPlan::Distinct(Distinct::On(DistinctOn { input, .. })) => {
423 f(Arc::make_mut(input))?
424 }
425 LogicalPlan::Explain(Explain { plan, .. }) => f(Arc::make_mut(plan))?,
426 LogicalPlan::Ddl(DdlStatement::CreateMemoryTable(CreateMemoryTable {
427 input,
428 ..
429 }))
430 | LogicalPlan::Ddl(DdlStatement::CreateView(CreateView { input, .. })) => {
431 f(Arc::make_mut(input))?
432 }
433 LogicalPlan::RecursiveQuery(RecursiveQuery {
434 static_term,
435 recursive_term,
436 ..
437 }) => {
438 let s = f(Arc::make_mut(static_term))?;
439 let r = f(Arc::make_mut(recursive_term))?;
440 s || r
441 }
442 LogicalPlan::Statement(Statement::Prepare(p)) => f(Arc::make_mut(&mut p.input))?,
443 LogicalPlan::Extension(Extension { node }) => {
444 let inputs = node.inputs();
445 if inputs.is_empty() {
446 false
447 } else {
448 let mut changed = false;
451 let exprs = node.expressions();
452 let new_inputs: Vec<LogicalPlan> = inputs
453 .into_iter()
454 .map(|input| {
455 let mut plan = input.clone();
456 if f(&mut plan)? {
457 changed = true;
458 }
459 Ok(plan)
460 })
461 .collect::<Result<Vec<_>>>()?;
462 if changed {
463 *node = node.with_exprs_and_inputs(exprs, new_inputs)?;
464 }
465 changed
466 }
467 }
468 LogicalPlan::TableScan { .. }
470 | LogicalPlan::EmptyRelation { .. }
471 | LogicalPlan::Values { .. }
472 | LogicalPlan::DescribeTable(_)
473 | LogicalPlan::Ddl(DdlStatement::CreateExternalTable(_))
474 | LogicalPlan::Ddl(DdlStatement::CreateCatalogSchema(_))
475 | LogicalPlan::Ddl(DdlStatement::CreateCatalog(_))
476 | LogicalPlan::Ddl(DdlStatement::CreateIndex(_))
477 | LogicalPlan::Ddl(DdlStatement::DropTable(_))
478 | LogicalPlan::Ddl(DdlStatement::DropView(_))
479 | LogicalPlan::Ddl(DdlStatement::DropCatalogSchema(_))
480 | LogicalPlan::Ddl(DdlStatement::CreateFunction(_))
481 | LogicalPlan::Ddl(DdlStatement::DropFunction(_))
482 | LogicalPlan::Statement(_) => false,
483 })
484}
485
486#[cfg_attr(feature = "recursive_protection", recursive::recursive)]
498fn rewrite_plan_in_place(
499 plan: &mut LogicalPlan,
500 apply_order: ApplyOrder,
501 rule: &dyn OptimizerRule,
502 config: &dyn OptimizerConfig,
503) -> Result<bool> {
504 let mut changed = false;
506 if apply_order == ApplyOrder::TopDown {
507 let owned = std::mem::take(plan);
512 let result = rule.rewrite(owned, config)?;
513 *plan = result.data;
514 changed |= result.transformed;
515 if result.tnr == TreeNodeRecursion::Stop {
517 return Ok(changed);
518 }
519 }
520
521 let mut child_schema_changed = false;
522 let children_changed = map_children_mut(plan, |child| {
523 let old_schema = Arc::clone(child.schema());
524 let child_changed = rewrite_plan_in_place(child, apply_order, rule, config)?;
525 if child_changed && old_schema.as_ref() != child.schema().as_ref() {
526 child_schema_changed = true;
527 }
528 Ok(child_changed)
529 })?;
530 changed |= children_changed;
531
532 if child_schema_changed {
533 let owned = std::mem::take(plan);
536 *plan = owned.recompute_schema()?;
537 }
538
539 if apply_order == ApplyOrder::BottomUp {
541 let owned = std::mem::take(plan);
542 let result = rule.rewrite(owned, config)?;
543 *plan = result.data;
544 changed |= result.transformed;
545 }
546
547 Ok(changed)
548}
549
550fn plan_has_subqueries(plan: &LogicalPlan) -> bool {
558 let mut found = false;
559 let _ = plan.apply(|node| {
560 if found {
561 return Ok(TreeNodeRecursion::Stop);
562 }
563 node.apply_expressions(|expr| {
564 if found {
565 return Ok(TreeNodeRecursion::Stop);
566 }
567 expr.apply(|e| {
568 if matches!(
569 e,
570 Expr::Exists(_)
571 | Expr::InSubquery(_)
572 | Expr::SetComparison(_)
573 | Expr::ScalarSubquery(_)
574 ) {
575 found = true;
576 Ok(TreeNodeRecursion::Stop)
577 } else {
578 Ok(TreeNodeRecursion::Continue)
579 }
580 })
581 })?;
582 Ok(if found {
583 TreeNodeRecursion::Stop
584 } else {
585 TreeNodeRecursion::Continue
586 })
587 });
588 found
589}
590
591impl Optimizer {
592 pub fn optimize<F>(
595 &self,
596 plan: LogicalPlan,
597 config: &dyn OptimizerConfig,
598 mut observer: F,
599 ) -> Result<LogicalPlan>
600 where
601 F: FnMut(&LogicalPlan, &dyn OptimizerRule),
602 {
603 plan.check_invariants(InvariantLevel::Executable)
605 .map_err(|e| e.context("Invalid input plan before LP Optimizers"))?;
606
607 let start_time = Instant::now();
608 let options = config.options();
609 let mut new_plan = plan;
610
611 let mut previous_plans = HashSet::with_capacity(16);
612 previous_plans.insert(LogicalPlanSignature::new(&new_plan));
613
614 let starting_schema = Arc::clone(new_plan.schema());
615
616 let mut i = 0;
617 while i < options.optimizer.max_passes {
618 log_plan(&format!("Optimizer input (pass {i})"), &new_plan);
619
620 let mut has_subqueries = plan_has_subqueries(&new_plan);
625
626 for rule in &self.rules {
627 let prev_plan = options
630 .optimizer
631 .skip_failed_rules
632 .then(|| new_plan.clone());
633
634 let starting_schema = Arc::clone(new_plan.schema());
635
636 let result = match rule.apply_order() {
637 Some(apply_order) => {
639 if has_subqueries {
640 new_plan.rewrite_with_subqueries(
644 &mut Rewriter::new(
645 apply_order,
646 rule.as_ref(),
647 config,
648 ),
649 )
650 } else {
651 rewrite_plan_in_place(
661 &mut new_plan,
662 apply_order,
663 rule.as_ref(),
664 config,
665 )
666 .map(|transformed| {
667 Transformed::new_transformed(
668 std::mem::take(&mut new_plan),
669 transformed,
670 )
671 })
672 }
673 }
674 None => {
676 rule.rewrite(new_plan, config)
677 },
678 }
679 .and_then(|tnr| {
680 assert_valid_optimization(&tnr.data, &starting_schema)
682 .map_err(|e| e.context(format!("Check optimizer-specific invariants after optimizer rule: {}", rule.name())))?;
683
684 #[cfg(debug_assertions)]
686 tnr.data.check_invariants(InvariantLevel::Executable)
687 .map_err(|e| e.context(format!("Invalid (non-executable) plan after Optimizer rule: {}", rule.name())))?;
688
689 Ok(tnr)
690 });
691
692 match (result, prev_plan) {
694 (
696 Ok(Transformed {
697 data, transformed, ..
698 }),
699 _,
700 ) => {
701 new_plan = data;
702 observer(&new_plan, rule.as_ref());
703 if transformed {
704 has_subqueries = plan_has_subqueries(&new_plan);
705 log_plan(rule.name(), &new_plan);
706 } else {
707 debug!(
708 "Plan unchanged by optimizer rule '{}' (pass {})",
709 rule.name(),
710 i
711 );
712 }
713 }
714 (Err(e), Some(orig_plan)) => {
717 warn!(
721 "Skipping optimizer rule '{}' due to unexpected error: {}",
722 rule.name(),
723 e
724 );
725 new_plan = orig_plan;
726 }
727 (Err(e), None) => {
729 return Err(e.context(format!(
730 "Optimizer rule '{}' failed",
731 rule.name()
732 )));
733 }
734 }
735 }
736 log_plan(&format!("Optimized plan (pass {i})"), &new_plan);
737
738 let plan_is_fresh =
740 previous_plans.insert(LogicalPlanSignature::new(&new_plan));
741 if !plan_is_fresh {
742 debug!("optimizer pass {i} did not make changes");
744 break;
745 }
746 i += 1;
747 }
748
749 assert_valid_optimization(&new_plan, &starting_schema).map_err(|e| {
751 e.context("Check optimizer-specific invariants after all passes")
752 })?;
753
754 new_plan
756 .check_invariants(InvariantLevel::Executable)
757 .map_err(|e| {
758 e.context("Invalid (non-executable) plan after LP Optimizers")
759 })?;
760
761 log_plan("Final optimized plan", &new_plan);
762 debug!("Optimizer took {} ms", start_time.elapsed().as_millis());
763 Ok(new_plan)
764 }
765}
766
767fn assert_valid_optimization(
772 plan: &LogicalPlan,
773 prev_schema: &Arc<DFSchema>,
774) -> Result<()> {
775 assert_expected_schema(prev_schema, plan)?;
778
779 Ok(())
780}
781
782#[cfg(test)]
783mod tests {
784 use std::sync::{Arc, Mutex};
785
786 use datafusion_common::tree_node::Transformed;
787 use datafusion_common::{
788 Column, DFSchema, DFSchemaRef, DataFusionError, Result, assert_contains, plan_err,
789 };
790 use datafusion_expr::logical_plan::EmptyRelation;
791 use datafusion_expr::{
792 Expr, JoinType, LogicalPlan, LogicalPlanBuilder, Projection, col, lit,
793 };
794
795 use crate::optimizer::Optimizer;
796 use crate::test::{test_table_scan, test_table_scan_with_name};
797 use crate::{OptimizerConfig, OptimizerContext, OptimizerRule};
798
799 use super::ApplyOrder;
800
801 #[test]
802 fn skip_failing_rule() {
803 let opt = Optimizer::with_rules(vec![Arc::new(BadRule {})]);
804 let config = OptimizerContext::new().with_skip_failing_rules(true);
805 let plan = LogicalPlan::EmptyRelation(EmptyRelation {
806 produce_one_row: false,
807 schema: Arc::new(DFSchema::empty()),
808 });
809 opt.optimize(plan, &config, &observe).unwrap();
810 }
811
812 #[test]
813 fn no_skip_failing_rule() {
814 let opt = Optimizer::with_rules(vec![Arc::new(BadRule {})]);
815 let config = OptimizerContext::new().with_skip_failing_rules(false);
816 let plan = LogicalPlan::EmptyRelation(EmptyRelation {
817 produce_one_row: false,
818 schema: Arc::new(DFSchema::empty()),
819 });
820 let err = opt.optimize(plan, &config, &observe).unwrap_err();
821 assert_eq!(
822 "Optimizer rule 'bad rule' failed\ncaused by\n\
823 Error during planning: rule failed",
824 err.strip_backtrace()
825 );
826 }
827
828 #[test]
829 fn generate_different_schema() {
830 let opt = Optimizer::with_rules(vec![Arc::new(GetTableScanRule {})]);
831 let config = OptimizerContext::new().with_skip_failing_rules(false);
832 let plan = LogicalPlan::EmptyRelation(EmptyRelation {
833 produce_one_row: false,
834 schema: Arc::new(DFSchema::empty()),
835 });
836 let err = opt.optimize(plan, &config, &observe).unwrap_err();
837
838 assert_contains!(
840 err.strip_backtrace(),
841 "Failed due to a difference in schemas: original schema: DFSchema"
842 );
843 }
844
845 #[test]
846 fn skip_generate_different_schema() {
847 let opt = Optimizer::with_rules(vec![Arc::new(GetTableScanRule {})]);
848 let config = OptimizerContext::new().with_skip_failing_rules(true);
849 let plan = LogicalPlan::EmptyRelation(EmptyRelation {
850 produce_one_row: false,
851 schema: Arc::new(DFSchema::empty()),
852 });
853 opt.optimize(plan, &config, &observe).unwrap();
854 }
855
856 #[test]
857 fn generate_same_schema_different_metadata() -> Result<()> {
858 let opt = Optimizer::with_rules(vec![Arc::new(GetTableScanRule {})]);
861 let config = OptimizerContext::new().with_skip_failing_rules(false);
862
863 let input = Arc::new(test_table_scan()?);
864 let input_schema = Arc::clone(input.schema());
865
866 let plan = LogicalPlan::Projection(Projection::try_new_with_schema(
867 vec![col("a"), col("b"), col("c")],
868 input,
869 add_metadata_to_fields(input_schema.as_ref()),
870 )?);
871
872 assert_ne!(plan.schema().as_ref(), input_schema.as_ref());
874 let optimized_plan = opt.optimize(plan, &config, &observe)?;
875 assert_eq!(optimized_plan.schema().as_ref(), input_schema.as_ref());
877 Ok(())
878 }
879
880 #[test]
881 fn in_place_rewrite_recomputes_parent_schema_when_child_schema_changes() -> Result<()>
882 {
883 let left = LogicalPlanBuilder::from(test_table_scan_with_name("left")?)
884 .project(vec![col("left.a"), col("left.b"), col("left.c")])?
885 .build()?;
886 let right = LogicalPlanBuilder::from(test_table_scan_with_name("right")?)
887 .project(vec![col("right.a"), col("right.b"), col("right.c")])?
888 .build()?;
889 let mut plan = LogicalPlanBuilder::from(left)
890 .join_on(right, JoinType::Inner, [col("left.a").eq(col("right.a"))])?
891 .build()?;
892
893 assert_eq!(plan.schema().fields().len(), 6);
894
895 let changed = super::rewrite_plan_in_place(
896 &mut plan,
897 ApplyOrder::TopDown,
898 &KeepOnlyAProjectionRule {},
899 &OptimizerContext::new(),
900 )?;
901
902 assert!(changed);
903 assert_eq!(plan.schema().fields().len(), 2);
904 assert!(plan.schema().has_column_with_unqualified_name("a"));
905 Ok(())
906 }
907
908 #[test]
909 fn optimizer_detects_plan_equal_to_the_initial() -> Result<()> {
910 let opt = Optimizer::with_rules(vec![Arc::new(RotateProjectionRule::new(false))]);
914 let config = OptimizerContext::new().with_max_passes(16);
915
916 let initial_plan = LogicalPlanBuilder::empty(false)
917 .project([lit(1), lit(2), lit(3)])?
918 .project([lit(100)])? .build()?;
920
921 let mut plans: Vec<LogicalPlan> = Vec::new();
922 let final_plan =
923 opt.optimize(initial_plan.clone(), &config, |p, _| plans.push(p.clone()))?;
924
925 assert_eq!(3, plans.len());
927
928 assert_eq!(initial_plan, final_plan);
930
931 Ok(())
932 }
933
934 #[test]
935 fn optimizer_detects_plan_equal_to_a_non_initial() -> Result<()> {
936 let opt = Optimizer::with_rules(vec![Arc::new(RotateProjectionRule::new(true))]);
940 let config = OptimizerContext::new().with_max_passes(16);
941
942 let initial_plan = LogicalPlanBuilder::empty(false)
943 .project([lit(1), lit(2), lit(3)])?
944 .project([lit(100)])? .build()?;
946
947 let mut plans: Vec<LogicalPlan> = Vec::new();
948 let final_plan =
949 opt.optimize(initial_plan, &config, |p, _| plans.push(p.clone()))?;
950
951 assert_eq!(4, plans.len());
953
954 assert_eq!(plans[0], final_plan);
956
957 Ok(())
958 }
959
960 fn add_metadata_to_fields(schema: &DFSchema) -> DFSchemaRef {
961 let new_fields = schema
962 .iter()
963 .enumerate()
964 .map(|(i, (qualifier, field))| {
965 let metadata =
966 [("key".into(), format!("value {i}"))].into_iter().collect();
967
968 let new_arrow_field = field.as_ref().clone().with_metadata(metadata);
969 (qualifier.cloned(), Arc::new(new_arrow_field))
970 })
971 .collect::<Vec<_>>();
972
973 let new_metadata = schema.metadata().clone();
974 Arc::new(DFSchema::new_with_metadata(new_fields, new_metadata).unwrap())
975 }
976
977 fn observe(_plan: &LogicalPlan, _rule: &dyn OptimizerRule) {}
978
979 #[derive(Default, Debug)]
980 struct BadRule {}
981
982 impl OptimizerRule for BadRule {
983 fn name(&self) -> &str {
984 "bad rule"
985 }
986
987 fn supports_rewrite(&self) -> bool {
988 true
989 }
990
991 fn rewrite(
992 &self,
993 _plan: LogicalPlan,
994 _config: &dyn OptimizerConfig,
995 ) -> Result<Transformed<LogicalPlan>, DataFusionError> {
996 plan_err!("rule failed")
997 }
998 }
999
1000 #[derive(Default, Debug)]
1002 struct GetTableScanRule {}
1003
1004 impl OptimizerRule for GetTableScanRule {
1005 fn name(&self) -> &str {
1006 "get table_scan rule"
1007 }
1008
1009 fn supports_rewrite(&self) -> bool {
1010 true
1011 }
1012
1013 fn rewrite(
1014 &self,
1015 _plan: LogicalPlan,
1016 _config: &dyn OptimizerConfig,
1017 ) -> Result<Transformed<LogicalPlan>> {
1018 let table_scan = test_table_scan()?;
1019 Ok(Transformed::yes(
1020 LogicalPlanBuilder::from(table_scan).build()?,
1021 ))
1022 }
1023 }
1024
1025 #[derive(Default, Debug)]
1026 struct KeepOnlyAProjectionRule {}
1027
1028 impl OptimizerRule for KeepOnlyAProjectionRule {
1029 fn name(&self) -> &str {
1030 "keep_only_a_projection"
1031 }
1032
1033 fn apply_order(&self) -> Option<ApplyOrder> {
1034 Some(ApplyOrder::TopDown)
1035 }
1036
1037 fn supports_rewrite(&self) -> bool {
1038 true
1039 }
1040
1041 fn rewrite(
1042 &self,
1043 plan: LogicalPlan,
1044 _config: &dyn OptimizerConfig,
1045 ) -> Result<Transformed<LogicalPlan>> {
1046 let projection = match plan {
1047 LogicalPlan::Projection(p) => p,
1048 _ => return Ok(Transformed::no(plan)),
1049 };
1050
1051 let expr = Expr::from(Column::from(projection.schema.qualified_field(0)));
1052
1053 Ok(Transformed::yes(LogicalPlan::Projection(
1054 Projection::try_new(vec![expr], Arc::clone(&projection.input))?,
1055 )))
1056 }
1057 }
1058
1059 #[derive(Default, Debug)]
1063 struct RotateProjectionRule {
1064 reverse_on_first_pass: Mutex<bool>,
1066 }
1067
1068 impl RotateProjectionRule {
1069 fn new(reverse_on_first_pass: bool) -> Self {
1070 Self {
1071 reverse_on_first_pass: Mutex::new(reverse_on_first_pass),
1072 }
1073 }
1074 }
1075
1076 impl OptimizerRule for RotateProjectionRule {
1077 fn name(&self) -> &str {
1078 "rotate_projection"
1079 }
1080
1081 fn apply_order(&self) -> Option<ApplyOrder> {
1082 Some(ApplyOrder::TopDown)
1083 }
1084
1085 fn supports_rewrite(&self) -> bool {
1086 true
1087 }
1088
1089 fn rewrite(
1090 &self,
1091 plan: LogicalPlan,
1092 _config: &dyn OptimizerConfig,
1093 ) -> Result<Transformed<LogicalPlan>> {
1094 let projection = match plan {
1095 LogicalPlan::Projection(p) if p.expr.len() >= 2 => p,
1096 _ => return Ok(Transformed::no(plan)),
1097 };
1098
1099 let mut exprs = projection.expr.clone();
1100
1101 let mut reverse = self.reverse_on_first_pass.lock().unwrap();
1102 if *reverse {
1103 exprs.reverse();
1104 *reverse = false;
1105 } else {
1106 exprs.rotate_left(1);
1107 }
1108
1109 Ok(Transformed::yes(LogicalPlan::Projection(
1110 Projection::try_new(exprs, Arc::clone(&projection.input))?,
1111 )))
1112 }
1113 }
1114}