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::optimizer::expr_depth;
57use crate::optimizer::infer_aggregate_input_bounds::InferAggregateInputBoundsRule;
58use crate::options::ScheduledTimeExtension;
59use crate::plan::ExtractExpr;
60use crate::query_engine::DefaultSerializer;
61
62#[cfg(test)]
63mod test;
64
65mod fallback;
66pub(crate) mod utils;
67
68pub(crate) use utils::AliasMapping;
69
70const OTHER_PHY_PART_COL_PLACEHOLDER: &str = "__OTHER_PHYSICAL_PART_COLS_PLACEHOLDER__";
72
73#[derive(Debug, Clone)]
74pub struct DistPlannerOptions {
75 pub allow_query_fallback: bool,
76}
77
78impl ConfigExtension for DistPlannerOptions {
79 const PREFIX: &'static str = "dist_planner";
80}
81
82impl ExtensionOptions for DistPlannerOptions {
83 fn as_any(&self) -> &dyn std::any::Any {
84 self
85 }
86
87 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
88 self
89 }
90
91 fn cloned(&self) -> Box<dyn ExtensionOptions> {
92 Box::new(self.clone())
93 }
94
95 fn set(&mut self, key: &str, value: &str) -> DfResult<()> {
96 Err(datafusion_common::DataFusionError::NotImplemented(format!(
97 "DistPlannerOptions does not support set key: {key} with value: {value}"
98 )))
99 }
100
101 fn entries(&self) -> Vec<datafusion::config::ConfigEntry> {
102 vec![datafusion::config::ConfigEntry {
103 key: "allow_query_fallback".to_string(),
104 value: Some(self.allow_query_fallback.to_string()),
105 description: "Allow query fallback to fallback plan rewriter",
106 }]
107 }
108}
109
110#[derive(Debug)]
111pub struct DistPlannerAnalyzer;
112
113impl AnalyzerRule for DistPlannerAnalyzer {
114 fn name(&self) -> &str {
115 "DistPlannerAnalyzer"
116 }
117
118 fn analyze(
119 &self,
120 plan: LogicalPlan,
121 config: &ConfigOptions,
122 ) -> datafusion_common::Result<LogicalPlan> {
123 expr_depth::validate_plan(&plan)?;
124 let mut config = config.clone();
125 config.optimizer.filter_null_join_keys = true;
127 let config = Arc::new(config);
128 let opt = config.extensions.get::<DistPlannerOptions>();
129 let allow_fallback = opt.map(|o| o.allow_query_fallback).unwrap_or(false);
130
131 let scheduled_time = config
135 .extensions
136 .get::<ScheduledTimeExtension>()
137 .and_then(|ext| ext.scheduled_time);
138
139 let optimizer_context = PatchOptimizerContext {
140 inner: datafusion_optimizer::OptimizerContext::new(),
141 config: config.clone(),
142 scheduled_time,
143 };
144
145 let plan = plan
146 .rewrite_with_subqueries(&mut PlanTreeExpressionSimplifier::new(optimizer_context))?
147 .data;
148 expr_depth::validate_plan(&plan)?;
149 let fallback_plan = plan.clone();
150
151 let optimizer_context = PatchOptimizerContext {
158 inner: datafusion_optimizer::OptimizerContext::new(),
159 config: config.clone(),
160 scheduled_time,
161 };
162 let plan = match pre_merge_scan_optimizer().optimize(plan, &optimizer_context, |_, _| {}) {
163 Ok(plan) => plan,
164 Err(err) => {
165 if allow_fallback {
166 common_telemetry::warn!(err; "Failed to pre-optimize plan, using fallback plan rewriter for plan: {fallback_plan}");
167 PUSH_DOWN_FALLBACK_ERRORS_TOTAL.inc();
168 return self.use_fallback(fallback_plan);
169 } else {
170 return Err(err);
171 }
172 }
173 };
174 let plan = plan
175 .transform_down_with_subqueries(&unwrap_dictionary_literals)?
176 .data;
177
178 let result = match self.try_push_down(plan.clone()) {
179 Ok(plan) => plan,
180 Err(err) => {
181 if allow_fallback {
182 common_telemetry::warn!(err; "Failed to push down plan, using fallback plan rewriter for plan: {plan}");
183 PUSH_DOWN_FALLBACK_ERRORS_TOTAL.inc();
185 self.use_fallback(fallback_plan)?
186 } else {
187 return Err(err);
188 }
189 }
190 };
191
192 Ok(result)
193 }
194}
195
196fn pre_merge_scan_optimizer() -> Optimizer {
220 Optimizer::with_rules(vec![
221 Arc::new(RewriteSetComparison::new()),
222 Arc::new(DecorrelatePredicateSubquery::new()),
223 Arc::new(ScalarSubqueryToJoin::new()),
224 Arc::new(DecorrelateLateralJoin::new()),
225 Arc::new(ExtractEquijoinPredicate::new()),
226 Arc::new(EliminateFilter::new()),
227 Arc::new(PropagateEmptyRelation::new()),
228 Arc::new(FilterNullJoinKeys::default()),
229 Arc::new(InferAggregateInputBoundsRule),
230 Arc::new(PushDownFilter::new()),
231 Arc::new(SimplifyExpressions::new()),
232 ])
233}
234
235fn unwrap_dictionary_literals(plan: LogicalPlan) -> DfResult<Transformed<LogicalPlan>> {
236 if matches!(&plan, LogicalPlan::Values(_)) {
239 return Ok(Transformed::no(plan));
240 }
241
242 plan.map_expressions(|expr| {
243 expr.transform_up(|expr| match expr {
244 Expr::Literal(ScalarValue::Dictionary(_, value), metadata) => {
245 Ok(Transformed::yes(Expr::Literal(*value, metadata)))
246 }
247 _ => Ok(Transformed::no(expr)),
248 })
249 })
250}
251
252impl DistPlannerAnalyzer {
253 fn try_push_down(&self, plan: LogicalPlan) -> DfResult<LogicalPlan> {
255 let plan = plan.transform_with_subqueries(&Self::inspect_plan_with_subquery)?;
260 let mut rewriter = PlanRewriter::new(&plan.data);
261 let result = plan.data.rewrite(&mut rewriter)?.data;
262 Self::assign_merge_scan_remote_dyn_filter_producer_ids(result)
263 }
264
265 fn use_fallback(&self, plan: LogicalPlan) -> DfResult<LogicalPlan> {
267 let mut rewriter = fallback::FallbackPlanRewriter;
268 let result = plan.rewrite(&mut rewriter)?.data;
269 Self::assign_merge_scan_remote_dyn_filter_producer_ids(result)
270 }
271
272 fn inspect_plan_with_subquery(plan: LogicalPlan) -> DfResult<Transformed<LogicalPlan>> {
273 if let LogicalPlan::Limit(_) | LogicalPlan::Distinct(_) = &plan {
276 return Ok(Transformed::no(plan));
277 }
278
279 let exprs = plan
280 .expressions_consider_join()
281 .into_iter()
282 .map(|e| e.transform(&Self::transform_subquery).map(|x| x.data))
283 .collect::<DfResult<Vec<_>>>()?;
284
285 if !matches!(plan, LogicalPlan::Unnest(_)) {
287 let inputs = plan.inputs().into_iter().cloned().collect::<Vec<_>>();
288 Ok(Transformed::yes(plan.with_new_exprs(exprs, inputs)?))
289 } else {
290 Ok(Transformed::no(plan))
291 }
292 }
293
294 fn transform_subquery(expr: Expr) -> DfResult<Transformed<Expr>> {
295 match expr {
296 Expr::Exists(exists) => Ok(Transformed::yes(Expr::Exists(Exists {
297 subquery: Self::handle_subquery(exists.subquery)?,
298 negated: exists.negated,
299 }))),
300 Expr::InSubquery(in_subquery) => Ok(Transformed::yes(Expr::InSubquery(InSubquery {
301 expr: in_subquery.expr,
302 subquery: Self::handle_subquery(in_subquery.subquery)?,
303 negated: in_subquery.negated,
304 }))),
305 Expr::ScalarSubquery(scalar_subquery) => Ok(Transformed::yes(Expr::ScalarSubquery(
306 Self::handle_subquery(scalar_subquery)?,
307 ))),
308
309 _ => Ok(Transformed::no(expr)),
310 }
311 }
312
313 fn handle_subquery(subquery: Subquery) -> DfResult<Subquery> {
314 let mut rewriter = PlanRewriter::new(&subquery.subquery);
315 let mut rewrote_subquery = subquery
316 .subquery
317 .as_ref()
318 .clone()
319 .rewrite(&mut rewriter)?
320 .data;
321 if matches!(rewrote_subquery, LogicalPlan::Extension(_)) {
323 let output_schema = rewrote_subquery.schema().clone();
324 let project_exprs = output_schema
325 .fields()
326 .iter()
327 .map(|f| col_fn(f.name()))
328 .collect::<Vec<_>>();
329 rewrote_subquery = LogicalPlanBuilder::from(rewrote_subquery)
330 .project(project_exprs)?
331 .build()?;
332 }
333
334 Ok(Subquery {
335 subquery: Arc::new(rewrote_subquery),
336 outer_ref_columns: subquery.outer_ref_columns,
337 spans: Default::default(),
338 })
339 }
340
341 fn assign_merge_scan_remote_dyn_filter_producer_ids(
342 plan: LogicalPlan,
343 ) -> DfResult<LogicalPlan> {
344 let mut assigner = MergeScanRemoteDynFilterProducerIdAssigner::default();
345 Ok(plan.rewrite_with_subqueries(&mut assigner)?.data)
346 }
347}
348
349#[derive(Debug, Default)]
350struct RemoteDynFilterProducerIdAllocator {
351 next_remote_dyn_filter_producer_id: u64,
352}
353
354impl RemoteDynFilterProducerIdAllocator {
355 fn allocate(&mut self) -> RemoteDynFilterProducerId {
356 self.next_remote_dyn_filter_producer_id += 1;
357 RemoteDynFilterProducerId::new(self.next_remote_dyn_filter_producer_id)
358 }
359}
360
361#[derive(Debug, Default)]
363struct MergeScanRemoteDynFilterProducerIdAssigner {
364 remote_dyn_filter_producer_id_allocator: RemoteDynFilterProducerIdAllocator,
365}
366
367impl TreeNodeRewriter for MergeScanRemoteDynFilterProducerIdAssigner {
368 type Node = LogicalPlan;
369
370 fn f_up(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
371 let LogicalPlan::Extension(extension) = &node else {
372 return Ok(Transformed::no(node));
373 };
374 let Some(merge_scan) = extension
375 .node
376 .as_any()
377 .downcast_ref::<MergeScanLogicalPlan>()
378 else {
379 return Ok(Transformed::no(node));
380 };
381
382 Ok(Transformed::yes(
383 merge_scan
384 .clone()
385 .with_remote_dyn_filter_producer_id(
386 self.remote_dyn_filter_producer_id_allocator.allocate(),
387 )
388 .into_logical_plan(),
389 ))
390 }
391}
392
393#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
395enum RewriterStatus {
396 #[default]
397 Unexpanded,
398 Expanded,
399}
400
401#[derive(Debug, Default)]
402struct PlanRewriter {
403 whole_plan_encodable: bool,
406 level: usize,
408 stack: Vec<(LogicalPlan, usize)>,
410 stage: Vec<LogicalPlan>,
412 status: RewriterStatus,
413 partition_cols: Option<AliasMapping>,
415 column_requirements: Vec<(HashSet<Column>, usize)>,
441 expand_on_next_call: bool,
446 expand_on_next_part_cond_trans_commutative: bool,
458 new_child_plan: Option<LogicalPlan>,
459}
460
461impl PlanRewriter {
462 fn new(plan: &LogicalPlan) -> Self {
465 Self {
466 whole_plan_encodable: DFLogicalSubstraitConvertor
467 .encode(plan, DefaultSerializer)
468 .is_ok(),
469 ..Default::default()
470 }
471 }
472
473 fn get_parent(&self) -> Option<&LogicalPlan> {
474 self.stack
476 .iter()
477 .rev()
478 .find(|(_, level)| *level == self.level - 1)
479 .map(|(node, _)| node)
480 }
481
482 fn should_expand(&mut self, plan: &LogicalPlan) -> DfResult<bool> {
484 debug!(
485 "Check should_expand at level: {} with Stack:\n{}, ",
486 self.level,
487 self.stack
488 .iter()
489 .map(|(p, l)| format!("{l}:{}{}", " ".repeat(l - 1), p.display()))
490 .collect::<Vec<String>>()
491 .join("\n"),
492 );
493 if !self.whole_plan_encodable
501 && let Err(e) = DFLogicalSubstraitConvertor.encode(plan, DefaultSerializer)
502 {
503 debug!(
504 "PlanRewriter: plan cannot be converted to substrait with error={e:?}, expanding now: {plan}"
505 );
506 return Ok(true);
507 }
508
509 if self.expand_on_next_call {
510 self.expand_on_next_call = false;
511 debug!("PlanRewriter: expand_on_next_call is true, expanding now");
512 return Ok(true);
513 }
514
515 if self.expand_on_next_part_cond_trans_commutative {
516 let comm = Categorizer::check_plan(plan, self.partition_cols.clone())?;
517 match comm {
518 Commutativity::PartialCommutative => {
519 self.expand_on_next_part_cond_trans_commutative = false;
525 self.expand_on_next_call = true;
526 }
527 Commutativity::ConditionalCommutative(_)
528 | Commutativity::TransformedCommutative { .. } => {
529 self.expand_on_next_part_cond_trans_commutative = false;
532 debug!(
533 "PlanRewriter: meet a new conditional/transformed commutative plan, expanding now: {plan}"
534 );
535 return Ok(true);
536 }
537 _ => (),
538 }
539 }
540
541 match Categorizer::check_plan(plan, self.partition_cols.clone())? {
542 Commutativity::Commutative => {
543 if let LogicalPlan::Extension(ext_a) = plan
548 && ext_a.node.name() == SeriesDivide::name()
549 && let Some(LogicalPlan::Extension(ext_b)) = self.stage.last()
550 && ext_b.node.name() == MergeSortLogicalPlan::name()
551 {
552 self.stage.pop();
556 self.expand_on_next_part_cond_trans_commutative = false;
557 self.column_requirements.clear();
558 }
559 }
560 Commutativity::PartialCommutative => {
561 if let Some(plan) = partial_commutative_transformer(plan) {
562 self.update_column_requirements(&plan, self.level - 1);
564 self.expand_on_next_part_cond_trans_commutative = true;
565 self.stage.push(plan)
566 }
567 }
568 Commutativity::ConditionalCommutative(transformer) => {
569 if let Some(transformer) = transformer
570 && let Some(plan) = transformer(plan)
571 {
572 self.update_column_requirements(&plan, self.level - 1);
574 self.expand_on_next_part_cond_trans_commutative = true;
575 self.stage.push(plan)
576 }
577 }
578 Commutativity::TransformedCommutative { transformer } => {
579 if let Some(transformer) = transformer {
580 let transformer_actions = transformer(plan)?;
581 debug!(
582 "PlanRewriter: transformed plan: {}\n from {plan}",
583 transformer_actions
584 .extra_parent_plans
585 .iter()
586 .enumerate()
587 .map(|(i, p)| format!(
588 "Extra {i}-th parent plan from parent to child = {}",
589 p.display()
590 ))
591 .collect::<Vec<_>>()
592 .join("\n")
593 );
594 if let Some(new_child_plan) = &transformer_actions.new_child_plan {
595 debug!("PlanRewriter: new child plan: {}", new_child_plan);
596 }
597 if let Some(last_stage) = transformer_actions.extra_parent_plans.last() {
598 self.update_column_requirements(last_stage, self.level - 1);
601 }
602 self.stage
603 .extend(transformer_actions.extra_parent_plans.into_iter().rev());
604 self.expand_on_next_call = true;
605 self.new_child_plan = transformer_actions.new_child_plan;
606 }
607 }
608 Commutativity::NonCommutative
609 | Commutativity::Unimplemented
610 | Commutativity::Unsupported => {
611 debug!("PlanRewriter: meet a non-commutative plan, expanding now: {plan}");
612 return Ok(true);
613 }
614 }
615
616 Ok(false)
617 }
618
619 fn update_column_requirements(&mut self, plan: &LogicalPlan, plan_level: usize) {
623 debug!(
624 "PlanRewriter: update column requirements for plan: {plan}\n with old column_requirements: {:?}",
625 self.column_requirements
626 );
627 let mut container = HashSet::new();
628 for expr in plan.expressions() {
629 let _ = expr_to_columns(&expr, &mut container);
631 }
632
633 self.column_requirements.push((container, plan_level));
634 debug!(
635 "PlanRewriter: updated column requirements: {:?}",
636 self.column_requirements
637 );
638 }
639
640 fn is_expanded(&self) -> bool {
641 self.status == RewriterStatus::Expanded
642 }
643
644 fn set_expanded(&mut self) {
645 self.status = RewriterStatus::Expanded;
646 }
647
648 fn set_unexpanded(&mut self) {
649 self.status = RewriterStatus::Unexpanded;
650 }
651
652 fn maybe_set_partitions(&mut self, plan: &LogicalPlan) -> DfResult<()> {
653 if let Some(part_cols) = &mut self.partition_cols {
654 let child = plan.inputs().first().cloned().ok_or_else(|| {
656 datafusion_common::DataFusionError::Internal(format!(
657 "PlanRewriter: maybe_set_partitions: plan has no child: {plan}"
658 ))
659 })?;
660
661 for alias_set in part_cols.values_mut() {
662 let aliased_cols = aliased_columns_for(
663 &alias_set.clone().into_iter().collect(),
664 plan,
665 Some(child),
666 )?;
667 *alias_set = aliased_cols.into_values().flatten().collect();
668 }
669
670 debug!(
671 "PlanRewriter: maybe_set_partitions: updated partition columns: {:?} at plan: {}",
672 part_cols,
673 plan.display()
674 );
675
676 return Ok(());
677 }
678
679 if let LogicalPlan::TableScan(table_scan) = plan
680 && let Some(source) = table_scan.source.downcast_ref::<DefaultTableSource>()
681 && let Some(provider) = source
682 .table_provider
683 .downcast_ref::<DfTableProviderAdapter>()
684 {
685 let table = provider.table();
686 if table.table_type() == TableType::Base {
687 let info = table.table_info();
688 let partition_key_indices = info.meta.partition_key_indices.clone();
689 let schema = info.meta.schema.clone();
690 let mut partition_cols = partition_key_indices
691 .iter()
692 .map(|index| schema.column_name_by_index(*index).to_string())
693 .collect::<Vec<String>>();
694 debug!(
695 "PlanRewriter: loaded table partition metadata, table: {}, table_id: {}, partition_key_indices: {:?}, partition_columns: {:?}",
696 info.name, info.ident.table_id, info.meta.partition_key_indices, partition_cols,
697 );
698
699 let partition_rules = table.partition_rules();
700 let exist_phy_part_cols_not_in_logical_table = partition_rules
701 .map(|r| !r.extra_phy_cols_not_in_logical_table.is_empty())
702 .unwrap_or(false);
703
704 if exist_phy_part_cols_not_in_logical_table && partition_cols.is_empty() {
705 partition_cols.push(OTHER_PHY_PART_COL_PLACEHOLDER.to_string());
713 }
714 self.partition_cols = Some(
715 partition_cols
716 .into_iter()
717 .map(|c| {
718 if c == OTHER_PHY_PART_COL_PLACEHOLDER {
719 return Ok((c.clone(), BTreeSet::new()));
721 }
722 let index =
723 if let Some(c) = plan.schema().index_of_column_by_name(None, &c){
724 c
725 } else {
726 return Ok((c.clone(), BTreeSet::new()))
729 };
730 let column = plan.schema().columns().get(index).cloned().ok_or_else(|| {
731 datafusion_common::DataFusionError::Internal(format!(
732 "PlanRewriter: maybe_set_partitions: column index {index} out of bounds in schema of plan: {plan}"
733 ))
734 })?;
735 Ok((c.clone(), BTreeSet::from([column])))
736 })
737 .collect::<DfResult<AliasMapping>>()?,
738 );
739 }
740 }
741
742 Ok(())
743 }
744
745 fn pop_stack(&mut self) {
747 self.level -= 1;
748 self.stack.pop();
749 }
750
751 fn expand(&mut self, mut on_node: LogicalPlan) -> DfResult<LogicalPlan> {
752 let schema = on_node.schema().clone();
754 if let Some(new_child_plan) = self.new_child_plan.take() {
755 on_node = new_child_plan;
757 }
758 let mut rewriter = EnforceDistRequirementRewriter::new(
759 std::mem::take(&mut self.column_requirements),
760 self.level,
761 );
762 debug!(
763 "PlanRewriter: enforce column requirements for node: {on_node} with rewriter: {rewriter:?}"
764 );
765 on_node = on_node.rewrite(&mut rewriter)?.data;
766 debug!(
767 "PlanRewriter: after enforced column requirements with rewriter: {rewriter:?} for node:\n{on_node}"
768 );
769
770 debug!(
771 "PlanRewriter: expand on node: {on_node} with partition col alias mapping: {:?}",
772 self.partition_cols
773 );
774
775 let mut node = MergeScanLogicalPlan::new(on_node.clone(), false).into_logical_plan();
777
778 for new_stage in self.stage.drain(..) {
780 let new_stage = if let LogicalPlan::Extension(ext) = &new_stage
782 && let Some(merge_sort) = ext.node.as_any().downcast_ref::<MergeSortLogicalPlan>()
783 {
784 rewrite_merge_sort_exprs(merge_sort, &on_node)?
786 } else {
787 new_stage
788 };
789 node = new_stage
790 .with_new_exprs(new_stage.expressions_consider_join(), vec![node.clone()])?;
791 }
792 self.set_expanded();
793
794 let node = LogicalPlanBuilder::from(node)
797 .project(schema.iter().map(|(qualifier, field)| {
798 Expr::Column(Column::new(qualifier.cloned(), field.name()))
799 }))?
800 .build()?;
801
802 Ok(node)
803 }
804}
805
806#[derive(Debug)]
814struct EnforceDistRequirementRewriter {
815 column_requirements: Vec<(HashSet<Column>, usize)>,
819 cur_level: usize,
830 plan_per_level: BTreeMap<usize, LogicalPlan>,
831}
832
833impl EnforceDistRequirementRewriter {
834 fn new(column_requirements: Vec<(HashSet<Column>, usize)>, cur_level: usize) -> Self {
835 debug!(
836 "Create EnforceDistRequirementRewriter with column_requirements: {:?} at cur_level: {}",
837 column_requirements, cur_level
838 );
839 Self {
840 column_requirements,
841 cur_level,
842 plan_per_level: BTreeMap::new(),
843 }
844 }
845
846 fn get_current_applicable_column_requirements(
850 &self,
851 node: &LogicalPlan,
852 ) -> DfResult<BTreeMap<(Column, usize), BTreeSet<Column>>> {
853 let col_req_per_level = self
854 .column_requirements
855 .iter()
856 .filter(|(_, level)| *level >= self.cur_level)
857 .collect::<Vec<_>>();
858
859 let mut result_alias_mapping = BTreeMap::new();
862 let Some(child) = node.inputs().first().cloned() else {
863 return Ok(Default::default());
864 };
865 for (col_req, level) in col_req_per_level {
866 if let Some(original) = self.plan_per_level.get(level) {
867 let aliased_cols =
869 aliased_columns_for(&col_req.iter().cloned().collect(), node, Some(original))?;
870 for original_col in col_req {
871 let aliased_cols = aliased_cols.get(original_col).cloned();
872 if let Some(cols) = aliased_cols
873 && !cols.is_empty()
874 {
875 result_alias_mapping.insert((original_col.clone(), *level), cols);
876 } else {
877 let aliases_in_child = aliased_columns_for(
882 &[original_col.clone()].into(),
883 child,
884 Some(original),
885 )?;
886 let Some(aliases) = aliases_in_child
887 .get(original_col)
888 .cloned()
889 .filter(|a| !a.is_empty())
890 else {
891 return Err(datafusion_common::DataFusionError::Internal(format!(
892 "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}",
893 )));
894 };
895
896 result_alias_mapping.insert((original_col.clone(), *level), aliases);
897 }
898 }
899 }
900 }
901 Ok(result_alias_mapping)
902 }
903}
904
905impl TreeNodeRewriter for EnforceDistRequirementRewriter {
906 type Node = LogicalPlan;
907
908 fn f_down(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
909 if node.inputs().len() > 1 {
911 return Err(datafusion_common::DataFusionError::Internal(
912 "EnforceDistRequirementRewriter: node with multiple inputs is not supported"
913 .to_string(),
914 ));
915 }
916 self.plan_per_level.insert(self.cur_level, node.clone());
917 self.cur_level += 1;
918 Ok(Transformed::no(node))
919 }
920
921 fn f_up(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
922 self.cur_level -= 1;
923 if let LogicalPlan::Projection(ref projection) = node {
927 let mut applicable_column_requirements =
928 self.get_current_applicable_column_requirements(&node)?;
929
930 debug!(
931 "EnforceDistRequirementRewriter: applicable column requirements at level {} = {:?} for node {}",
932 self.cur_level,
933 applicable_column_requirements,
934 node.display()
935 );
936
937 for expr in &projection.expr {
938 let (qualifier, name) = expr.qualified_name();
939 let column = Column::new(qualifier, name);
940 applicable_column_requirements.retain(|_col_level, alias_set| {
941 !alias_set.contains(&column)
943 });
944 }
945 if applicable_column_requirements.is_empty() {
946 return Ok(Transformed::no(node));
947 }
948
949 let mut new_exprs = projection.expr.clone();
950 for (col, alias_set) in &applicable_column_requirements {
951 new_exprs.push(Expr::Column(alias_set.first().cloned().ok_or_else(
953 || {
954 datafusion_common::DataFusionError::Internal(
955 format!("EnforceDistRequirementRewriter: alias set is empty, for column {col:?} in node {node}"),
956 )
957 },
958 )?));
959 }
960 let new_node =
961 node.with_new_exprs(new_exprs, node.inputs().into_iter().cloned().collect())?;
962 debug!(
963 "EnforceDistRequirementRewriter: added missing columns {:?} to projection node from old node: \n{node}\n Making new node: \n{new_node}",
964 applicable_column_requirements
965 );
966
967 self.plan_per_level.insert(self.cur_level, new_node.clone());
969
970 return Ok(Transformed::yes(new_node));
972 }
973 Ok(Transformed::no(node))
974 }
975}
976
977impl TreeNodeRewriter for PlanRewriter {
978 type Node = LogicalPlan;
979
980 fn f_down<'a>(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
982 self.level += 1;
983 self.stack.push((node.clone(), self.level));
984 self.stage.clear();
986 self.set_unexpanded();
987 self.partition_cols = None;
988 Ok(Transformed::no(node))
989 }
990
991 fn f_up(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
995 if self.is_expanded() {
997 self.pop_stack();
998 return Ok(Transformed::no(node));
999 }
1000
1001 if node.inputs().is_empty() && !matches!(node, LogicalPlan::TableScan(_)) {
1003 self.set_expanded();
1004 self.pop_stack();
1005 return Ok(Transformed::no(node));
1006 }
1007
1008 self.maybe_set_partitions(&node)?;
1009
1010 let Some(parent) = self.get_parent() else {
1011 debug!("Plan Rewriter: expand now for no parent found for node: {node}");
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 let parent = parent.clone();
1026
1027 if self.should_expand(&parent)? {
1028 debug!(
1030 "PlanRewriter: should expand child:\n {node}\n Of Parent: {}",
1031 parent.display()
1032 );
1033 let node = self.expand(node);
1034 debug!(
1035 "PlanRewriter: expanded plan: {}",
1036 match &node {
1037 Ok(n) => n.to_string(),
1038 Err(e) => format!("Error expanding plan: {e}"),
1039 }
1040 );
1041 let node = node?;
1042 self.pop_stack();
1043 return Ok(Transformed::yes(node));
1044 }
1045
1046 self.pop_stack();
1047 Ok(Transformed::no(node))
1048 }
1049}