@@ -12,6 +12,62 @@ use slotmap::{Key, SecondaryMap, SlotMap};
1212
1313use super :: { PhysNode , PhysNodeKey , PhysNodeKind } ;
1414
15+ /// A style of a graph node.
16+ enum NodeStyle {
17+ InMemoryFallback ,
18+ MemoryIntensive ,
19+ Generic ,
20+ }
21+
22+ impl NodeStyle {
23+ const COLOR_IN_MEM_FALLBACK : & str = "0.0 0.3 1.0" ; // Pastel red
24+ const COLOR_MEM_INTENSIVE : & str = "0.16 0.3 1.0" ; // Pastel yellow
25+
26+ /// Returns a style for a node kind.
27+ pub fn for_node_kind ( kind : & PhysNodeKind ) -> Self {
28+ use PhysNodeKind as K ;
29+ match kind {
30+ K :: InMemoryMap { .. } => Self :: InMemoryFallback ,
31+ K :: InMemorySource { .. }
32+ | K :: InputIndependentSelect { .. }
33+ | K :: NegativeSlice { .. }
34+ | K :: InMemorySink { .. }
35+ | K :: Sort { .. }
36+ | K :: GroupBy { .. }
37+ | K :: EquiJoin { .. }
38+ | K :: SemiAntiJoin { .. }
39+ | K :: InMemoryJoin { .. }
40+ | K :: MergeSorted { .. }
41+ | K :: Multiplexer { .. } => Self :: MemoryIntensive ,
42+ _ => Self :: Generic ,
43+ }
44+ }
45+
46+ /// Returns extra styling attributes (if any) for the graph node.
47+ pub fn node_attrs ( & self ) -> Option < String > {
48+ match self {
49+ Self :: InMemoryFallback => Some ( format ! (
50+ "style=filled,fillcolor=\" {}\" " ,
51+ Self :: COLOR_IN_MEM_FALLBACK
52+ ) ) ,
53+ Self :: MemoryIntensive => Some ( format ! (
54+ "style=filled,fillcolor=\" {}\" " ,
55+ Self :: COLOR_MEM_INTENSIVE
56+ ) ) ,
57+ Self :: Generic => None ,
58+ }
59+ }
60+
61+ /// Returns a legend explaining the node style meaning.
62+ pub fn legend ( ) -> String {
63+ format ! (
64+ "fontsize=\" 10\" \n labelloc=\" b\" \n label=<<BR/><BR/><B>Legend</B><BR/><BR/>◯ streaming engine node<FONT COLOR=\" {}\" >⬤</FONT>potentially memory-intensive node<FONT COLOR=\" {}\" >⬤</FONT>in-memory engine fallback>" ,
65+ Self :: COLOR_MEM_INTENSIVE ,
66+ Self :: COLOR_IN_MEM_FALLBACK ,
67+ )
68+ }
69+ }
70+
1571fn escape_graphviz ( s : & str ) -> String {
1672 s. replace ( '\\' , "\\ \\ " )
1773 . replace ( '\n' , "\\ n" )
@@ -39,8 +95,10 @@ fn visualize_plan_rec(
3995 }
4096 visited. insert ( node_key, ( ) ) ;
4197
98+ let kind = & phys_sm[ node_key] . kind ;
99+
42100 use std:: slice:: from_ref;
43- let ( label, inputs) = match & phys_sm [ node_key ] . kind {
101+ let ( label, inputs) = match kind {
44102 PhysNodeKind :: InMemorySource { df } => (
45103 format ! (
46104 "in-memory-source\\ ncols: {}" ,
@@ -329,11 +387,14 @@ fn visualize_plan_rec(
329387 } ,
330388 } ;
331389
332- out. push ( format ! (
333- "{} [label=\" {}\" ];" ,
334- node_key. data( ) . as_ffi( ) ,
335- label
336- ) ) ;
390+ let node_id = node_key. data ( ) . as_ffi ( ) ;
391+ let style = NodeStyle :: for_node_kind ( kind) ;
392+
393+ if let Some ( attrs) = style. node_attrs ( ) {
394+ out. push ( format ! ( "{node_id} [label=\" {label}\" ,{attrs}];" ) ) ;
395+ } else {
396+ out. push ( format ! ( "{node_id} [label=\" {label}\" ];" ) ) ;
397+ }
337398 for input in inputs {
338399 visualize_plan_rec ( input. node , phys_sm, expr_arena, visited, out) ;
339400 out. push ( format ! (
@@ -350,8 +411,9 @@ pub fn visualize_plan(
350411 expr_arena : & Arena < AExpr > ,
351412) -> String {
352413 let mut visited: SecondaryMap < PhysNodeKey , ( ) > = SecondaryMap :: new ( ) ;
353- let mut out = Vec :: with_capacity ( phys_sm. len ( ) + 2 ) ;
414+ let mut out = Vec :: with_capacity ( phys_sm. len ( ) + 3 ) ;
354415 out. push ( "digraph polars {\n rankdir=\" BT\" " . to_string ( ) ) ;
416+ out. push ( NodeStyle :: legend ( ) ) ;
355417 visualize_plan_rec ( root, phys_sm, expr_arena, & mut visited, & mut out) ;
356418 out. push ( "}" . to_string ( ) ) ;
357419 out. join ( "\n " )
0 commit comments