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::MetricType;
32use crate::render_tree::RenderTree;
33
34use super::{accept, ExecutionPlan, ExecutionPlanVisitor};
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 tree_maximum_render_width: usize,
128}
129
130impl<'a> DisplayableExecutionPlan<'a> {
131 fn default_metric_types() -> Vec<MetricType> {
132 vec![MetricType::SUMMARY, MetricType::DEV]
133 }
134
135 pub fn new(inner: &'a dyn ExecutionPlan) -> Self {
138 Self {
139 inner,
140 show_metrics: ShowMetrics::None,
141 show_statistics: false,
142 show_schema: false,
143 metric_types: Self::default_metric_types(),
144 tree_maximum_render_width: 240,
145 }
146 }
147
148 pub fn with_metrics(inner: &'a dyn ExecutionPlan) -> Self {
152 Self {
153 inner,
154 show_metrics: ShowMetrics::Aggregated,
155 show_statistics: false,
156 show_schema: false,
157 metric_types: Self::default_metric_types(),
158 tree_maximum_render_width: 240,
159 }
160 }
161
162 pub fn with_full_metrics(inner: &'a dyn ExecutionPlan) -> Self {
166 Self {
167 inner,
168 show_metrics: ShowMetrics::Full,
169 show_statistics: false,
170 show_schema: false,
171 metric_types: Self::default_metric_types(),
172 tree_maximum_render_width: 240,
173 }
174 }
175
176 pub fn set_show_schema(mut self, show_schema: bool) -> Self {
181 self.show_schema = show_schema;
182 self
183 }
184
185 pub fn set_show_statistics(mut self, show_statistics: bool) -> Self {
187 self.show_statistics = show_statistics;
188 self
189 }
190
191 pub fn set_metric_types(mut self, metric_types: Vec<MetricType>) -> Self {
193 self.metric_types = metric_types;
194 self
195 }
196
197 pub fn set_tree_maximum_render_width(mut self, width: usize) -> Self {
199 self.tree_maximum_render_width = width;
200 self
201 }
202
203 pub fn indent(&self, verbose: bool) -> impl fmt::Display + 'a {
214 let format_type = if verbose {
215 DisplayFormatType::Verbose
216 } else {
217 DisplayFormatType::Default
218 };
219 struct Wrapper<'a> {
220 format_type: DisplayFormatType,
221 plan: &'a dyn ExecutionPlan,
222 show_metrics: ShowMetrics,
223 show_statistics: bool,
224 show_schema: bool,
225 metric_types: Vec<MetricType>,
226 }
227 impl fmt::Display for Wrapper<'_> {
228 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
229 let mut visitor = IndentVisitor {
230 t: self.format_type,
231 f,
232 indent: 0,
233 show_metrics: self.show_metrics,
234 show_statistics: self.show_statistics,
235 show_schema: self.show_schema,
236 metric_types: &self.metric_types,
237 };
238 accept(self.plan, &mut visitor)
239 }
240 }
241 Wrapper {
242 format_type,
243 plan: self.inner,
244 show_metrics: self.show_metrics,
245 show_statistics: self.show_statistics,
246 show_schema: self.show_schema,
247 metric_types: self.metric_types.clone(),
248 }
249 }
250
251 pub fn graphviz(&self) -> impl fmt::Display + 'a {
263 struct Wrapper<'a> {
264 plan: &'a dyn ExecutionPlan,
265 show_metrics: ShowMetrics,
266 show_statistics: bool,
267 metric_types: Vec<MetricType>,
268 }
269 impl fmt::Display for Wrapper<'_> {
270 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
271 let t = DisplayFormatType::Default;
272
273 let mut visitor = GraphvizVisitor {
274 f,
275 t,
276 show_metrics: self.show_metrics,
277 show_statistics: self.show_statistics,
278 metric_types: &self.metric_types,
279 graphviz_builder: GraphvizBuilder::default(),
280 parents: Vec::new(),
281 };
282
283 visitor.start_graph()?;
284
285 accept(self.plan, &mut visitor)?;
286
287 visitor.end_graph()?;
288 Ok(())
289 }
290 }
291
292 Wrapper {
293 plan: self.inner,
294 show_metrics: self.show_metrics,
295 show_statistics: self.show_statistics,
296 metric_types: self.metric_types.clone(),
297 }
298 }
299
300 pub fn tree_render(&self) -> impl fmt::Display + 'a {
304 struct Wrapper<'a> {
305 plan: &'a dyn ExecutionPlan,
306 maximum_render_width: usize,
307 }
308 impl fmt::Display for Wrapper<'_> {
309 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
310 let mut visitor = TreeRenderVisitor {
311 f,
312 maximum_render_width: self.maximum_render_width,
313 };
314 visitor.visit(self.plan)
315 }
316 }
317 Wrapper {
318 plan: self.inner,
319 maximum_render_width: self.tree_maximum_render_width,
320 }
321 }
322
323 pub fn one_line(&self) -> impl fmt::Display + 'a {
326 struct Wrapper<'a> {
327 plan: &'a dyn ExecutionPlan,
328 show_metrics: ShowMetrics,
329 show_statistics: bool,
330 show_schema: bool,
331 metric_types: Vec<MetricType>,
332 }
333
334 impl fmt::Display for Wrapper<'_> {
335 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
336 let mut visitor = IndentVisitor {
337 f,
338 t: DisplayFormatType::Default,
339 indent: 0,
340 show_metrics: self.show_metrics,
341 show_statistics: self.show_statistics,
342 show_schema: self.show_schema,
343 metric_types: &self.metric_types,
344 };
345 visitor.pre_visit(self.plan)?;
346 Ok(())
347 }
348 }
349
350 Wrapper {
351 plan: self.inner,
352 show_metrics: self.show_metrics,
353 show_statistics: self.show_statistics,
354 show_schema: self.show_schema,
355 metric_types: self.metric_types.clone(),
356 }
357 }
358
359 #[deprecated(since = "47.0.0", note = "indent() or tree_render() instead")]
360 pub fn to_stringified(
361 &self,
362 verbose: bool,
363 plan_type: PlanType,
364 explain_format: DisplayFormatType,
365 ) -> StringifiedPlan {
366 match (&explain_format, &plan_type) {
367 (DisplayFormatType::TreeRender, PlanType::FinalPhysicalPlan) => {
368 StringifiedPlan::new(plan_type, self.tree_render().to_string())
369 }
370 _ => StringifiedPlan::new(plan_type, self.indent(verbose).to_string()),
371 }
372 }
373}
374
375#[derive(Debug, Clone, Copy)]
377enum ShowMetrics {
378 None,
380
381 Aggregated,
383
384 Full,
386}
387
388struct IndentVisitor<'a, 'b> {
398 t: DisplayFormatType,
400 f: &'a mut Formatter<'b>,
402 indent: usize,
404 show_metrics: ShowMetrics,
406 show_statistics: bool,
408 show_schema: bool,
410 metric_types: &'a [MetricType],
412}
413
414impl ExecutionPlanVisitor for IndentVisitor<'_, '_> {
415 type Error = fmt::Error;
416 fn pre_visit(&mut self, plan: &dyn ExecutionPlan) -> Result<bool, Self::Error> {
417 write!(self.f, "{:indent$}", "", indent = self.indent * 2)?;
418 plan.fmt_as(self.t, self.f)?;
419 match self.show_metrics {
420 ShowMetrics::None => {}
421 ShowMetrics::Aggregated => {
422 if let Some(metrics) = plan.metrics() {
423 let metrics = metrics
424 .filter_by_metric_types(self.metric_types)
425 .aggregate_by_name()
426 .sorted_for_display()
427 .timestamps_removed();
428
429 write!(self.f, ", metrics=[{metrics}]")?;
430 } else {
431 write!(self.f, ", metrics=[]")?;
432 }
433 }
434 ShowMetrics::Full => {
435 if let Some(metrics) = plan.metrics() {
436 let metrics = metrics.filter_by_metric_types(self.metric_types);
437 write!(self.f, ", metrics=[{metrics}]")?;
438 } else {
439 write!(self.f, ", metrics=[]")?;
440 }
441 }
442 }
443 if self.show_statistics {
444 let stats = plan.partition_statistics(None).map_err(|_e| fmt::Error)?;
445 write!(self.f, ", statistics=[{stats}]")?;
446 }
447 if self.show_schema {
448 write!(
449 self.f,
450 ", schema={}",
451 display_schema(plan.schema().as_ref())
452 )?;
453 }
454 writeln!(self.f)?;
455 self.indent += 1;
456 Ok(true)
457 }
458
459 fn post_visit(&mut self, _plan: &dyn ExecutionPlan) -> Result<bool, Self::Error> {
460 self.indent -= 1;
461 Ok(true)
462 }
463}
464
465struct GraphvizVisitor<'a, 'b> {
466 f: &'a mut Formatter<'b>,
467 t: DisplayFormatType,
469 show_metrics: ShowMetrics,
471 show_statistics: bool,
473 metric_types: &'a [MetricType],
475
476 graphviz_builder: GraphvizBuilder,
477 parents: Vec<usize>,
479}
480
481impl GraphvizVisitor<'_, '_> {
482 fn start_graph(&mut self) -> fmt::Result {
483 self.graphviz_builder.start_graph(self.f)
484 }
485
486 fn end_graph(&mut self) -> fmt::Result {
487 self.graphviz_builder.end_graph(self.f)
488 }
489}
490
491impl ExecutionPlanVisitor for GraphvizVisitor<'_, '_> {
492 type Error = fmt::Error;
493
494 fn pre_visit(&mut self, plan: &dyn ExecutionPlan) -> Result<bool, Self::Error> {
495 let id = self.graphviz_builder.next_id();
496
497 struct Wrapper<'a>(&'a dyn ExecutionPlan, DisplayFormatType);
498
499 impl fmt::Display for Wrapper<'_> {
500 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
501 self.0.fmt_as(self.1, f)
502 }
503 }
504
505 let label = { format!("{}", Wrapper(plan, self.t)) };
506
507 let metrics = match self.show_metrics {
508 ShowMetrics::None => "".to_string(),
509 ShowMetrics::Aggregated => {
510 if let Some(metrics) = plan.metrics() {
511 let metrics = metrics
512 .filter_by_metric_types(self.metric_types)
513 .aggregate_by_name()
514 .sorted_for_display()
515 .timestamps_removed();
516
517 format!("metrics=[{metrics}]")
518 } else {
519 "metrics=[]".to_string()
520 }
521 }
522 ShowMetrics::Full => {
523 if let Some(metrics) = plan.metrics() {
524 let metrics = metrics.filter_by_metric_types(self.metric_types);
525 format!("metrics=[{metrics}]")
526 } else {
527 "metrics=[]".to_string()
528 }
529 }
530 };
531
532 let statistics = if self.show_statistics {
533 let stats = plan.partition_statistics(None).map_err(|_e| fmt::Error)?;
534 format!("statistics=[{stats}]")
535 } else {
536 "".to_string()
537 };
538
539 let delimiter = if !metrics.is_empty() && !statistics.is_empty() {
540 ", "
541 } else {
542 ""
543 };
544
545 self.graphviz_builder.add_node(
546 self.f,
547 id,
548 &label,
549 Some(&format!("{metrics}{delimiter}{statistics}")),
550 )?;
551
552 if let Some(parent_node_id) = self.parents.last() {
553 self.graphviz_builder
554 .add_edge(self.f, *parent_node_id, id)?;
555 }
556
557 self.parents.push(id);
558
559 Ok(true)
560 }
561
562 fn post_visit(&mut self, _plan: &dyn ExecutionPlan) -> Result<bool, Self::Error> {
563 self.parents.pop();
564 Ok(true)
565 }
566}
567
568struct TreeRenderVisitor<'a, 'b> {
592 f: &'a mut Formatter<'b>,
594 maximum_render_width: usize,
596}
597
598impl TreeRenderVisitor<'_, '_> {
599 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> {
622 let root = RenderTree::create_tree(plan);
623
624 for y in 0..root.height {
625 self.render_top_layer(&root, y)?;
627 self.render_box_content(&root, y)?;
629 self.render_bottom_layer(&root, y)?;
631 }
632
633 Ok(())
634 }
635
636 fn render_top_layer(
642 &mut self,
643 root: &RenderTree,
644 y: usize,
645 ) -> Result<(), fmt::Error> {
646 for x in 0..root.width {
647 if self.maximum_render_width > 0
648 && x * Self::NODE_RENDER_WIDTH >= self.maximum_render_width
649 {
650 break;
651 }
652
653 if root.has_node(x, y) {
654 write!(self.f, "{}", Self::LTCORNER)?;
655 write!(
656 self.f,
657 "{}",
658 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2 - 1)
659 )?;
660 if y == 0 {
661 write!(self.f, "{}", Self::HORIZONTAL)?;
663 } else {
664 write!(self.f, "{}", Self::DMIDDLE)?;
666 }
667 write!(
668 self.f,
669 "{}",
670 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2 - 1)
671 )?;
672 write!(self.f, "{}", Self::RTCORNER)?;
673 } else {
674 let mut has_adjacent_nodes = false;
675 for i in 0..(root.width - x) {
676 has_adjacent_nodes = has_adjacent_nodes || root.has_node(x + i, y);
677 }
678 if !has_adjacent_nodes {
679 continue;
682 }
683 write!(self.f, "{}", &" ".repeat(Self::NODE_RENDER_WIDTH))?;
685 }
686 }
687 writeln!(self.f)?;
688
689 Ok(())
690 }
691
692 fn render_box_content(
698 &mut self,
699 root: &RenderTree,
700 y: usize,
701 ) -> Result<(), fmt::Error> {
702 let mut extra_info: Vec<Vec<String>> = vec![vec![]; root.width];
703 let mut extra_height = 0;
704
705 for (x, extra_info_item) in extra_info.iter_mut().enumerate().take(root.width) {
706 if let Some(node) = root.get_node(x, y) {
707 Self::split_up_extra_info(
708 &node.extra_text,
709 extra_info_item,
710 Self::MAX_EXTRA_LINES,
711 );
712 if extra_info_item.len() > extra_height {
713 extra_height = extra_info_item.len();
714 }
715 }
716 }
717
718 let halfway_point = extra_height.div_ceil(2);
719
720 for render_y in 0..=extra_height {
722 for (x, _) in root.nodes.iter().enumerate().take(root.width) {
723 if self.maximum_render_width > 0
724 && x * Self::NODE_RENDER_WIDTH >= self.maximum_render_width
725 {
726 break;
727 }
728
729 let mut has_adjacent_nodes = false;
730 for i in 0..(root.width - x) {
731 has_adjacent_nodes = has_adjacent_nodes || root.has_node(x + i, y);
732 }
733
734 if let Some(node) = root.get_node(x, y) {
735 write!(self.f, "{}", Self::VERTICAL)?;
736
737 let mut render_text = String::new();
739 if render_y == 0 {
740 render_text = node.name.clone();
741 } else if render_y <= extra_info[x].len() {
742 render_text = extra_info[x][render_y - 1].clone();
743 }
744
745 render_text = Self::adjust_text_for_rendering(
746 &render_text,
747 Self::NODE_RENDER_WIDTH - 2,
748 );
749 write!(self.f, "{render_text}")?;
750
751 if render_y == halfway_point && node.child_positions.len() > 1 {
752 write!(self.f, "{}", Self::LMIDDLE)?;
753 } else {
754 write!(self.f, "{}", Self::VERTICAL)?;
755 }
756 } else if render_y == halfway_point {
757 let has_child_to_the_right =
758 Self::should_render_whitespace(root, x, y);
759 if root.has_node(x, y + 1) {
760 write!(
762 self.f,
763 "{}",
764 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2)
765 )?;
766 if has_child_to_the_right {
767 write!(self.f, "{}", Self::TMIDDLE)?;
768 write!(
770 self.f,
771 "{}",
772 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2)
773 )?;
774 } else {
775 write!(self.f, "{}", Self::RTCORNER)?;
776 if has_adjacent_nodes {
777 write!(
779 self.f,
780 "{}",
781 " ".repeat(Self::NODE_RENDER_WIDTH / 2)
782 )?;
783 }
784 }
785 } else if has_child_to_the_right {
786 write!(
789 self.f,
790 "{}",
791 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH)
792 )?;
793 } else if has_adjacent_nodes {
794 write!(self.f, "{}", " ".repeat(Self::NODE_RENDER_WIDTH))?;
796 }
797 } else if render_y >= halfway_point {
798 if root.has_node(x, y + 1) {
799 write!(
801 self.f,
802 "{}{}",
803 " ".repeat(Self::NODE_RENDER_WIDTH / 2),
804 Self::VERTICAL
805 )?;
806 if has_adjacent_nodes
807 || Self::should_render_whitespace(root, x, y)
808 {
809 write!(
810 self.f,
811 "{}",
812 " ".repeat(Self::NODE_RENDER_WIDTH / 2)
813 )?;
814 }
815 } else if has_adjacent_nodes
816 || Self::should_render_whitespace(root, x, y)
817 {
818 write!(self.f, "{}", " ".repeat(Self::NODE_RENDER_WIDTH))?;
820 }
821 } else if has_adjacent_nodes {
822 write!(self.f, "{}", " ".repeat(Self::NODE_RENDER_WIDTH))?;
824 }
825 }
826 writeln!(self.f)?;
827 }
828
829 Ok(())
830 }
831
832 fn render_bottom_layer(
838 &mut self,
839 root: &RenderTree,
840 y: usize,
841 ) -> Result<(), fmt::Error> {
842 for x in 0..=root.width {
843 if self.maximum_render_width > 0
844 && x * Self::NODE_RENDER_WIDTH >= self.maximum_render_width
845 {
846 break;
847 }
848 let mut has_adjacent_nodes = false;
849 for i in 0..(root.width - x) {
850 has_adjacent_nodes = has_adjacent_nodes || root.has_node(x + i, y);
851 }
852 if root.get_node(x, y).is_some() {
853 write!(self.f, "{}", Self::LDCORNER)?;
854 write!(
855 self.f,
856 "{}",
857 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2 - 1)
858 )?;
859 if root.has_node(x, y + 1) {
860 write!(self.f, "{}", Self::TMIDDLE)?;
862 } else {
863 write!(self.f, "{}", Self::HORIZONTAL)?;
865 }
866 write!(
867 self.f,
868 "{}",
869 Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2 - 1)
870 )?;
871 write!(self.f, "{}", Self::RDCORNER)?;
872 } else if root.has_node(x, y + 1) {
873 write!(self.f, "{}", &" ".repeat(Self::NODE_RENDER_WIDTH / 2))?;
874 write!(self.f, "{}", Self::VERTICAL)?;
875 if has_adjacent_nodes || Self::should_render_whitespace(root, x, y) {
876 write!(self.f, "{}", &" ".repeat(Self::NODE_RENDER_WIDTH / 2))?;
877 }
878 } else if has_adjacent_nodes || Self::should_render_whitespace(root, x, y) {
879 write!(self.f, "{}", &" ".repeat(Self::NODE_RENDER_WIDTH))?;
880 }
881 }
882 writeln!(self.f)?;
883
884 Ok(())
885 }
886
887 fn extra_info_separator() -> String {
888 "-".repeat(Self::NODE_RENDER_WIDTH - 9)
889 }
890
891 fn remove_padding(s: &str) -> String {
892 s.trim().to_string()
893 }
894
895 pub fn split_up_extra_info(
896 extra_info: &HashMap<String, String>,
897 result: &mut Vec<String>,
898 max_lines: usize,
899 ) {
900 if extra_info.is_empty() {
901 return;
902 }
903
904 result.push(Self::extra_info_separator());
905
906 let mut requires_padding = false;
907 let mut was_inlined = false;
908
909 let sorted_extra_info: BTreeMap<_, _> = extra_info.iter().collect();
911 for (key, value) in sorted_extra_info {
912 let mut str = Self::remove_padding(value);
913 let mut is_inlined = false;
914 let available_width = Self::NODE_RENDER_WIDTH - 7;
915 let total_size = key.len() + str.len() + 2;
916 let is_multiline = str.contains('\n');
917
918 if str.is_empty() {
919 str = key.to_string();
920 } else if !is_multiline && total_size < available_width {
921 str = format!("{key}: {str}");
922 is_inlined = true;
923 } else {
924 str = format!("{key}:\n{str}");
925 }
926
927 if is_inlined && was_inlined {
928 requires_padding = false;
929 }
930
931 if requires_padding {
932 result.push(String::new());
933 }
934
935 let mut splits: Vec<String> = str.split('\n').map(String::from).collect();
936 if splits.len() > max_lines {
937 let mut truncated_splits = Vec::new();
938 for split in splits.iter().take(max_lines / 2) {
939 truncated_splits.push(split.clone());
940 }
941 truncated_splits.push("...".to_string());
942 for split in splits.iter().skip(splits.len() - max_lines / 2) {
943 truncated_splits.push(split.clone());
944 }
945 splits = truncated_splits;
946 }
947 for split in splits {
948 Self::split_string_buffer(&split, result);
949 }
950 if result.len() > max_lines {
951 result.truncate(max_lines);
952 result.push("...".to_string());
953 }
954
955 requires_padding = true;
956 was_inlined = is_inlined;
957 }
958 }
959
960 fn adjust_text_for_rendering(source: &str, max_render_width: usize) -> String {
964 let render_width = source.chars().count();
965 if render_width > max_render_width {
966 let truncated = &source[..max_render_width - 3];
967 format!("{truncated}...")
968 } else {
969 let total_spaces = max_render_width - render_width;
970 let half_spaces = total_spaces / 2;
971 let extra_left_space = if total_spaces.is_multiple_of(2) { 0 } else { 1 };
972 format!(
973 "{}{}{}",
974 " ".repeat(half_spaces + extra_left_space),
975 source,
976 " ".repeat(half_spaces)
977 )
978 }
979 }
980
981 fn should_render_whitespace(root: &RenderTree, x: usize, y: usize) -> bool {
987 let mut found_children = 0;
988
989 for i in (0..=x).rev() {
990 let node = root.get_node(i, y);
991 if root.has_node(i, y + 1) {
992 found_children += 1;
993 }
994 if let Some(node) = node {
995 if node.child_positions.len() > 1
996 && found_children < node.child_positions.len()
997 {
998 return true;
999 }
1000
1001 return false;
1002 }
1003 }
1004
1005 false
1006 }
1007
1008 fn split_string_buffer(source: &str, result: &mut Vec<String>) {
1009 let mut character_pos = 0;
1010 let mut start_pos = 0;
1011 let mut render_width = 0;
1012 let mut last_possible_split = 0;
1013
1014 let chars: Vec<char> = source.chars().collect();
1015
1016 while character_pos < chars.len() {
1017 let char_width = 1;
1019
1020 if render_width + char_width > Self::NODE_RENDER_WIDTH - 2 {
1022 if start_pos + 8 > last_possible_split {
1023 last_possible_split = character_pos;
1026 }
1027
1028 result.push(source[start_pos..last_possible_split].to_string());
1029 render_width = character_pos - last_possible_split;
1030 start_pos = last_possible_split;
1031 character_pos = last_possible_split;
1032 }
1033
1034 if Self::can_split_on_this_char(chars[character_pos]) {
1036 last_possible_split = character_pos;
1037 }
1038
1039 character_pos += 1;
1040 render_width += char_width;
1041 }
1042
1043 if source.len() > start_pos {
1044 result.push(source[start_pos..].to_string());
1046 }
1047 }
1048
1049 fn can_split_on_this_char(c: char) -> bool {
1050 (!c.is_ascii_digit() && !c.is_ascii_uppercase() && !c.is_ascii_lowercase())
1051 && c != '_'
1052 }
1053}
1054
1055pub trait DisplayAs {
1057 fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result;
1062}
1063
1064pub struct DefaultDisplay<T>(pub T);
1066
1067impl<T: DisplayAs> fmt::Display for DefaultDisplay<T> {
1068 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1069 self.0.fmt_as(DisplayFormatType::Default, f)
1070 }
1071}
1072
1073pub struct VerboseDisplay<T>(pub T);
1075
1076impl<T: DisplayAs> fmt::Display for VerboseDisplay<T> {
1077 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1078 self.0.fmt_as(DisplayFormatType::Verbose, f)
1079 }
1080}
1081
1082#[derive(Debug)]
1084pub struct ProjectSchemaDisplay<'a>(pub &'a SchemaRef);
1085
1086impl fmt::Display for ProjectSchemaDisplay<'_> {
1087 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1088 let parts: Vec<_> = self
1089 .0
1090 .fields()
1091 .iter()
1092 .map(|x| x.name().to_owned())
1093 .collect::<Vec<String>>();
1094 write!(f, "[{}]", parts.join(", "))
1095 }
1096}
1097
1098pub fn display_orderings(f: &mut Formatter, orderings: &[LexOrdering]) -> fmt::Result {
1099 if !orderings.is_empty() {
1100 let start = if orderings.len() == 1 {
1101 ", output_ordering="
1102 } else {
1103 ", output_orderings=["
1104 };
1105 write!(f, "{start}")?;
1106 for (idx, ordering) in orderings.iter().enumerate() {
1107 match idx {
1108 0 => write!(f, "[{ordering}]")?,
1109 _ => write!(f, ", [{ordering}]")?,
1110 }
1111 }
1112 let end = if orderings.len() == 1 { "" } else { "]" };
1113 write!(f, "{end}")?;
1114 }
1115 Ok(())
1116}
1117
1118#[cfg(test)]
1119mod tests {
1120 use std::fmt::Write;
1121 use std::sync::Arc;
1122
1123 use datafusion_common::{internal_datafusion_err, Result, Statistics};
1124 use datafusion_execution::{SendableRecordBatchStream, TaskContext};
1125
1126 use crate::{DisplayAs, ExecutionPlan, PlanProperties};
1127
1128 use super::DisplayableExecutionPlan;
1129
1130 #[derive(Debug, Clone, Copy)]
1131 enum TestStatsExecPlan {
1132 Panic,
1133 Error,
1134 Ok,
1135 }
1136
1137 impl DisplayAs for TestStatsExecPlan {
1138 fn fmt_as(
1139 &self,
1140 _t: crate::DisplayFormatType,
1141 f: &mut std::fmt::Formatter,
1142 ) -> std::fmt::Result {
1143 write!(f, "TestStatsExecPlan")
1144 }
1145 }
1146
1147 impl ExecutionPlan for TestStatsExecPlan {
1148 fn name(&self) -> &'static str {
1149 "TestStatsExecPlan"
1150 }
1151
1152 fn as_any(&self) -> &dyn std::any::Any {
1153 self
1154 }
1155
1156 fn properties(&self) -> &PlanProperties {
1157 unimplemented!()
1158 }
1159
1160 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
1161 vec![]
1162 }
1163
1164 fn with_new_children(
1165 self: Arc<Self>,
1166 _: Vec<Arc<dyn ExecutionPlan>>,
1167 ) -> Result<Arc<dyn ExecutionPlan>> {
1168 unimplemented!()
1169 }
1170
1171 fn execute(
1172 &self,
1173 _: usize,
1174 _: Arc<TaskContext>,
1175 ) -> Result<SendableRecordBatchStream> {
1176 todo!()
1177 }
1178
1179 fn statistics(&self) -> Result<Statistics> {
1180 self.partition_statistics(None)
1181 }
1182
1183 fn partition_statistics(&self, partition: Option<usize>) -> Result<Statistics> {
1184 if partition.is_some() {
1185 return Ok(Statistics::new_unknown(self.schema().as_ref()));
1186 }
1187 match self {
1188 Self::Panic => panic!("expected panic"),
1189 Self::Error => Err(internal_datafusion_err!("expected error")),
1190 Self::Ok => Ok(Statistics::new_unknown(self.schema().as_ref())),
1191 }
1192 }
1193 }
1194
1195 fn test_stats_display(exec: TestStatsExecPlan, show_stats: bool) {
1196 let display =
1197 DisplayableExecutionPlan::new(&exec).set_show_statistics(show_stats);
1198
1199 let mut buf = String::new();
1200 write!(&mut buf, "{}", display.one_line()).unwrap();
1201 let buf = buf.trim();
1202 assert_eq!(buf, "TestStatsExecPlan");
1203 }
1204
1205 #[test]
1206 fn test_display_when_stats_panic_with_no_show_stats() {
1207 test_stats_display(TestStatsExecPlan::Panic, false);
1208 }
1209
1210 #[test]
1211 fn test_display_when_stats_error_with_no_show_stats() {
1212 test_stats_display(TestStatsExecPlan::Error, false);
1213 }
1214
1215 #[test]
1216 fn test_display_when_stats_ok_with_no_show_stats() {
1217 test_stats_display(TestStatsExecPlan::Ok, false);
1218 }
1219
1220 #[test]
1221 #[should_panic(expected = "expected panic")]
1222 fn test_display_when_stats_panic_with_show_stats() {
1223 test_stats_display(TestStatsExecPlan::Panic, true);
1224 }
1225
1226 #[test]
1227 #[should_panic(expected = "Error")] fn test_display_when_stats_error_with_show_stats() {
1229 test_stats_display(TestStatsExecPlan::Error, true);
1230 }
1231
1232 #[test]
1233 fn test_display_when_stats_ok_with_show_stats() {
1234 test_stats_display(TestStatsExecPlan::Ok, false);
1235 }
1236}