datafusion_physical_optimizer/
utils.rs1use std::sync::Arc;
19
20use datafusion_common::Result;
21use datafusion_physical_expr::{Distribution, LexOrdering, LexRequirement};
22use datafusion_physical_plan::coalesce_partitions::CoalescePartitionsExec;
23use datafusion_physical_plan::limit::{GlobalLimitExec, LocalLimitExec};
24use datafusion_physical_plan::repartition::RepartitionExec;
25use datafusion_physical_plan::sorts::sort::SortExec;
26use datafusion_physical_plan::sorts::sort_preserving_merge::SortPreservingMergeExec;
27use datafusion_physical_plan::tree_node::PlanContext;
28use datafusion_physical_plan::union::UnionExec;
29use datafusion_physical_plan::windows::{BoundedWindowAggExec, WindowAggExec};
30use datafusion_physical_plan::{ExecutionPlan, ExecutionPlanProperties};
31
32pub fn add_sort_above<T: Clone + Default>(
39 node: PlanContext<T>,
40 sort_requirements: LexRequirement,
41 fetch: Option<usize>,
42) -> PlanContext<T> {
43 let mut sort_reqs: Vec<_> = sort_requirements.into();
44 sort_reqs.retain(|sort_expr| {
45 node.plan
46 .equivalence_properties()
47 .is_expr_constant(&sort_expr.expr)
48 .is_none()
49 });
50 let sort_exprs = sort_reqs.into_iter().map(Into::into).collect::<Vec<_>>();
51 let Some(ordering) = LexOrdering::new(sort_exprs) else {
52 return node;
53 };
54 let mut new_sort = SortExec::new(ordering, Arc::clone(&node.plan)).with_fetch(fetch);
55 if node.plan.output_partitioning().partition_count() > 1 {
56 new_sort = new_sort.with_preserve_partitioning(true);
57 }
58 PlanContext::new(Arc::new(new_sort), T::default(), vec![node])
59}
60
61pub fn add_sort_above_with_distribution<T: Clone + Default>(
66 node: PlanContext<T>,
67 sort_requirements: LexRequirement,
68 fetch: Option<usize>,
69 required_distribution: &Distribution,
70) -> PlanContext<T> {
71 let mut sort_reqs: Vec<_> = sort_requirements.into();
72 sort_reqs.retain(|sort_expr| {
73 node.plan
74 .equivalence_properties()
75 .is_expr_constant(&sort_expr.expr)
76 .is_none()
77 });
78 let sort_exprs = sort_reqs.into_iter().map(Into::into).collect::<Vec<_>>();
79 let Some(ordering) = LexOrdering::new(sort_exprs) else {
80 return node;
81 };
82 let input_has_multiple_partitions =
83 node.plan.output_partitioning().partition_count() > 1;
84
85 let mut new_sort =
86 SortExec::new(ordering.clone(), Arc::clone(&node.plan)).with_fetch(fetch);
87 if input_has_multiple_partitions {
88 new_sort = new_sort.with_preserve_partitioning(true);
89 }
90
91 let sort_node = PlanContext::new(Arc::new(new_sort), T::default(), vec![node]);
92
93 if matches!(required_distribution, Distribution::SinglePartition)
96 && input_has_multiple_partitions
97 {
98 PlanContext::new(
99 Arc::new(
100 SortPreservingMergeExec::new(ordering, Arc::clone(&sort_node.plan))
101 .with_fetch(fetch),
102 ),
103 T::default(),
104 vec![sort_node],
105 )
106 } else {
107 sort_node
108 }
109}
110
111pub fn add_sort_above_with_check<T: Clone + Default>(
115 node: PlanContext<T>,
116 sort_requirements: LexRequirement,
117 fetch: Option<usize>,
118) -> Result<PlanContext<T>> {
119 if !node
120 .plan
121 .equivalence_properties()
122 .ordering_satisfy_requirement(sort_requirements.clone())?
123 {
124 Ok(add_sort_above(node, sort_requirements, fetch))
125 } else {
126 Ok(node)
127 }
128}
129
130pub fn is_sort(plan: &Arc<dyn ExecutionPlan>) -> bool {
132 plan.is::<SortExec>()
133}
134
135pub fn is_window(plan: &Arc<dyn ExecutionPlan>) -> bool {
138 plan.is::<WindowAggExec>() || plan.is::<BoundedWindowAggExec>()
139}
140
141pub fn is_union(plan: &Arc<dyn ExecutionPlan>) -> bool {
143 plan.is::<UnionExec>()
144}
145
146pub fn is_sort_preserving_merge(plan: &Arc<dyn ExecutionPlan>) -> bool {
148 plan.is::<SortPreservingMergeExec>()
149}
150
151pub fn is_coalesce_partitions(plan: &Arc<dyn ExecutionPlan>) -> bool {
153 plan.is::<CoalescePartitionsExec>()
154}
155
156pub fn is_repartition(plan: &Arc<dyn ExecutionPlan>) -> bool {
158 plan.is::<RepartitionExec>()
159}
160
161pub fn is_limit(plan: &Arc<dyn ExecutionPlan>) -> bool {
164 plan.is::<GlobalLimitExec>() || plan.is::<LocalLimitExec>()
165}