1use std::collections::{BTreeMap, HashMap};
22use std::fmt;
23use std::fmt::Formatter;
24
25use arrow::datatypes::SchemaRef;
26
27use datafusion_common::display::{GraphvizBuilder, PlanType, StringifiedPlan};
28use datafusion_expr::display_schema;
29use datafusion_physical_expr::LexOrdering;
30
31use crate::metrics::{MetricCategory, MetricType};
32use crate::render_tree::RenderTree;
33
34use super::{ExecutionPlan, ExecutionPlanVisitor, accept};
35
36#[derive(Debug, Clone, Copy, PartialEq)]
38pub enum DisplayFormatType {
39 Default,
44 Verbose,
48 TreeRender,
80}
81
82#[derive(Debug, Clone)]
116pub struct DisplayableExecutionPlan<'a> {
117 inner: &'a dyn ExecutionPlan,
118 show_metrics: ShowMetrics,
120 show_statistics: bool,
122 show_schema: bool,
124 metric_types: Vec<MetricType>,
126 metric_categories: Option<Vec<MetricCategory>>,
129 tree_maximum_render_width: usize,
131}
132
133impl<'a> DisplayableExecutionPlan<'a> {
134 fn default_metric_types() -> Vec<MetricType> {
135 vec![MetricType::Summary, MetricType::Dev]
136 }
137
138 pub fn new(inner: &'a dyn ExecutionPlan) -> Self {
141 Self {
142 inner,
143 show_metrics: ShowMetrics::None,
144 show_statistics: false,
145 show_schema: false,
146 metric_types: Self::default_metric_types(),
147 metric_categories: None,
148 tree_maximum_render_width: 240,
149 }
150 }
151
152 pub fn with_metrics(inner: &'a dyn ExecutionPlan) -> Self {
156 Self {
157 inner,
158 show_metrics: ShowMetrics::Aggregated,
159 show_statistics: false,
160 show_schema: false,
161 metric_types: Self::default_metric_types(),
162 metric_categories: None,
163 tree_maximum_render_width: 240,
164 }
165 }
166
167 pub fn with_full_metrics(inner: &'a dyn ExecutionPlan) -> Self {
171 Self {
172 inner,
173 show_metrics: ShowMetrics::Full,
174 show_statistics: false,
175 show_schema: false,
176 metric_types: Self::default_metric_types(),
177 metric_categories: None,
178 tree_maximum_render_width: 240,
179 }
180 }
181
182 pub fn set_show_schema(mut self, show_schema: bool) -> Self {
187 self.show_schema = show_schema;
188 self
189 }
190
191 pub fn set_show_statistics(mut self, show_statistics: bool) -> Self {
193 self.show_statistics = show_statistics;
194 self
195 }
196
197 pub fn set_metric_types(mut self, metric_types: Vec<MetricType>) -> Self {
199 self.metric_types = metric_types;
200 self
201 }
202
203 pub fn set_metric_categories(
213 mut self,
214 metric_categories: Option<Vec<MetricCategory>>,
215 ) -> Self {
216 self.metric_categories = metric_categories;
217 self
218 }
219
220 pub fn set_tree_maximum_render_width(mut self, width: usize) -> Self {
222 self.tree_maximum_render_width = width;
223 self
224 }
225
226 pub fn indent(&self, verbose: bool) -> impl fmt::Display + 'a {
237 let format_type = if verbose {
238 DisplayFormatType::Verbose
239 } else {
240 DisplayFormatType::Default
241 };
242 struct Wrapper<'a> {
243 format_type: DisplayFormatType,
244 plan: &'a dyn ExecutionPlan,
245 show_metrics: ShowMetrics,
246 show_statistics: bool,
247 show_schema: bool,
248 metric_types: Vec<MetricType>,
249 metric_categories: Option<Vec<MetricCategory>>,
250 }
251 impl fmt::Display for Wrapper<'_> {
252 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
253 let mut visitor = IndentVisitor {
254 t: self.format_type,
255 f,
256 indent: 0,
257 show_metrics: self.show_metrics,
258 show_statistics: self.show_statistics,
259 show_schema: self.show_schema,
260 metric_types: &self.metric_types,
261 metric_categories: self.metric_categories.as_deref(),
262 };
263 accept(self.plan, &mut visitor)
264 }
265 }
266 Wrapper {
267 format_type,
268 plan: self.inner,
269 show_metrics: self.show_metrics,
270 show_statistics: self.show_statistics,
271 show_schema: self.show_schema,
272 metric_types: self.metric_types.clone(),
273 metric_categories: self.metric_categories.clone(),
274 }
275 }
276
277 pub fn graphviz(&self) -> impl fmt::Display + 'a {
289 struct Wrapper<'a> {
290 plan: &'a dyn ExecutionPlan,
291 show_metrics: ShowMetrics,
292 show_statistics: bool,
293 metric_types: Vec<MetricType>,
294 metric_categories: Option<Vec<MetricCategory>>,
295 }
296 impl fmt::Display for Wrapper<'_> {
297 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
298 let t = DisplayFormatType::Default;
299
300 let mut visitor = GraphvizVisitor {
301 f,
302 t,
303 show_metrics: self.show_metrics,
304 show_statistics: self.show_statistics,
305 metric_types: &self.metric_types,
306 metric_categories: self.metric_categories.as_deref(),
307 graphviz_builder: GraphvizBuilder::default(),
308 parents: Vec::new(),
309 };
310
311 visitor.start_graph()?;
312
313 accept(self.plan, &mut visitor)?;
314
315 visitor.end_graph()?;
316 Ok(())
317 }
318 }
319
320 Wrapper {
321 plan: self.inner,
322 show_metrics: self.show_metrics,
323 show_statistics: self.show_statistics,
324 metric_types: self.metric_types.clone(),
325 metric_categories: self.metric_categories.clone(),
326 }
327 }
328
329 pub fn tree_render(&self) -> impl fmt::Display + 'a {
333 struct Wrapper<'a> {
334 plan: &'a dyn ExecutionPlan,
335 maximum_render_width: usize,
336 }
337 impl fmt::Display for Wrapper<'_> {
338 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
339 let mut visitor = TreeRenderVisitor {
340 f,
341 maximum_render_width: self.maximum_render_width,
342 };
343 visitor.visit(self.plan)
344 }
345 }
346 Wrapper {
347 plan: self.inner,
348 maximum_render_width: self.tree_maximum_render_width,
349 }
350 }
351
352 pub fn one_line(&self) -> impl fmt::Display + 'a {
355 struct Wrapper<'a> {
356 plan: &'a dyn ExecutionPlan,
357 show_metrics: ShowMetrics,
358 show_statistics: bool,
359 show_schema: bool,
360 metric_types: Vec<MetricType>,
361 metric_categories: Option<Vec<MetricCategory>>,
362 }
363
364 impl fmt::Display for Wrapper<'_> {
365 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
366 let mut visitor = IndentVisitor {
367 f,
368 t: DisplayFormatType::Default,
369 indent: 0,
370 show_metrics: self.show_metrics,
371 show_statistics: self.show_statistics,
372 show_schema: self.show_schema,
373 metric_types: &self.metric_types,
374 metric_categories: self.metric_categories.as_deref(),
375 };
376 visitor.pre_visit(self.plan)?;
377 Ok(())
378 }
379 }
380
381 Wrapper {
382 plan: self.inner,
383 show_metrics: self.show_metrics,
384 show_statistics: self.show_statistics,
385 show_schema: self.show_schema,
386 metric_types: self.metric_types.clone(),
387 metric_categories: self.metric_categories.clone(),
388 }
389 }
390
391 #[deprecated(since = "47.0.0", note = "indent() or tree_render() instead")]
392 pub fn to_stringified(
393 &self,
394 verbose: bool,
395 plan_type: PlanType,
396 explain_format: DisplayFormatType,
397 ) -> StringifiedPlan {
398 match (&explain_format, &plan_type) {
399 (DisplayFormatType::TreeRender, PlanType::FinalPhysicalPlan) => {
400 StringifiedPlan::new(plan_type, self.tree_render().to_string())
401 }
402 _ => StringifiedPlan::new(plan_type, self.indent(verbose).to_string()),
403 }
404 }
405}
406
407#[derive(Debug, Clone, Copy)]
409enum ShowMetrics {
410 None,
412
413 Aggregated,
415
416 Full,
418}
419
420struct IndentVisitor<'a, 'b> {
430 t: DisplayFormatType,
432 f: &'a mut Formatter<'b>,
434 indent: usize,
436 show_metrics: ShowMetrics,
438 show_statistics: bool,
440 show_schema: bool,
442 metric_types: &'a [MetricType],
444 metric_categories: Option<&'a [MetricCategory]>,
446}
447
448impl ExecutionPlanVisitor for IndentVisitor<'_, '_> {
449 type Error = fmt::Error;
450 fn pre_visit(&mut self, plan: &dyn ExecutionPlan) -> Result<bool, Self::Error> {
451 write!(self.f, "{:indent$}", "", indent = self.indent * 2)?;
452 plan.fmt_as(self.t, self.f)?;
453 match self.show_metrics {
454 ShowMetrics::None => {}
455 ShowMetrics::Aggregated => {
456 if let Some(metrics) = plan.metrics() {
457 let mut metrics = metrics
458 .filter_by_metric_types(self.metric_types)
459 .aggregate_by_name()
460 .sorted_for_display()
461 .timestamps_removed();
462 if let Some(cats) = self.metric_categories {
463 metrics = metrics.filter_by_categories(cats);
464 }
465 write!(self.f, ", metrics=[{metrics}]")?;
466 } else {
467 write!(self.f, ", metrics=[]")?;
468 }
469 }
470 ShowMetrics::Full => {
471 if let Some(metrics) = plan.metrics() {
472 let mut metrics = metrics.filter_by_metric_types(self.metric_types);
473 if let Some(cats) = self.metric_categories {
474 metrics = metrics.filter_by_categories(cats);
475 }
476 write!(self.f, ", metrics=[{metrics}]")?;
477 } else {
478 write!(self.f, ", metrics=[]")?;
479 }
480 }
481 }
482 if self.show_statistics {
483 let stats = plan.partition_statistics(None).map_err(|_e| fmt::Error)?;
484 write!(self.f, ", statistics=[{stats}]")?;
485 }
486 if self.show_schema {
487 write!(
488 self.f,
489 ", schema={}",
490 display_schema(plan.schema().as_ref())
491 )?;
492 }
493 writeln!(self.f)?;
494 self.indent += 1;
495 Ok(true)
496 }
497
498 fn post_visit(&mut self, _plan: &dyn ExecutionPlan) -> Result<bool, Self::Error> {
499 self.indent -= 1;
500 Ok(true)
501 }
502}
503
504struct GraphvizVisitor<'a, 'b> {
505 f: &'a mut Formatter<'b>,
506 t: DisplayFormatType,
508 show_metrics: ShowMetrics,
510 show_statistics: bool,
512 metric_types: &'a [MetricType],
514 metric_categories: Option<&'a [MetricCategory]>,
516
517 graphviz_builder: GraphvizBuilder,
518 parents: Vec<usize>,
520}
521
522impl GraphvizVisitor<'_, '_> {
523 fn start_graph(&mut self) -> fmt::Result {
524 self.graphviz_builder.start_graph(self.f)
525 }
526
527 fn end_graph(&mut self) -> fmt::Result {
528 self.graphviz_builder.end_graph(self.f)
529 }
530}
531
532impl ExecutionPlanVisitor for GraphvizVisitor<'_, '_> {
533 type Error = fmt::Error;
534
535 fn pre_visit(&mut self, plan: &dyn ExecutionPlan) -> Result<bool, Self::Error> {
536 let id = self.graphviz_builder.next_id();
537
538 struct Wrapper<'a>(&'a dyn ExecutionPlan, DisplayFormatType);
539
540 impl fmt::Display for Wrapper<'_> {
541 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
542 self.0.fmt_as(self.1, f)
543 }
544 }
545
546 let label = { format!("{}", Wrapper(plan, self.t)) };
547
548 let metrics = match self.show_metrics {
549 ShowMetrics::None => "".to_string(),
550 ShowMetrics::Aggregated => {
551 if let Some(metrics) = plan.metrics() {
552 let mut metrics = metrics
553 .filter_by_metric_types(self.metric_types)
554 .aggregate_by_name()
555 .sorted_for_display()
556 .timestamps_removed();
557 if let Some(cats) = self.metric_categories {
558 metrics = metrics.filter_by_categories(cats);
559 }
560 format!("metrics=[{metrics}]")
561 } else {
562 "metrics=[]".to_string()
563 }
564 }
565 ShowMetrics::Full => {
566 if let Some(metrics) = plan.metrics() {
567 let mut metrics = metrics.filter_by_metric_types(self.metric_types);
568 if let Some(cats) = self.metric_categories {
569 metrics = metrics.filter_by_categories(cats);
570 }
571 format!("metrics=[{metrics}]")
572 } else {
573 "metrics=[]".to_string()
574 }
575 }
576 };
577
578 let statistics = if self.show_statistics {
579 let stats = plan.partition_statistics(None).map_err(|_e| fmt::Error)?;
580 format!("statistics=[{stats}]")
581 } else {
582 "".to_string()
583 };
584
585 let delimiter = if !metrics.is_empty() && !statistics.is_empty() {
586 ", "
587 } else {
588 ""
589 };
590
591 self.graphviz_builder.add_node(
592 self.f,
593 id,
594 &label,
595 Some(&format!("{metrics}{delimiter}{statistics}")),
596 )?;
597
598 if let Some(parent_node_id) = self.parents.last() {
599 self.graphviz_builder
600 .add_edge(self.f, *parent_node_id, id)?;
601 }
602
603 self.parents.push(id);
604
605 Ok(true)
606 }
607
608 fn post_visit(&mut self, _plan: &dyn ExecutionPlan) -> Result<bool, Self::Error> {
609 self.parents.pop();
610 Ok(true)
611 }
612}
613
614struct TreeRenderVisitor<'a, 'b> {
638 f: &'a mut Formatter<'b>,
640 maximum_render_width: usize,
642}
643
644impl TreeRenderVisitor<'_, '_> {
645 const LTCORNER: &'static str = "┌"; const RTCORNER: &'static str = "┐"; const LDCORNER: &'static str = "└"; const RDCORNER: &'static str = "┘"; const TMIDDLE: &'static str = "┬"; const LMIDDLE: &'static str = "├"; const DMIDDLE: &'static str = "┴"; const VERTICAL: &'static str = "│"; const HORIZONTAL: &'static str = "─"; const NODE_RENDER_WIDTH: usize = 29; const MAX_EXTRA_LINES: usize = 30; pub fn visit(&mut self, plan: &dyn ExecutionPlan) -> Result<(), fmt::Error> {
668 let root = RenderTree::create_tree(plan);
669
670 for y in 0..root.height {
671 self.render_top_layer(&root, y)?;
673 self.render_box_content(&root, y)?;
675 self.render_bottom_layer(&root, y)?;
677 }
678
679 Ok(())
680 }
681
682 fn render_top_layer(
688 &mut self,
689 root: &RenderTree,
690 y: usize,
691 ) -> Result<(), fmt::Error> {
692 for x in 0..root.width {
693 if self.maximum_render_width > 0
694 && x * Self::NODE_RENDER_WIDTH >= self.maximum_render_width
695 {
696 break;
697 }
698
699 if root.has_node(x, y) {
700 write!(self.f, "{}", Self::LTCORNER)?;
701 write!(
702 self.f,
703 "{}",
704 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2 - 1)
705 )?;
706 if y == 0 {
707 write!(self.f, "{}", Self::HORIZONTAL)?;
709 } else {
710 write!(self.f, "{}", Self::DMIDDLE)?;
712 }
713 write!(
714 self.f,
715 "{}",
716 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2 - 1)
717 )?;
718 write!(self.f, "{}", Self::RTCORNER)?;
719 } else {
720 let mut has_adjacent_nodes = false;
721 for i in 0..(root.width - x) {
722 has_adjacent_nodes = has_adjacent_nodes || root.has_node(x + i, y);
723 }
724 if !has_adjacent_nodes {
725 continue;
728 }
729 write!(self.f, "{}", &" ".repeat(Self::NODE_RENDER_WIDTH))?;
731 }
732 }
733 writeln!(self.f)?;
734
735 Ok(())
736 }
737
738 fn render_box_content(
744 &mut self,
745 root: &RenderTree,
746 y: usize,
747 ) -> Result<(), fmt::Error> {
748 let mut extra_info: Vec<Vec<String>> = vec![vec![]; root.width];
749 let mut extra_height = 0;
750
751 for (x, extra_info_item) in extra_info.iter_mut().enumerate().take(root.width) {
752 if let Some(node) = root.get_node(x, y) {
753 Self::split_up_extra_info(
754 &node.extra_text,
755 extra_info_item,
756 Self::MAX_EXTRA_LINES,
757 );
758 if extra_info_item.len() > extra_height {
759 extra_height = extra_info_item.len();
760 }
761 }
762 }
763
764 let halfway_point = extra_height.div_ceil(2);
765
766 for render_y in 0..=extra_height {
768 for (x, _) in root.nodes.iter().enumerate().take(root.width) {
769 if self.maximum_render_width > 0
770 && x * Self::NODE_RENDER_WIDTH >= self.maximum_render_width
771 {
772 break;
773 }
774
775 let mut has_adjacent_nodes = false;
776 for i in 0..(root.width - x) {
777 has_adjacent_nodes = has_adjacent_nodes || root.has_node(x + i, y);
778 }
779
780 if let Some(node) = root.get_node(x, y) {
781 write!(self.f, "{}", Self::VERTICAL)?;
782
783 let mut render_text = if render_y == 0 {
785 node.name.clone()
786 } else if render_y <= extra_info[x].len() {
787 extra_info[x][render_y - 1].clone()
788 } else {
789 String::new()
790 };
791
792 render_text = Self::adjust_text_for_rendering(
793 &render_text,
794 Self::NODE_RENDER_WIDTH - 2,
795 );
796 write!(self.f, "{render_text}")?;
797
798 if render_y == halfway_point && node.child_positions.len() > 1 {
799 write!(self.f, "{}", Self::LMIDDLE)?;
800 } else {
801 write!(self.f, "{}", Self::VERTICAL)?;
802 }
803 } else if render_y == halfway_point {
804 let has_child_to_the_right =
805 Self::should_render_whitespace(root, x, y);
806 if root.has_node(x, y + 1) {
807 write!(
809 self.f,
810 "{}",
811 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2)
812 )?;
813 if has_child_to_the_right {
814 write!(self.f, "{}", Self::TMIDDLE)?;
815 write!(
817 self.f,
818 "{}",
819 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2)
820 )?;
821 } else {
822 write!(self.f, "{}", Self::RTCORNER)?;
823 if has_adjacent_nodes {
824 write!(
826 self.f,
827 "{}",
828 " ".repeat(Self::NODE_RENDER_WIDTH / 2)
829 )?;
830 }
831 }
832 } else if has_child_to_the_right {
833 write!(
836 self.f,
837 "{}",
838 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH)
839 )?;
840 } else if has_adjacent_nodes {
841 write!(self.f, "{}", " ".repeat(Self::NODE_RENDER_WIDTH))?;
843 }
844 } else if render_y >= halfway_point {
845 if root.has_node(x, y + 1) {
846 write!(
848 self.f,
849 "{}{}",
850 " ".repeat(Self::NODE_RENDER_WIDTH / 2),
851 Self::VERTICAL
852 )?;
853 if has_adjacent_nodes
854 || Self::should_render_whitespace(root, x, y)
855 {
856 write!(
857 self.f,
858 "{}",
859 " ".repeat(Self::NODE_RENDER_WIDTH / 2)
860 )?;
861 }
862 } else if has_adjacent_nodes
863 || Self::should_render_whitespace(root, x, y)
864 {
865 write!(self.f, "{}", " ".repeat(Self::NODE_RENDER_WIDTH))?;
867 }
868 } else if has_adjacent_nodes {
869 write!(self.f, "{}", " ".repeat(Self::NODE_RENDER_WIDTH))?;
871 }
872 }
873 writeln!(self.f)?;
874 }
875
876 Ok(())
877 }
878
879 fn render_bottom_layer(
885 &mut self,
886 root: &RenderTree,
887 y: usize,
888 ) -> Result<(), fmt::Error> {
889 for x in 0..=root.width {
890 if self.maximum_render_width > 0
891 && x * Self::NODE_RENDER_WIDTH >= self.maximum_render_width
892 {
893 break;
894 }
895 let mut has_adjacent_nodes = false;
896 for i in 0..(root.width - x) {
897 has_adjacent_nodes = has_adjacent_nodes || root.has_node(x + i, y);
898 }
899 if root.get_node(x, y).is_some() {
900 write!(self.f, "{}", Self::LDCORNER)?;
901 write!(
902 self.f,
903 "{}",
904 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2 - 1)
905 )?;
906 if root.has_node(x, y + 1) {
907 write!(self.f, "{}", Self::TMIDDLE)?;
909 } else {
910 write!(self.f, "{}", Self::HORIZONTAL)?;
912 }
913 write!(
914 self.f,
915 "{}",
916 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2 - 1)
917 )?;
918 write!(self.f, "{}", Self::RDCORNER)?;
919 } else if root.has_node(x, y + 1) {
920 write!(self.f, "{}", &" ".repeat(Self::NODE_RENDER_WIDTH / 2))?;
921 write!(self.f, "{}", Self::VERTICAL)?;
922 if has_adjacent_nodes || Self::should_render_whitespace(root, x, y) {
923 write!(self.f, "{}", &" ".repeat(Self::NODE_RENDER_WIDTH / 2))?;
924 }
925 } else if has_adjacent_nodes || Self::should_render_whitespace(root, x, y) {
926 write!(self.f, "{}", &" ".repeat(Self::NODE_RENDER_WIDTH))?;
927 }
928 }
929 writeln!(self.f)?;
930
931 Ok(())
932 }
933
934 fn extra_info_separator() -> String {
935 "-".repeat(Self::NODE_RENDER_WIDTH - 9)
936 }
937
938 fn remove_padding(s: &str) -> String {
939 s.trim().to_string()
940 }
941
942 pub fn split_up_extra_info(
943 extra_info: &HashMap<String, String>,
944 result: &mut Vec<String>,
945 max_lines: usize,
946 ) {
947 if extra_info.is_empty() {
948 return;
949 }
950
951 result.push(Self::extra_info_separator());
952
953 let mut requires_padding = false;
954 let mut was_inlined = false;
955
956 let sorted_extra_info: BTreeMap<_, _> = extra_info.iter().collect();
958 for (key, value) in sorted_extra_info {
959 let mut str = Self::remove_padding(value);
960 let mut is_inlined = false;
961 let available_width = Self::NODE_RENDER_WIDTH - 7;
962 let total_size = key.len() + str.len() + 2;
963 let is_multiline = str.contains('\n');
964
965 if str.is_empty() {
966 str = key.to_string();
967 } else if !is_multiline && total_size < available_width {
968 str = format!("{key}: {str}");
969 is_inlined = true;
970 } else {
971 str = format!("{key}:\n{str}");
972 }
973
974 if is_inlined && was_inlined {
975 requires_padding = false;
976 }
977
978 if requires_padding {
979 result.push(String::new());
980 }
981
982 let mut splits: Vec<String> = str.split('\n').map(String::from).collect();
983 if splits.len() > max_lines {
984 let mut truncated_splits = Vec::new();
985 for split in splits.iter().take(max_lines / 2) {
986 truncated_splits.push(split.clone());
987 }
988 truncated_splits.push("...".to_string());
989 for split in splits.iter().skip(splits.len() - max_lines / 2) {
990 truncated_splits.push(split.clone());
991 }
992 splits = truncated_splits;
993 }
994 for split in splits {
995 Self::split_string_buffer(&split, result);
996 }
997 if result.len() > max_lines {
998 result.truncate(max_lines);
999 result.push("...".to_string());
1000 }
1001
1002 requires_padding = true;
1003 was_inlined = is_inlined;
1004 }
1005 }
1006
1007 fn adjust_text_for_rendering(source: &str, max_render_width: usize) -> String {
1011 let render_width = source.chars().count();
1012 if render_width > max_render_width {
1013 let truncated = &source[..max_render_width - 3];
1014 format!("{truncated}...")
1015 } else {
1016 let total_spaces = max_render_width - render_width;
1017 let half_spaces = total_spaces / 2;
1018 let extra_left_space = if total_spaces.is_multiple_of(2) { 0 } else { 1 };
1019 format!(
1020 "{}{}{}",
1021 " ".repeat(half_spaces + extra_left_space),
1022 source,
1023 " ".repeat(half_spaces)
1024 )
1025 }
1026 }
1027
1028 fn should_render_whitespace(root: &RenderTree, x: usize, y: usize) -> bool {
1034 let mut found_children = 0;
1035
1036 for i in (0..=x).rev() {
1037 let node = root.get_node(i, y);
1038 if root.has_node(i, y + 1) {
1039 found_children += 1;
1040 }
1041 if let Some(node) = node {
1042 if node.child_positions.len() > 1
1043 && found_children < node.child_positions.len()
1044 {
1045 return true;
1046 }
1047
1048 return false;
1049 }
1050 }
1051
1052 false
1053 }
1054
1055 fn split_string_buffer(source: &str, result: &mut Vec<String>) {
1056 let mut character_pos = 0;
1057 let mut start_pos = 0;
1058 let mut render_width = 0;
1059 let mut last_possible_split = 0;
1060
1061 let chars: Vec<char> = source.chars().collect();
1062
1063 while character_pos < chars.len() {
1064 let char_width = 1;
1066
1067 if render_width + char_width > Self::NODE_RENDER_WIDTH - 2 {
1069 if start_pos + 8 > last_possible_split {
1070 last_possible_split = character_pos;
1073 }
1074
1075 result.push(source[start_pos..last_possible_split].to_string());
1076 render_width = character_pos - last_possible_split;
1077 start_pos = last_possible_split;
1078 character_pos = last_possible_split;
1079 }
1080
1081 if Self::can_split_on_this_char(chars[character_pos]) {
1083 last_possible_split = character_pos;
1084 }
1085
1086 character_pos += 1;
1087 render_width += char_width;
1088 }
1089
1090 if source.len() > start_pos {
1091 result.push(source[start_pos..].to_string());
1093 }
1094 }
1095
1096 fn can_split_on_this_char(c: char) -> bool {
1097 (!c.is_ascii_digit() && !c.is_ascii_uppercase() && !c.is_ascii_lowercase())
1098 && c != '_'
1099 }
1100}
1101
1102pub trait DisplayAs {
1104 fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result;
1109}
1110
1111pub struct DefaultDisplay<T>(pub T);
1113
1114impl<T: DisplayAs> fmt::Display for DefaultDisplay<T> {
1115 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1116 self.0.fmt_as(DisplayFormatType::Default, f)
1117 }
1118}
1119
1120pub struct VerboseDisplay<T>(pub T);
1122
1123impl<T: DisplayAs> fmt::Display for VerboseDisplay<T> {
1124 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1125 self.0.fmt_as(DisplayFormatType::Verbose, f)
1126 }
1127}
1128
1129#[derive(Debug)]
1131pub struct ProjectSchemaDisplay<'a>(pub &'a SchemaRef);
1132
1133impl fmt::Display for ProjectSchemaDisplay<'_> {
1134 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1135 let parts: Vec<_> = self
1136 .0
1137 .fields()
1138 .iter()
1139 .map(|x| x.name().to_owned())
1140 .collect::<Vec<String>>();
1141 write!(f, "[{}]", parts.join(", "))
1142 }
1143}
1144
1145pub fn display_orderings(f: &mut Formatter, orderings: &[LexOrdering]) -> fmt::Result {
1146 if !orderings.is_empty() {
1147 let start = if orderings.len() == 1 {
1148 ", output_ordering="
1149 } else {
1150 ", output_orderings=["
1151 };
1152 write!(f, "{start}")?;
1153 for (idx, ordering) in orderings.iter().enumerate() {
1154 match idx {
1155 0 => write!(f, "[{ordering}]")?,
1156 _ => write!(f, ", [{ordering}]")?,
1157 }
1158 }
1159 let end = if orderings.len() == 1 { "" } else { "]" };
1160 write!(f, "{end}")?;
1161 }
1162 Ok(())
1163}
1164
1165#[cfg(test)]
1166mod tests {
1167 use std::fmt::Write;
1168 use std::sync::Arc;
1169
1170 use datafusion_common::{Result, Statistics, internal_datafusion_err};
1171 use datafusion_execution::{SendableRecordBatchStream, TaskContext};
1172
1173 use crate::{DisplayAs, ExecutionPlan, PlanProperties};
1174
1175 use super::DisplayableExecutionPlan;
1176
1177 #[derive(Debug, Clone, Copy)]
1178 enum TestStatsExecPlan {
1179 Panic,
1180 Error,
1181 Ok,
1182 }
1183
1184 impl DisplayAs for TestStatsExecPlan {
1185 fn fmt_as(
1186 &self,
1187 _t: crate::DisplayFormatType,
1188 f: &mut std::fmt::Formatter,
1189 ) -> std::fmt::Result {
1190 write!(f, "TestStatsExecPlan")
1191 }
1192 }
1193
1194 impl ExecutionPlan for TestStatsExecPlan {
1195 fn name(&self) -> &'static str {
1196 "TestStatsExecPlan"
1197 }
1198
1199 fn properties(&self) -> &Arc<PlanProperties> {
1200 unimplemented!()
1201 }
1202
1203 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
1204 vec![]
1205 }
1206
1207 fn with_new_children(
1208 self: Arc<Self>,
1209 _: Vec<Arc<dyn ExecutionPlan>>,
1210 ) -> Result<Arc<dyn ExecutionPlan>> {
1211 unimplemented!()
1212 }
1213
1214 fn execute(
1215 &self,
1216 _: usize,
1217 _: Arc<TaskContext>,
1218 ) -> Result<SendableRecordBatchStream> {
1219 todo!()
1220 }
1221
1222 fn partition_statistics(
1223 &self,
1224 partition: Option<usize>,
1225 ) -> Result<Arc<Statistics>> {
1226 if partition.is_some() {
1227 return Ok(Arc::new(Statistics::new_unknown(self.schema().as_ref())));
1228 }
1229 match self {
1230 Self::Panic => panic!("expected panic"),
1231 Self::Error => Err(internal_datafusion_err!("expected error")),
1232 Self::Ok => Ok(Arc::new(Statistics::new_unknown(self.schema().as_ref()))),
1233 }
1234 }
1235 }
1236
1237 fn test_stats_display(exec: TestStatsExecPlan, show_stats: bool) {
1238 let display =
1239 DisplayableExecutionPlan::new(&exec).set_show_statistics(show_stats);
1240
1241 let mut buf = String::new();
1242 write!(&mut buf, "{}", display.one_line()).unwrap();
1243 let buf = buf.trim();
1244 assert_eq!(buf, "TestStatsExecPlan");
1245 }
1246
1247 #[test]
1248 fn test_display_when_stats_panic_with_no_show_stats() {
1249 test_stats_display(TestStatsExecPlan::Panic, false);
1250 }
1251
1252 #[test]
1253 fn test_display_when_stats_error_with_no_show_stats() {
1254 test_stats_display(TestStatsExecPlan::Error, false);
1255 }
1256
1257 #[test]
1258 fn test_display_when_stats_ok_with_no_show_stats() {
1259 test_stats_display(TestStatsExecPlan::Ok, false);
1260 }
1261
1262 #[test]
1263 #[should_panic(expected = "expected panic")]
1264 fn test_display_when_stats_panic_with_show_stats() {
1265 test_stats_display(TestStatsExecPlan::Panic, true);
1266 }
1267
1268 #[test]
1269 #[should_panic(expected = "Error")] fn test_display_when_stats_error_with_show_stats() {
1271 test_stats_display(TestStatsExecPlan::Error, true);
1272 }
1273
1274 #[test]
1275 fn test_display_when_stats_ok_with_show_stats() {
1276 test_stats_display(TestStatsExecPlan::Ok, false);
1277 }
1278}