1use std::collections::{BTreeMap, BTreeSet, HashSet};
16use std::sync::Arc;
17
18use common_function::function::FunctionContext;
19use common_telemetry::debug;
20use datafusion::config::{ConfigExtension, ExtensionOptions};
21use datafusion::datasource::DefaultTableSource;
22use datafusion::error::Result as DfResult;
23use datafusion_common::config::ConfigOptions;
24use datafusion_common::tree_node::{Transformed, TreeNode, TreeNodeRewriter};
25use datafusion_common::{Column, ScalarValue};
26use datafusion_expr::expr::{Exists, InSubquery};
27use datafusion_expr::utils::expr_to_columns;
28use datafusion_expr::{Expr, LogicalPlan, LogicalPlanBuilder, Subquery, col as col_fn};
29use datafusion_optimizer::analyzer::AnalyzerRule;
30use datafusion_optimizer::decorrelate_lateral_join::DecorrelateLateralJoin;
31use datafusion_optimizer::decorrelate_predicate_subquery::DecorrelatePredicateSubquery;
32use datafusion_optimizer::eliminate_filter::EliminateFilter;
33use datafusion_optimizer::extract_equijoin_predicate::ExtractEquijoinPredicate;
34use datafusion_optimizer::filter_null_join_keys::FilterNullJoinKeys;
35use datafusion_optimizer::optimizer::Optimizer;
36use datafusion_optimizer::propagate_empty_relation::PropagateEmptyRelation;
37use datafusion_optimizer::push_down_filter::PushDownFilter;
38use datafusion_optimizer::rewrite_set_comparison::RewriteSetComparison;
39use datafusion_optimizer::scalar_subquery_to_join::ScalarSubqueryToJoin;
40use datafusion_optimizer::simplify_expressions::SimplifyExpressions;
41use promql::extension_plan::SeriesDivide;
42use substrait::{DFLogicalSubstraitConvertor, SubstraitPlan};
43use table::metadata::TableType;
44use table::table::adapter::DfTableProviderAdapter;
45
46use crate::dist_plan::RemoteDynFilterProducerId;
47use crate::dist_plan::analyzer::utils::{
48 PatchOptimizerContext, PlanTreeExpressionSimplifier, aliased_columns_for,
49 rewrite_merge_sort_exprs,
50};
51use crate::dist_plan::commutativity::{
52 Categorizer, Commutativity, partial_commutative_transformer,
53};
54use crate::dist_plan::merge_scan::MergeScanLogicalPlan;
55use crate::dist_plan::merge_sort::MergeSortLogicalPlan;
56use crate::metrics::PUSH_DOWN_FALLBACK_ERRORS_TOTAL;
57use crate::optimizer::expr_depth;
58use crate::optimizer::infer_aggregate_input_bounds::InferAggregateInputBoundsRule;
59use crate::options::ScheduledTimeExtension;
60use crate::plan::ExtractExpr;
61use crate::query_engine::DefaultSerializer;
62
63#[cfg(test)]
64mod test;
65
66mod fallback;
67pub(crate) mod utils;
68
69pub(crate) use utils::AliasMapping;
70
71const OTHER_PHY_PART_COL_PLACEHOLDER: &str = "__OTHER_PHYSICAL_PART_COLS_PLACEHOLDER__";
73
74#[derive(Debug, Clone)]
75pub struct DistPlannerOptions {
76 pub allow_query_fallback: bool,
77}
78
79impl ConfigExtension for DistPlannerOptions {
80 const PREFIX: &'static str = "dist_planner";
81}
82
83impl ExtensionOptions for DistPlannerOptions {
84 fn as_any(&self) -> &dyn std::any::Any {
85 self
86 }
87
88 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
89 self
90 }
91
92 fn cloned(&self) -> Box<dyn ExtensionOptions> {
93 Box::new(self.clone())
94 }
95
96 fn set(&mut self, key: &str, value: &str) -> DfResult<()> {
97 Err(datafusion_common::DataFusionError::NotImplemented(format!(
98 "DistPlannerOptions does not support set key: {key} with value: {value}"
99 )))
100 }
101
102 fn entries(&self) -> Vec<datafusion::config::ConfigEntry> {
103 vec![datafusion::config::ConfigEntry {
104 key: "allow_query_fallback".to_string(),
105 value: Some(self.allow_query_fallback.to_string()),
106 description: "Allow query fallback to fallback plan rewriter",
107 }]
108 }
109}
110
111#[derive(Debug)]
112pub struct DistPlannerAnalyzer;
113
114impl AnalyzerRule for DistPlannerAnalyzer {
115 fn name(&self) -> &str {
116 "DistPlannerAnalyzer"
117 }
118
119 fn analyze(
120 &self,
121 plan: LogicalPlan,
122 config: &ConfigOptions,
123 ) -> datafusion_common::Result<LogicalPlan> {
124 expr_depth::validate_plan(&plan)?;
125 let mut config = config.clone();
126 let explicit = config
128 .extensions
129 .get::<FunctionContext>()
130 .is_some_and(|context| {
131 context
132 .query_ctx
133 .extension("datafusion.optimizer.filter_null_join_keys")
134 .is_some()
135 });
136 if !explicit {
137 config.optimizer.filter_null_join_keys = true;
138 }
139 let config = Arc::new(config);
140 let opt = config.extensions.get::<DistPlannerOptions>();
141 let allow_fallback = opt.map(|o| o.allow_query_fallback).unwrap_or(false);
142
143 let scheduled_time = config
147 .extensions
148 .get::<ScheduledTimeExtension>()
149 .and_then(|ext| ext.scheduled_time);
150
151 let optimizer_context = PatchOptimizerContext {
152 inner: datafusion_optimizer::OptimizerContext::new(),
153 config: config.clone(),
154 scheduled_time,
155 };
156
157 let plan = plan
158 .rewrite_with_subqueries(&mut PlanTreeExpressionSimplifier::new(optimizer_context))?
159 .data;
160 expr_depth::validate_plan(&plan)?;
161 let fallback_plan = plan.clone();
162
163 let optimizer_context = PatchOptimizerContext {
170 inner: datafusion_optimizer::OptimizerContext::new(),
171 config: config.clone(),
172 scheduled_time,
173 };
174 let plan = match pre_merge_scan_optimizer().optimize(plan, &optimizer_context, |_, _| {}) {
175 Ok(plan) => plan,
176 Err(err) => {
177 if allow_fallback {
178 common_telemetry::warn!(err; "Failed to pre-optimize plan, using fallback plan rewriter for plan: {fallback_plan}");
179 PUSH_DOWN_FALLBACK_ERRORS_TOTAL.inc();
180 return self.use_fallback(fallback_plan);
181 } else {
182 return Err(err);
183 }
184 }
185 };
186 let plan = plan
187 .transform_down_with_subqueries(&unwrap_dictionary_literals)?
188 .data;
189
190 let result = match self.try_push_down(plan.clone()) {
191 Ok(plan) => plan,
192 Err(err) => {
193 if allow_fallback {
194 common_telemetry::warn!(err; "Failed to push down plan, using fallback plan rewriter for plan: {plan}");
195 PUSH_DOWN_FALLBACK_ERRORS_TOTAL.inc();
197 self.use_fallback(fallback_plan)?
198 } else {
199 return Err(err);
200 }
201 }
202 };
203
204 Ok(result)
205 }
206}
207
208fn pre_merge_scan_optimizer() -> Optimizer {
232 Optimizer::with_rules(vec![
233 Arc::new(RewriteSetComparison::new()),
234 Arc::new(DecorrelatePredicateSubquery::new()),
235 Arc::new(ScalarSubqueryToJoin::new()),
236 Arc::new(DecorrelateLateralJoin::new()),
237 Arc::new(ExtractEquijoinPredicate::new()),
238 Arc::new(EliminateFilter::new()),
239 Arc::new(PropagateEmptyRelation::new()),
240 Arc::new(FilterNullJoinKeys::default()),
241 Arc::new(InferAggregateInputBoundsRule),
242 Arc::new(PushDownFilter::new()),
243 Arc::new(SimplifyExpressions::new()),
244 ])
245}
246
247fn unwrap_dictionary_literals(plan: LogicalPlan) -> DfResult<Transformed<LogicalPlan>> {
248 if matches!(&plan, LogicalPlan::Values(_)) {
251 return Ok(Transformed::no(plan));
252 }
253
254 plan.map_expressions(|expr| {
255 expr.transform_up(|expr| match expr {
256 Expr::Literal(ScalarValue::Dictionary(_, value), metadata) => {
257 Ok(Transformed::yes(Expr::Literal(*value, metadata)))
258 }
259 _ => Ok(Transformed::no(expr)),
260 })
261 })
262}
263
264impl DistPlannerAnalyzer {
265 fn try_push_down(&self, plan: LogicalPlan) -> DfResult<LogicalPlan> {
267 let plan = plan.transform_with_subqueries(&Self::inspect_plan_with_subquery)?;
272 let mut rewriter = PlanRewriter::new(&plan.data);
273 let result = plan.data.rewrite(&mut rewriter)?.data;
274 Self::assign_merge_scan_remote_dyn_filter_producer_ids(result)
275 }
276
277 fn use_fallback(&self, plan: LogicalPlan) -> DfResult<LogicalPlan> {
279 let mut rewriter = fallback::FallbackPlanRewriter;
280 let result = plan.rewrite(&mut rewriter)?.data;
281 Self::assign_merge_scan_remote_dyn_filter_producer_ids(result)
282 }
283
284 fn inspect_plan_with_subquery(plan: LogicalPlan) -> DfResult<Transformed<LogicalPlan>> {
285 if let LogicalPlan::Limit(_) | LogicalPlan::Distinct(_) = &plan {
288 return Ok(Transformed::no(plan));
289 }
290
291 let exprs = plan
292 .expressions_consider_join()
293 .into_iter()
294 .map(|e| e.transform(&Self::transform_subquery).map(|x| x.data))
295 .collect::<DfResult<Vec<_>>>()?;
296
297 if !matches!(plan, LogicalPlan::Unnest(_)) {
299 let inputs = plan.inputs().into_iter().cloned().collect::<Vec<_>>();
300 Ok(Transformed::yes(plan.with_new_exprs(exprs, inputs)?))
301 } else {
302 Ok(Transformed::no(plan))
303 }
304 }
305
306 fn transform_subquery(expr: Expr) -> DfResult<Transformed<Expr>> {
307 match expr {
308 Expr::Exists(exists) => Ok(Transformed::yes(Expr::Exists(Exists {
309 subquery: Self::handle_subquery(exists.subquery)?,
310 negated: exists.negated,
311 }))),
312 Expr::InSubquery(in_subquery) => Ok(Transformed::yes(Expr::InSubquery(InSubquery {
313 expr: in_subquery.expr,
314 subquery: Self::handle_subquery(in_subquery.subquery)?,
315 negated: in_subquery.negated,
316 }))),
317 Expr::ScalarSubquery(scalar_subquery) => Ok(Transformed::yes(Expr::ScalarSubquery(
318 Self::handle_subquery(scalar_subquery)?,
319 ))),
320
321 _ => Ok(Transformed::no(expr)),
322 }
323 }
324
325 fn handle_subquery(subquery: Subquery) -> DfResult<Subquery> {
326 let mut rewriter = PlanRewriter::new(&subquery.subquery);
327 let mut rewrote_subquery = subquery
328 .subquery
329 .as_ref()
330 .clone()
331 .rewrite(&mut rewriter)?
332 .data;
333 if matches!(rewrote_subquery, LogicalPlan::Extension(_)) {
335 let output_schema = rewrote_subquery.schema().clone();
336 let project_exprs = output_schema
337 .fields()
338 .iter()
339 .map(|f| col_fn(f.name()))
340 .collect::<Vec<_>>();
341 rewrote_subquery = LogicalPlanBuilder::from(rewrote_subquery)
342 .project(project_exprs)?
343 .build()?;
344 }
345
346 Ok(Subquery {
347 subquery: Arc::new(rewrote_subquery),
348 outer_ref_columns: subquery.outer_ref_columns,
349 spans: Default::default(),
350 })
351 }
352
353 fn assign_merge_scan_remote_dyn_filter_producer_ids(
354 plan: LogicalPlan,
355 ) -> DfResult<LogicalPlan> {
356 let mut assigner = MergeScanRemoteDynFilterProducerIdAssigner::default();
357 Ok(plan.rewrite_with_subqueries(&mut assigner)?.data)
358 }
359}
360
361#[derive(Debug, Default)]
362struct RemoteDynFilterProducerIdAllocator {
363 next_remote_dyn_filter_producer_id: u64,
364}
365
366impl RemoteDynFilterProducerIdAllocator {
367 fn allocate(&mut self) -> RemoteDynFilterProducerId {
368 self.next_remote_dyn_filter_producer_id += 1;
369 RemoteDynFilterProducerId::new(self.next_remote_dyn_filter_producer_id)
370 }
371}
372
373#[derive(Debug, Default)]
375struct MergeScanRemoteDynFilterProducerIdAssigner {
376 remote_dyn_filter_producer_id_allocator: RemoteDynFilterProducerIdAllocator,
377}
378
379impl TreeNodeRewriter for MergeScanRemoteDynFilterProducerIdAssigner {
380 type Node = LogicalPlan;
381
382 fn f_up(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
383 let LogicalPlan::Extension(extension) = &node else {
384 return Ok(Transformed::no(node));
385 };
386 let Some(merge_scan) = extension
387 .node
388 .as_any()
389 .downcast_ref::<MergeScanLogicalPlan>()
390 else {
391 return Ok(Transformed::no(node));
392 };
393
394 Ok(Transformed::yes(
395 merge_scan
396 .clone()
397 .with_remote_dyn_filter_producer_id(
398 self.remote_dyn_filter_producer_id_allocator.allocate(),
399 )
400 .into_logical_plan(),
401 ))
402 }
403}
404
405#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
407enum RewriterStatus {
408 #[default]
409 Unexpanded,
410 Expanded,
411}
412
413#[derive(Debug, Default)]
414struct PlanRewriter {
415 whole_plan_encodable: bool,
418 level: usize,
420 stack: Vec<(LogicalPlan, usize)>,
422 stage: Vec<LogicalPlan>,
424 status: RewriterStatus,
425 partition_cols: Option<AliasMapping>,
427 column_requirements: Vec<(HashSet<Column>, usize)>,
453 expand_on_next_call: bool,
458 expand_on_next_part_cond_trans_commutative: bool,
470 new_child_plan: Option<LogicalPlan>,
471}
472
473impl PlanRewriter {
474 fn new(plan: &LogicalPlan) -> Self {
477 Self {
478 whole_plan_encodable: DFLogicalSubstraitConvertor
479 .encode(plan, DefaultSerializer)
480 .is_ok(),
481 ..Default::default()
482 }
483 }
484
485 fn get_parent(&self) -> Option<&LogicalPlan> {
486 self.stack
488 .iter()
489 .rev()
490 .find(|(_, level)| *level == self.level - 1)
491 .map(|(node, _)| node)
492 }
493
494 fn should_expand(&mut self, plan: &LogicalPlan) -> DfResult<bool> {
496 debug!(
497 "Check should_expand at level: {} with Stack:\n{}, ",
498 self.level,
499 self.stack
500 .iter()
501 .map(|(p, l)| format!("{l}:{}{}", " ".repeat(l - 1), p.display()))
502 .collect::<Vec<String>>()
503 .join("\n"),
504 );
505 if !self.whole_plan_encodable
513 && let Err(e) = DFLogicalSubstraitConvertor.encode(plan, DefaultSerializer)
514 {
515 debug!(
516 "PlanRewriter: plan cannot be converted to substrait with error={e:?}, expanding now: {plan}"
517 );
518 return Ok(true);
519 }
520
521 if self.expand_on_next_call {
522 self.expand_on_next_call = false;
523 debug!("PlanRewriter: expand_on_next_call is true, expanding now");
524 return Ok(true);
525 }
526
527 if self.expand_on_next_part_cond_trans_commutative {
528 let comm = Categorizer::check_plan(plan, self.partition_cols.clone())?;
529 match comm {
530 Commutativity::PartialCommutative => {
531 self.expand_on_next_part_cond_trans_commutative = false;
537 self.expand_on_next_call = true;
538 }
539 Commutativity::ConditionalCommutative(_)
540 | Commutativity::TransformedCommutative { .. } => {
541 self.expand_on_next_part_cond_trans_commutative = false;
544 debug!(
545 "PlanRewriter: meet a new conditional/transformed commutative plan, expanding now: {plan}"
546 );
547 return Ok(true);
548 }
549 _ => (),
550 }
551 }
552
553 match Categorizer::check_plan(plan, self.partition_cols.clone())? {
554 Commutativity::Commutative => {
555 if let LogicalPlan::Extension(ext_a) = plan
560 && ext_a.node.name() == SeriesDivide::name()
561 && let Some(LogicalPlan::Extension(ext_b)) = self.stage.last()
562 && ext_b.node.name() == MergeSortLogicalPlan::name()
563 {
564 self.stage.pop();
568 self.expand_on_next_part_cond_trans_commutative = false;
569 self.column_requirements.clear();
570 }
571 }
572 Commutativity::PartialCommutative => {
573 if let Some(plan) = partial_commutative_transformer(plan) {
574 self.update_column_requirements(&plan, self.level - 1);
576 self.expand_on_next_part_cond_trans_commutative = true;
577 self.stage.push(plan)
578 }
579 }
580 Commutativity::ConditionalCommutative(transformer) => {
581 if let Some(transformer) = transformer
582 && let Some(plan) = transformer(plan)
583 {
584 self.update_column_requirements(&plan, self.level - 1);
586 self.expand_on_next_part_cond_trans_commutative = true;
587 self.stage.push(plan)
588 }
589 }
590 Commutativity::TransformedCommutative { transformer } => {
591 if let Some(transformer) = transformer {
592 let transformer_actions = transformer(plan)?;
593 debug!(
594 "PlanRewriter: transformed plan: {}\n from {plan}",
595 transformer_actions
596 .extra_parent_plans
597 .iter()
598 .enumerate()
599 .map(|(i, p)| format!(
600 "Extra {i}-th parent plan from parent to child = {}",
601 p.display()
602 ))
603 .collect::<Vec<_>>()
604 .join("\n")
605 );
606 if let Some(new_child_plan) = &transformer_actions.new_child_plan {
607 debug!("PlanRewriter: new child plan: {}", new_child_plan);
608 }
609 if let Some(last_stage) = transformer_actions.extra_parent_plans.last() {
610 self.update_column_requirements(last_stage, self.level - 1);
613 }
614 self.stage
615 .extend(transformer_actions.extra_parent_plans.into_iter().rev());
616 self.expand_on_next_call = true;
617 self.new_child_plan = transformer_actions.new_child_plan;
618 }
619 }
620 Commutativity::NonCommutative
621 | Commutativity::Unimplemented
622 | Commutativity::Unsupported => {
623 debug!("PlanRewriter: meet a non-commutative plan, expanding now: {plan}");
624 return Ok(true);
625 }
626 }
627
628 Ok(false)
629 }
630
631 fn update_column_requirements(&mut self, plan: &LogicalPlan, plan_level: usize) {
635 debug!(
636 "PlanRewriter: update column requirements for plan: {plan}\n with old column_requirements: {:?}",
637 self.column_requirements
638 );
639 let mut container = HashSet::new();
640 for expr in plan.expressions() {
641 let _ = expr_to_columns(&expr, &mut container);
643 }
644
645 self.column_requirements.push((container, plan_level));
646 debug!(
647 "PlanRewriter: updated column requirements: {:?}",
648 self.column_requirements
649 );
650 }
651
652 fn is_expanded(&self) -> bool {
653 self.status == RewriterStatus::Expanded
654 }
655
656 fn set_expanded(&mut self) {
657 self.status = RewriterStatus::Expanded;
658 }
659
660 fn set_unexpanded(&mut self) {
661 self.status = RewriterStatus::Unexpanded;
662 }
663
664 fn maybe_set_partitions(&mut self, plan: &LogicalPlan) -> DfResult<()> {
665 if let Some(part_cols) = &mut self.partition_cols {
666 let child = plan.inputs().first().cloned().ok_or_else(|| {
668 datafusion_common::DataFusionError::Internal(format!(
669 "PlanRewriter: maybe_set_partitions: plan has no child: {plan}"
670 ))
671 })?;
672
673 for alias_set in part_cols.values_mut() {
674 let aliased_cols = aliased_columns_for(
675 &alias_set.clone().into_iter().collect(),
676 plan,
677 Some(child),
678 )?;
679 *alias_set = aliased_cols.into_values().flatten().collect();
680 }
681
682 debug!(
683 "PlanRewriter: maybe_set_partitions: updated partition columns: {:?} at plan: {}",
684 part_cols,
685 plan.display()
686 );
687
688 return Ok(());
689 }
690
691 if let LogicalPlan::TableScan(table_scan) = plan
692 && let Some(source) = table_scan.source.downcast_ref::<DefaultTableSource>()
693 && let Some(provider) = source
694 .table_provider
695 .downcast_ref::<DfTableProviderAdapter>()
696 {
697 let table = provider.table();
698 if table.table_type() == TableType::Base {
699 let info = table.table_info();
700 let partition_key_indices = info.meta.partition_key_indices.clone();
701 let schema = info.meta.schema.clone();
702 let mut partition_cols = partition_key_indices
703 .iter()
704 .map(|index| schema.column_name_by_index(*index).to_string())
705 .collect::<Vec<String>>();
706 debug!(
707 "PlanRewriter: loaded table partition metadata, table: {}, table_id: {}, partition_key_indices: {:?}, partition_columns: {:?}",
708 info.name, info.ident.table_id, info.meta.partition_key_indices, partition_cols,
709 );
710
711 let partition_rules = table.partition_rules();
712 let exist_phy_part_cols_not_in_logical_table = partition_rules
713 .map(|r| !r.extra_phy_cols_not_in_logical_table.is_empty())
714 .unwrap_or(false);
715
716 if exist_phy_part_cols_not_in_logical_table && partition_cols.is_empty() {
717 partition_cols.push(OTHER_PHY_PART_COL_PLACEHOLDER.to_string());
725 }
726 self.partition_cols = Some(
727 partition_cols
728 .into_iter()
729 .map(|c| {
730 if c == OTHER_PHY_PART_COL_PLACEHOLDER {
731 return Ok((c.clone(), BTreeSet::new()));
733 }
734 let index =
735 if let Some(c) = plan.schema().index_of_column_by_name(None, &c){
736 c
737 } else {
738 return Ok((c.clone(), BTreeSet::new()))
741 };
742 let column = plan.schema().columns().get(index).cloned().ok_or_else(|| {
743 datafusion_common::DataFusionError::Internal(format!(
744 "PlanRewriter: maybe_set_partitions: column index {index} out of bounds in schema of plan: {plan}"
745 ))
746 })?;
747 Ok((c.clone(), BTreeSet::from([column])))
748 })
749 .collect::<DfResult<AliasMapping>>()?,
750 );
751 }
752 }
753
754 Ok(())
755 }
756
757 fn pop_stack(&mut self) {
759 self.level -= 1;
760 self.stack.pop();
761 }
762
763 fn expand(&mut self, mut on_node: LogicalPlan) -> DfResult<LogicalPlan> {
764 let schema = on_node.schema().clone();
766 if let Some(new_child_plan) = self.new_child_plan.take() {
767 on_node = new_child_plan;
769 }
770 let mut rewriter = EnforceDistRequirementRewriter::new(
771 std::mem::take(&mut self.column_requirements),
772 self.level,
773 );
774 debug!(
775 "PlanRewriter: enforce column requirements for node: {on_node} with rewriter: {rewriter:?}"
776 );
777 on_node = on_node.rewrite(&mut rewriter)?.data;
778 debug!(
779 "PlanRewriter: after enforced column requirements with rewriter: {rewriter:?} for node:\n{on_node}"
780 );
781
782 debug!(
783 "PlanRewriter: expand on node: {on_node} with partition col alias mapping: {:?}",
784 self.partition_cols
785 );
786
787 let mut node = MergeScanLogicalPlan::new(on_node.clone(), false).into_logical_plan();
789
790 for new_stage in self.stage.drain(..) {
792 let new_stage = if let LogicalPlan::Extension(ext) = &new_stage
794 && let Some(merge_sort) = ext.node.as_any().downcast_ref::<MergeSortLogicalPlan>()
795 {
796 rewrite_merge_sort_exprs(merge_sort, &on_node)?
798 } else {
799 new_stage
800 };
801 node = new_stage
802 .with_new_exprs(new_stage.expressions_consider_join(), vec![node.clone()])?;
803 }
804 self.set_expanded();
805
806 let node = LogicalPlanBuilder::from(node)
809 .project(schema.iter().map(|(qualifier, field)| {
810 Expr::Column(Column::new(qualifier.cloned(), field.name()))
811 }))?
812 .build()?;
813
814 Ok(node)
815 }
816}
817
818#[derive(Debug)]
826struct EnforceDistRequirementRewriter {
827 column_requirements: Vec<(HashSet<Column>, usize)>,
831 cur_level: usize,
842 plan_per_level: BTreeMap<usize, LogicalPlan>,
843}
844
845impl EnforceDistRequirementRewriter {
846 fn new(column_requirements: Vec<(HashSet<Column>, usize)>, cur_level: usize) -> Self {
847 debug!(
848 "Create EnforceDistRequirementRewriter with column_requirements: {:?} at cur_level: {}",
849 column_requirements, cur_level
850 );
851 Self {
852 column_requirements,
853 cur_level,
854 plan_per_level: BTreeMap::new(),
855 }
856 }
857
858 fn get_current_applicable_column_requirements(
862 &self,
863 node: &LogicalPlan,
864 ) -> DfResult<BTreeMap<(Column, usize), BTreeSet<Column>>> {
865 let col_req_per_level = self
866 .column_requirements
867 .iter()
868 .filter(|(_, level)| *level >= self.cur_level)
869 .collect::<Vec<_>>();
870
871 let mut result_alias_mapping = BTreeMap::new();
874 let Some(child) = node.inputs().first().cloned() else {
875 return Ok(Default::default());
876 };
877 for (col_req, level) in col_req_per_level {
878 if let Some(original) = self.plan_per_level.get(level) {
879 let aliased_cols =
881 aliased_columns_for(&col_req.iter().cloned().collect(), node, Some(original))?;
882 for original_col in col_req {
883 let aliased_cols = aliased_cols.get(original_col).cloned();
884 if let Some(cols) = aliased_cols
885 && !cols.is_empty()
886 {
887 result_alias_mapping.insert((original_col.clone(), *level), cols);
888 } else {
889 let aliases_in_child = aliased_columns_for(
894 &[original_col.clone()].into(),
895 child,
896 Some(original),
897 )?;
898 let Some(aliases) = aliases_in_child
899 .get(original_col)
900 .cloned()
901 .filter(|a| !a.is_empty())
902 else {
903 return Err(datafusion_common::DataFusionError::Internal(format!(
904 "EnforceDistRequirementRewriter: no alias found for required column {original_col} at level {level} in current node's child plan: \n{child} from original plan: \n{original}",
905 )));
906 };
907
908 result_alias_mapping.insert((original_col.clone(), *level), aliases);
909 }
910 }
911 }
912 }
913 Ok(result_alias_mapping)
914 }
915}
916
917impl TreeNodeRewriter for EnforceDistRequirementRewriter {
918 type Node = LogicalPlan;
919
920 fn f_down(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
921 if node.inputs().len() > 1 {
923 return Err(datafusion_common::DataFusionError::Internal(
924 "EnforceDistRequirementRewriter: node with multiple inputs is not supported"
925 .to_string(),
926 ));
927 }
928 self.plan_per_level.insert(self.cur_level, node.clone());
929 self.cur_level += 1;
930 Ok(Transformed::no(node))
931 }
932
933 fn f_up(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
934 self.cur_level -= 1;
935 if let LogicalPlan::Projection(ref projection) = node {
939 let mut applicable_column_requirements =
940 self.get_current_applicable_column_requirements(&node)?;
941
942 debug!(
943 "EnforceDistRequirementRewriter: applicable column requirements at level {} = {:?} for node {}",
944 self.cur_level,
945 applicable_column_requirements,
946 node.display()
947 );
948
949 for expr in &projection.expr {
950 let (qualifier, name) = expr.qualified_name();
951 let column = Column::new(qualifier, name);
952 applicable_column_requirements.retain(|_col_level, alias_set| {
953 !alias_set.contains(&column)
955 });
956 }
957 if applicable_column_requirements.is_empty() {
958 return Ok(Transformed::no(node));
959 }
960
961 let mut new_exprs = projection.expr.clone();
962 for (col, alias_set) in &applicable_column_requirements {
963 new_exprs.push(Expr::Column(alias_set.first().cloned().ok_or_else(
965 || {
966 datafusion_common::DataFusionError::Internal(
967 format!("EnforceDistRequirementRewriter: alias set is empty, for column {col:?} in node {node}"),
968 )
969 },
970 )?));
971 }
972 let new_node =
973 node.with_new_exprs(new_exprs, node.inputs().into_iter().cloned().collect())?;
974 debug!(
975 "EnforceDistRequirementRewriter: added missing columns {:?} to projection node from old node: \n{node}\n Making new node: \n{new_node}",
976 applicable_column_requirements
977 );
978
979 self.plan_per_level.insert(self.cur_level, new_node.clone());
981
982 return Ok(Transformed::yes(new_node));
984 }
985 Ok(Transformed::no(node))
986 }
987}
988
989impl TreeNodeRewriter for PlanRewriter {
990 type Node = LogicalPlan;
991
992 fn f_down<'a>(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
994 self.level += 1;
995 self.stack.push((node.clone(), self.level));
996 self.stage.clear();
998 self.set_unexpanded();
999 self.partition_cols = None;
1000 Ok(Transformed::no(node))
1001 }
1002
1003 fn f_up(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
1007 if self.is_expanded() {
1009 self.pop_stack();
1010 return Ok(Transformed::no(node));
1011 }
1012
1013 if node.inputs().is_empty() && !matches!(node, LogicalPlan::TableScan(_)) {
1015 self.set_expanded();
1016 self.pop_stack();
1017 return Ok(Transformed::no(node));
1018 }
1019
1020 self.maybe_set_partitions(&node)?;
1021
1022 let Some(parent) = self.get_parent() else {
1023 debug!("Plan Rewriter: expand now for no parent found for node: {node}");
1024 let node = self.expand(node);
1025 debug!(
1026 "PlanRewriter: expanded plan: {}",
1027 match &node {
1028 Ok(n) => n.to_string(),
1029 Err(e) => format!("Error expanding plan: {e}"),
1030 }
1031 );
1032 let node = node?;
1033 self.pop_stack();
1034 return Ok(Transformed::yes(node));
1035 };
1036
1037 let parent = parent.clone();
1038
1039 if self.should_expand(&parent)? {
1040 debug!(
1042 "PlanRewriter: should expand child:\n {node}\n Of Parent: {}",
1043 parent.display()
1044 );
1045 let node = self.expand(node);
1046 debug!(
1047 "PlanRewriter: expanded plan: {}",
1048 match &node {
1049 Ok(n) => n.to_string(),
1050 Err(e) => format!("Error expanding plan: {e}"),
1051 }
1052 );
1053 let node = node?;
1054 self.pop_stack();
1055 return Ok(Transformed::yes(node));
1056 }
1057
1058 self.pop_stack();
1059 Ok(Transformed::no(node))
1060 }
1061}