1use std::collections::{BTreeMap, BTreeSet, HashSet};
16use std::sync::Arc;
17
18use common_telemetry::debug;
19use datafusion::config::{ConfigExtension, ExtensionOptions};
20use datafusion::datasource::DefaultTableSource;
21use datafusion::error::Result as DfResult;
22use datafusion_common::config::ConfigOptions;
23use datafusion_common::tree_node::{Transformed, TreeNode, TreeNodeRewriter};
24use datafusion_common::{Column, ScalarValue};
25use datafusion_expr::expr::{Exists, InSubquery};
26use datafusion_expr::utils::expr_to_columns;
27use datafusion_expr::{Expr, LogicalPlan, LogicalPlanBuilder, Subquery, col as col_fn};
28use datafusion_optimizer::analyzer::AnalyzerRule;
29use datafusion_optimizer::decorrelate_lateral_join::DecorrelateLateralJoin;
30use datafusion_optimizer::decorrelate_predicate_subquery::DecorrelatePredicateSubquery;
31use datafusion_optimizer::eliminate_filter::EliminateFilter;
32use datafusion_optimizer::extract_equijoin_predicate::ExtractEquijoinPredicate;
33use datafusion_optimizer::filter_null_join_keys::FilterNullJoinKeys;
34use datafusion_optimizer::optimizer::Optimizer;
35use datafusion_optimizer::propagate_empty_relation::PropagateEmptyRelation;
36use datafusion_optimizer::push_down_filter::PushDownFilter;
37use datafusion_optimizer::rewrite_set_comparison::RewriteSetComparison;
38use datafusion_optimizer::scalar_subquery_to_join::ScalarSubqueryToJoin;
39use datafusion_optimizer::simplify_expressions::SimplifyExpressions;
40use promql::extension_plan::SeriesDivide;
41use substrait::{DFLogicalSubstraitConvertor, SubstraitPlan};
42use table::metadata::TableType;
43use table::table::adapter::DfTableProviderAdapter;
44
45use crate::dist_plan::RemoteDynFilterProducerId;
46use crate::dist_plan::analyzer::utils::{
47 PatchOptimizerContext, PlanTreeExpressionSimplifier, aliased_columns_for,
48 rewrite_merge_sort_exprs,
49};
50use crate::dist_plan::commutativity::{
51 Categorizer, Commutativity, partial_commutative_transformer,
52};
53use crate::dist_plan::merge_scan::MergeScanLogicalPlan;
54use crate::dist_plan::merge_sort::MergeSortLogicalPlan;
55use crate::metrics::PUSH_DOWN_FALLBACK_ERRORS_TOTAL;
56use crate::options::ScheduledTimeExtension;
57use crate::plan::ExtractExpr;
58use crate::query_engine::DefaultSerializer;
59
60#[cfg(test)]
61mod test;
62
63mod fallback;
64pub(crate) mod utils;
65
66pub(crate) use utils::AliasMapping;
67
68const OTHER_PHY_PART_COL_PLACEHOLDER: &str = "__OTHER_PHYSICAL_PART_COLS_PLACEHOLDER__";
70
71#[derive(Debug, Clone)]
72pub struct DistPlannerOptions {
73 pub allow_query_fallback: bool,
74}
75
76impl ConfigExtension for DistPlannerOptions {
77 const PREFIX: &'static str = "dist_planner";
78}
79
80impl ExtensionOptions for DistPlannerOptions {
81 fn as_any(&self) -> &dyn std::any::Any {
82 self
83 }
84
85 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
86 self
87 }
88
89 fn cloned(&self) -> Box<dyn ExtensionOptions> {
90 Box::new(self.clone())
91 }
92
93 fn set(&mut self, key: &str, value: &str) -> DfResult<()> {
94 Err(datafusion_common::DataFusionError::NotImplemented(format!(
95 "DistPlannerOptions does not support set key: {key} with value: {value}"
96 )))
97 }
98
99 fn entries(&self) -> Vec<datafusion::config::ConfigEntry> {
100 vec![datafusion::config::ConfigEntry {
101 key: "allow_query_fallback".to_string(),
102 value: Some(self.allow_query_fallback.to_string()),
103 description: "Allow query fallback to fallback plan rewriter",
104 }]
105 }
106}
107
108#[derive(Debug)]
109pub struct DistPlannerAnalyzer;
110
111impl AnalyzerRule for DistPlannerAnalyzer {
112 fn name(&self) -> &str {
113 "DistPlannerAnalyzer"
114 }
115
116 fn analyze(
117 &self,
118 plan: LogicalPlan,
119 config: &ConfigOptions,
120 ) -> datafusion_common::Result<LogicalPlan> {
121 let mut config = config.clone();
122 config.optimizer.filter_null_join_keys = true;
124 let config = Arc::new(config);
125 let opt = config.extensions.get::<DistPlannerOptions>();
126 let allow_fallback = opt.map(|o| o.allow_query_fallback).unwrap_or(false);
127
128 let scheduled_time = config
132 .extensions
133 .get::<ScheduledTimeExtension>()
134 .and_then(|ext| ext.scheduled_time);
135
136 let optimizer_context = PatchOptimizerContext {
137 inner: datafusion_optimizer::OptimizerContext::new(),
138 config: config.clone(),
139 scheduled_time,
140 };
141
142 let plan = plan
143 .rewrite_with_subqueries(&mut PlanTreeExpressionSimplifier::new(optimizer_context))?
144 .data;
145 let fallback_plan = plan.clone();
146
147 let optimizer_context = PatchOptimizerContext {
154 inner: datafusion_optimizer::OptimizerContext::new(),
155 config: config.clone(),
156 scheduled_time,
157 };
158 let plan = match pre_merge_scan_optimizer().optimize(plan, &optimizer_context, |_, _| {}) {
159 Ok(plan) => plan,
160 Err(err) => {
161 if allow_fallback {
162 common_telemetry::warn!(err; "Failed to pre-optimize plan, using fallback plan rewriter for plan: {fallback_plan}");
163 PUSH_DOWN_FALLBACK_ERRORS_TOTAL.inc();
164 return self.use_fallback(fallback_plan);
165 } else {
166 return Err(err);
167 }
168 }
169 };
170 let plan = plan
171 .transform_down_with_subqueries(&unwrap_dictionary_literals)?
172 .data;
173
174 let result = match self.try_push_down(plan.clone()) {
175 Ok(plan) => plan,
176 Err(err) => {
177 if allow_fallback {
178 common_telemetry::warn!(err; "Failed to push down plan, using fallback plan rewriter for plan: {plan}");
179 PUSH_DOWN_FALLBACK_ERRORS_TOTAL.inc();
181 self.use_fallback(fallback_plan)?
182 } else {
183 return Err(err);
184 }
185 }
186 };
187
188 Ok(result)
189 }
190}
191
192fn pre_merge_scan_optimizer() -> Optimizer {
216 Optimizer::with_rules(vec![
217 Arc::new(RewriteSetComparison::new()),
218 Arc::new(DecorrelatePredicateSubquery::new()),
219 Arc::new(ScalarSubqueryToJoin::new()),
220 Arc::new(DecorrelateLateralJoin::new()),
221 Arc::new(ExtractEquijoinPredicate::new()),
222 Arc::new(EliminateFilter::new()),
223 Arc::new(PropagateEmptyRelation::new()),
224 Arc::new(FilterNullJoinKeys::default()),
225 Arc::new(PushDownFilter::new()),
226 Arc::new(SimplifyExpressions::new()),
227 ])
228}
229
230fn unwrap_dictionary_literals(plan: LogicalPlan) -> DfResult<Transformed<LogicalPlan>> {
231 if matches!(&plan, LogicalPlan::Values(_)) {
234 return Ok(Transformed::no(plan));
235 }
236
237 plan.map_expressions(|expr| {
238 expr.transform_up(|expr| match expr {
239 Expr::Literal(ScalarValue::Dictionary(_, value), metadata) => {
240 Ok(Transformed::yes(Expr::Literal(*value, metadata)))
241 }
242 _ => Ok(Transformed::no(expr)),
243 })
244 })
245}
246
247impl DistPlannerAnalyzer {
248 fn try_push_down(&self, plan: LogicalPlan) -> DfResult<LogicalPlan> {
250 let plan = plan.transform(&Self::inspect_plan_with_subquery)?;
251 let mut rewriter = PlanRewriter::default();
252 let result = plan.data.rewrite(&mut rewriter)?.data;
253 Self::assign_merge_scan_remote_dyn_filter_producer_ids(result)
254 }
255
256 fn use_fallback(&self, plan: LogicalPlan) -> DfResult<LogicalPlan> {
258 let mut rewriter = fallback::FallbackPlanRewriter;
259 let result = plan.rewrite(&mut rewriter)?.data;
260 Self::assign_merge_scan_remote_dyn_filter_producer_ids(result)
261 }
262
263 fn inspect_plan_with_subquery(plan: LogicalPlan) -> DfResult<Transformed<LogicalPlan>> {
264 if let LogicalPlan::Limit(_) | LogicalPlan::Distinct(_) = &plan {
267 return Ok(Transformed::no(plan));
268 }
269
270 let exprs = plan
271 .expressions_consider_join()
272 .into_iter()
273 .map(|e| e.transform(&Self::transform_subquery).map(|x| x.data))
274 .collect::<DfResult<Vec<_>>>()?;
275
276 if !matches!(plan, LogicalPlan::Unnest(_)) {
278 let inputs = plan.inputs().into_iter().cloned().collect::<Vec<_>>();
279 Ok(Transformed::yes(plan.with_new_exprs(exprs, inputs)?))
280 } else {
281 Ok(Transformed::no(plan))
282 }
283 }
284
285 fn transform_subquery(expr: Expr) -> DfResult<Transformed<Expr>> {
286 match expr {
287 Expr::Exists(exists) => Ok(Transformed::yes(Expr::Exists(Exists {
288 subquery: Self::handle_subquery(exists.subquery)?,
289 negated: exists.negated,
290 }))),
291 Expr::InSubquery(in_subquery) => Ok(Transformed::yes(Expr::InSubquery(InSubquery {
292 expr: in_subquery.expr,
293 subquery: Self::handle_subquery(in_subquery.subquery)?,
294 negated: in_subquery.negated,
295 }))),
296 Expr::ScalarSubquery(scalar_subquery) => Ok(Transformed::yes(Expr::ScalarSubquery(
297 Self::handle_subquery(scalar_subquery)?,
298 ))),
299
300 _ => Ok(Transformed::no(expr)),
301 }
302 }
303
304 fn handle_subquery(subquery: Subquery) -> DfResult<Subquery> {
305 let mut rewriter = PlanRewriter::default();
306 let mut rewrote_subquery = subquery
307 .subquery
308 .as_ref()
309 .clone()
310 .rewrite(&mut rewriter)?
311 .data;
312 if matches!(rewrote_subquery, LogicalPlan::Extension(_)) {
314 let output_schema = rewrote_subquery.schema().clone();
315 let project_exprs = output_schema
316 .fields()
317 .iter()
318 .map(|f| col_fn(f.name()))
319 .collect::<Vec<_>>();
320 rewrote_subquery = LogicalPlanBuilder::from(rewrote_subquery)
321 .project(project_exprs)?
322 .build()?;
323 }
324
325 Ok(Subquery {
326 subquery: Arc::new(rewrote_subquery),
327 outer_ref_columns: subquery.outer_ref_columns,
328 spans: Default::default(),
329 })
330 }
331
332 fn assign_merge_scan_remote_dyn_filter_producer_ids(
333 plan: LogicalPlan,
334 ) -> DfResult<LogicalPlan> {
335 let mut assigner = MergeScanRemoteDynFilterProducerIdAssigner::default();
336 Ok(plan.rewrite_with_subqueries(&mut assigner)?.data)
337 }
338}
339
340#[derive(Debug, Default)]
341struct RemoteDynFilterProducerIdAllocator {
342 next_remote_dyn_filter_producer_id: u64,
343}
344
345impl RemoteDynFilterProducerIdAllocator {
346 fn allocate(&mut self) -> RemoteDynFilterProducerId {
347 self.next_remote_dyn_filter_producer_id += 1;
348 RemoteDynFilterProducerId::new(self.next_remote_dyn_filter_producer_id)
349 }
350}
351
352#[derive(Debug, Default)]
354struct MergeScanRemoteDynFilterProducerIdAssigner {
355 remote_dyn_filter_producer_id_allocator: RemoteDynFilterProducerIdAllocator,
356}
357
358impl TreeNodeRewriter for MergeScanRemoteDynFilterProducerIdAssigner {
359 type Node = LogicalPlan;
360
361 fn f_up(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
362 let LogicalPlan::Extension(extension) = &node else {
363 return Ok(Transformed::no(node));
364 };
365 let Some(merge_scan) = extension
366 .node
367 .as_any()
368 .downcast_ref::<MergeScanLogicalPlan>()
369 else {
370 return Ok(Transformed::no(node));
371 };
372
373 Ok(Transformed::yes(
374 merge_scan
375 .clone()
376 .with_remote_dyn_filter_producer_id(
377 self.remote_dyn_filter_producer_id_allocator.allocate(),
378 )
379 .into_logical_plan(),
380 ))
381 }
382}
383
384#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
386enum RewriterStatus {
387 #[default]
388 Unexpanded,
389 Expanded,
390}
391
392#[derive(Debug, Default)]
393struct PlanRewriter {
394 level: usize,
396 stack: Vec<(LogicalPlan, usize)>,
398 stage: Vec<LogicalPlan>,
400 status: RewriterStatus,
401 partition_cols: Option<AliasMapping>,
403 column_requirements: Vec<(HashSet<Column>, usize)>,
429 expand_on_next_call: bool,
434 expand_on_next_part_cond_trans_commutative: bool,
446 new_child_plan: Option<LogicalPlan>,
447}
448
449impl PlanRewriter {
450 fn get_parent(&self) -> Option<&LogicalPlan> {
451 self.stack
453 .iter()
454 .rev()
455 .find(|(_, level)| *level == self.level - 1)
456 .map(|(node, _)| node)
457 }
458
459 fn should_expand(&mut self, plan: &LogicalPlan) -> DfResult<bool> {
461 debug!(
462 "Check should_expand at level: {} with Stack:\n{}, ",
463 self.level,
464 self.stack
465 .iter()
466 .map(|(p, l)| format!("{l}:{}{}", " ".repeat(l - 1), p.display()))
467 .collect::<Vec<String>>()
468 .join("\n"),
469 );
470 if let Err(e) = DFLogicalSubstraitConvertor.encode(plan, DefaultSerializer) {
471 debug!(
472 "PlanRewriter: plan cannot be converted to substrait with error={e:?}, expanding now: {plan}"
473 );
474 return Ok(true);
475 }
476
477 if self.expand_on_next_call {
478 self.expand_on_next_call = false;
479 debug!("PlanRewriter: expand_on_next_call is true, expanding now");
480 return Ok(true);
481 }
482
483 if self.expand_on_next_part_cond_trans_commutative {
484 let comm = Categorizer::check_plan(plan, self.partition_cols.clone())?;
485 match comm {
486 Commutativity::PartialCommutative => {
487 self.expand_on_next_part_cond_trans_commutative = false;
493 self.expand_on_next_call = true;
494 }
495 Commutativity::ConditionalCommutative(_)
496 | Commutativity::TransformedCommutative { .. } => {
497 self.expand_on_next_part_cond_trans_commutative = false;
500 debug!(
501 "PlanRewriter: meet a new conditional/transformed commutative plan, expanding now: {plan}"
502 );
503 return Ok(true);
504 }
505 _ => (),
506 }
507 }
508
509 match Categorizer::check_plan(plan, self.partition_cols.clone())? {
510 Commutativity::Commutative => {
511 if let LogicalPlan::Extension(ext_a) = plan
516 && ext_a.node.name() == SeriesDivide::name()
517 && let Some(LogicalPlan::Extension(ext_b)) = self.stage.last()
518 && ext_b.node.name() == MergeSortLogicalPlan::name()
519 {
520 self.stage.pop();
524 self.expand_on_next_part_cond_trans_commutative = false;
525 self.column_requirements.clear();
526 }
527 }
528 Commutativity::PartialCommutative => {
529 if let Some(plan) = partial_commutative_transformer(plan) {
530 self.update_column_requirements(&plan, self.level - 1);
532 self.expand_on_next_part_cond_trans_commutative = true;
533 self.stage.push(plan)
534 }
535 }
536 Commutativity::ConditionalCommutative(transformer) => {
537 if let Some(transformer) = transformer
538 && let Some(plan) = transformer(plan)
539 {
540 self.update_column_requirements(&plan, self.level - 1);
542 self.expand_on_next_part_cond_trans_commutative = true;
543 self.stage.push(plan)
544 }
545 }
546 Commutativity::TransformedCommutative { transformer } => {
547 if let Some(transformer) = transformer {
548 let transformer_actions = transformer(plan)?;
549 debug!(
550 "PlanRewriter: transformed plan: {}\n from {plan}",
551 transformer_actions
552 .extra_parent_plans
553 .iter()
554 .enumerate()
555 .map(|(i, p)| format!(
556 "Extra {i}-th parent plan from parent to child = {}",
557 p.display()
558 ))
559 .collect::<Vec<_>>()
560 .join("\n")
561 );
562 if let Some(new_child_plan) = &transformer_actions.new_child_plan {
563 debug!("PlanRewriter: new child plan: {}", new_child_plan);
564 }
565 if let Some(last_stage) = transformer_actions.extra_parent_plans.last() {
566 self.update_column_requirements(last_stage, self.level - 1);
569 }
570 self.stage
571 .extend(transformer_actions.extra_parent_plans.into_iter().rev());
572 self.expand_on_next_call = true;
573 self.new_child_plan = transformer_actions.new_child_plan;
574 }
575 }
576 Commutativity::NonCommutative
577 | Commutativity::Unimplemented
578 | Commutativity::Unsupported => {
579 debug!("PlanRewriter: meet a non-commutative plan, expanding now: {plan}");
580 return Ok(true);
581 }
582 }
583
584 Ok(false)
585 }
586
587 fn update_column_requirements(&mut self, plan: &LogicalPlan, plan_level: usize) {
591 debug!(
592 "PlanRewriter: update column requirements for plan: {plan}\n with old column_requirements: {:?}",
593 self.column_requirements
594 );
595 let mut container = HashSet::new();
596 for expr in plan.expressions() {
597 let _ = expr_to_columns(&expr, &mut container);
599 }
600
601 self.column_requirements.push((container, plan_level));
602 debug!(
603 "PlanRewriter: updated column requirements: {:?}",
604 self.column_requirements
605 );
606 }
607
608 fn is_expanded(&self) -> bool {
609 self.status == RewriterStatus::Expanded
610 }
611
612 fn set_expanded(&mut self) {
613 self.status = RewriterStatus::Expanded;
614 }
615
616 fn set_unexpanded(&mut self) {
617 self.status = RewriterStatus::Unexpanded;
618 }
619
620 fn maybe_set_partitions(&mut self, plan: &LogicalPlan) -> DfResult<()> {
621 if let Some(part_cols) = &mut self.partition_cols {
622 let child = plan.inputs().first().cloned().ok_or_else(|| {
624 datafusion_common::DataFusionError::Internal(format!(
625 "PlanRewriter: maybe_set_partitions: plan has no child: {plan}"
626 ))
627 })?;
628
629 for (_col_name, alias_set) in part_cols.iter_mut() {
630 let aliased_cols = aliased_columns_for(
631 &alias_set.clone().into_iter().collect(),
632 plan,
633 Some(child),
634 )?;
635 *alias_set = aliased_cols.into_values().flatten().collect();
636 }
637
638 debug!(
639 "PlanRewriter: maybe_set_partitions: updated partition columns: {:?} at plan: {}",
640 part_cols,
641 plan.display()
642 );
643
644 return Ok(());
645 }
646
647 if let LogicalPlan::TableScan(table_scan) = plan
648 && let Some(source) = table_scan
649 .source
650 .as_any()
651 .downcast_ref::<DefaultTableSource>()
652 && let Some(provider) = source
653 .table_provider
654 .as_any()
655 .downcast_ref::<DfTableProviderAdapter>()
656 {
657 let table = provider.table();
658 if table.table_type() == TableType::Base {
659 let info = table.table_info();
660 let partition_key_indices = info.meta.partition_key_indices.clone();
661 let schema = info.meta.schema.clone();
662 let mut partition_cols = partition_key_indices
663 .iter()
664 .map(|index| schema.column_name_by_index(*index).to_string())
665 .collect::<Vec<String>>();
666 debug!(
667 "PlanRewriter: loaded table partition metadata, table: {}, table_id: {}, partition_key_indices: {:?}, partition_columns: {:?}",
668 info.name, info.ident.table_id, info.meta.partition_key_indices, partition_cols,
669 );
670
671 let partition_rules = table.partition_rules();
672 let exist_phy_part_cols_not_in_logical_table = partition_rules
673 .map(|r| !r.extra_phy_cols_not_in_logical_table.is_empty())
674 .unwrap_or(false);
675
676 if exist_phy_part_cols_not_in_logical_table && partition_cols.is_empty() {
677 partition_cols.push(OTHER_PHY_PART_COL_PLACEHOLDER.to_string());
685 }
686 self.partition_cols = Some(
687 partition_cols
688 .into_iter()
689 .map(|c| {
690 if c == OTHER_PHY_PART_COL_PLACEHOLDER {
691 return Ok((c.clone(), BTreeSet::new()));
693 }
694 let index =
695 if let Some(c) = plan.schema().index_of_column_by_name(None, &c){
696 c
697 } else {
698 return Ok((c.clone(), BTreeSet::new()))
701 };
702 let column = plan.schema().columns().get(index).cloned().ok_or_else(|| {
703 datafusion_common::DataFusionError::Internal(format!(
704 "PlanRewriter: maybe_set_partitions: column index {index} out of bounds in schema of plan: {plan}"
705 ))
706 })?;
707 Ok((c.clone(), BTreeSet::from([column])))
708 })
709 .collect::<DfResult<AliasMapping>>()?,
710 );
711 }
712 }
713
714 Ok(())
715 }
716
717 fn pop_stack(&mut self) {
719 self.level -= 1;
720 self.stack.pop();
721 }
722
723 fn expand(&mut self, mut on_node: LogicalPlan) -> DfResult<LogicalPlan> {
724 let schema = on_node.schema().clone();
726 if let Some(new_child_plan) = self.new_child_plan.take() {
727 on_node = new_child_plan;
729 }
730 let mut rewriter = EnforceDistRequirementRewriter::new(
731 std::mem::take(&mut self.column_requirements),
732 self.level,
733 );
734 debug!(
735 "PlanRewriter: enforce column requirements for node: {on_node} with rewriter: {rewriter:?}"
736 );
737 on_node = on_node.rewrite(&mut rewriter)?.data;
738 debug!(
739 "PlanRewriter: after enforced column requirements with rewriter: {rewriter:?} for node:\n{on_node}"
740 );
741
742 debug!(
743 "PlanRewriter: expand on node: {on_node} with partition col alias mapping: {:?}",
744 self.partition_cols
745 );
746
747 let mut node = MergeScanLogicalPlan::new(
749 on_node.clone(),
750 false,
751 self.partition_cols.clone().unwrap_or_default(),
754 )
755 .into_logical_plan();
756
757 for new_stage in self.stage.drain(..) {
759 let new_stage = if let LogicalPlan::Extension(ext) = &new_stage
761 && let Some(merge_sort) = ext.node.as_any().downcast_ref::<MergeSortLogicalPlan>()
762 {
763 rewrite_merge_sort_exprs(merge_sort, &on_node)?
765 } else {
766 new_stage
767 };
768 node = new_stage
769 .with_new_exprs(new_stage.expressions_consider_join(), vec![node.clone()])?;
770 }
771 self.set_expanded();
772
773 let node = LogicalPlanBuilder::from(node)
776 .project(schema.iter().map(|(qualifier, field)| {
777 Expr::Column(Column::new(qualifier.cloned(), field.name()))
778 }))?
779 .build()?;
780
781 Ok(node)
782 }
783}
784
785#[derive(Debug)]
793struct EnforceDistRequirementRewriter {
794 column_requirements: Vec<(HashSet<Column>, usize)>,
798 cur_level: usize,
809 plan_per_level: BTreeMap<usize, LogicalPlan>,
810}
811
812impl EnforceDistRequirementRewriter {
813 fn new(column_requirements: Vec<(HashSet<Column>, usize)>, cur_level: usize) -> Self {
814 debug!(
815 "Create EnforceDistRequirementRewriter with column_requirements: {:?} at cur_level: {}",
816 column_requirements, cur_level
817 );
818 Self {
819 column_requirements,
820 cur_level,
821 plan_per_level: BTreeMap::new(),
822 }
823 }
824
825 fn get_current_applicable_column_requirements(
829 &self,
830 node: &LogicalPlan,
831 ) -> DfResult<BTreeMap<(Column, usize), BTreeSet<Column>>> {
832 let col_req_per_level = self
833 .column_requirements
834 .iter()
835 .filter(|(_, level)| *level >= self.cur_level)
836 .collect::<Vec<_>>();
837
838 let mut result_alias_mapping = BTreeMap::new();
841 let Some(child) = node.inputs().first().cloned() else {
842 return Ok(Default::default());
843 };
844 for (col_req, level) in col_req_per_level {
845 if let Some(original) = self.plan_per_level.get(level) {
846 let aliased_cols =
848 aliased_columns_for(&col_req.iter().cloned().collect(), node, Some(original))?;
849 for original_col in col_req {
850 let aliased_cols = aliased_cols.get(original_col).cloned();
851 if let Some(cols) = aliased_cols
852 && !cols.is_empty()
853 {
854 result_alias_mapping.insert((original_col.clone(), *level), cols);
855 } else {
856 let aliases_in_child = aliased_columns_for(
861 &[original_col.clone()].into(),
862 child,
863 Some(original),
864 )?;
865 let Some(aliases) = aliases_in_child
866 .get(original_col)
867 .cloned()
868 .filter(|a| !a.is_empty())
869 else {
870 return Err(datafusion_common::DataFusionError::Internal(format!(
871 "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}",
872 )));
873 };
874
875 result_alias_mapping.insert((original_col.clone(), *level), aliases);
876 }
877 }
878 }
879 }
880 Ok(result_alias_mapping)
881 }
882}
883
884impl TreeNodeRewriter for EnforceDistRequirementRewriter {
885 type Node = LogicalPlan;
886
887 fn f_down(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
888 if node.inputs().len() > 1 {
890 return Err(datafusion_common::DataFusionError::Internal(
891 "EnforceDistRequirementRewriter: node with multiple inputs is not supported"
892 .to_string(),
893 ));
894 }
895 self.plan_per_level.insert(self.cur_level, node.clone());
896 self.cur_level += 1;
897 Ok(Transformed::no(node))
898 }
899
900 fn f_up(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
901 self.cur_level -= 1;
902 if let LogicalPlan::Projection(ref projection) = node {
906 let mut applicable_column_requirements =
907 self.get_current_applicable_column_requirements(&node)?;
908
909 debug!(
910 "EnforceDistRequirementRewriter: applicable column requirements at level {} = {:?} for node {}",
911 self.cur_level,
912 applicable_column_requirements,
913 node.display()
914 );
915
916 for expr in &projection.expr {
917 let (qualifier, name) = expr.qualified_name();
918 let column = Column::new(qualifier, name);
919 applicable_column_requirements.retain(|_col_level, alias_set| {
920 !alias_set.contains(&column)
922 });
923 }
924 if applicable_column_requirements.is_empty() {
925 return Ok(Transformed::no(node));
926 }
927
928 let mut new_exprs = projection.expr.clone();
929 for (col, alias_set) in &applicable_column_requirements {
930 new_exprs.push(Expr::Column(alias_set.first().cloned().ok_or_else(
932 || {
933 datafusion_common::DataFusionError::Internal(
934 format!("EnforceDistRequirementRewriter: alias set is empty, for column {col:?} in node {node}"),
935 )
936 },
937 )?));
938 }
939 let new_node =
940 node.with_new_exprs(new_exprs, node.inputs().into_iter().cloned().collect())?;
941 debug!(
942 "EnforceDistRequirementRewriter: added missing columns {:?} to projection node from old node: \n{node}\n Making new node: \n{new_node}",
943 applicable_column_requirements
944 );
945
946 self.plan_per_level.insert(self.cur_level, new_node.clone());
948
949 return Ok(Transformed::yes(new_node));
951 }
952 Ok(Transformed::no(node))
953 }
954}
955
956impl TreeNodeRewriter for PlanRewriter {
957 type Node = LogicalPlan;
958
959 fn f_down<'a>(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
961 self.level += 1;
962 self.stack.push((node.clone(), self.level));
963 self.stage.clear();
965 self.set_unexpanded();
966 self.partition_cols = None;
967 Ok(Transformed::no(node))
968 }
969
970 fn f_up(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
974 if self.is_expanded() {
976 self.pop_stack();
977 return Ok(Transformed::no(node));
978 }
979
980 if node.inputs().is_empty() && !matches!(node, LogicalPlan::TableScan(_)) {
982 self.set_expanded();
983 self.pop_stack();
984 return Ok(Transformed::no(node));
985 }
986
987 self.maybe_set_partitions(&node)?;
988
989 let Some(parent) = self.get_parent() else {
990 debug!("Plan Rewriter: expand now for no parent found for node: {node}");
991 let node = self.expand(node);
992 debug!(
993 "PlanRewriter: expanded plan: {}",
994 match &node {
995 Ok(n) => n.to_string(),
996 Err(e) => format!("Error expanding plan: {e}"),
997 }
998 );
999 let node = node?;
1000 self.pop_stack();
1001 return Ok(Transformed::yes(node));
1002 };
1003
1004 let parent = parent.clone();
1005
1006 if self.should_expand(&parent)? {
1007 debug!(
1009 "PlanRewriter: should expand child:\n {node}\n Of Parent: {}",
1010 parent.display()
1011 );
1012 let node = self.expand(node);
1013 debug!(
1014 "PlanRewriter: expanded plan: {}",
1015 match &node {
1016 Ok(n) => n.to_string(),
1017 Err(e) => format!("Error expanding plan: {e}"),
1018 }
1019 );
1020 let node = node?;
1021 self.pop_stack();
1022 return Ok(Transformed::yes(node));
1023 }
1024
1025 self.pop_stack();
1026 Ok(Transformed::no(node))
1027 }
1028}