From da39e4099cb49ad5e70335904d2a0c0ccb17268d Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Mon, 15 Feb 2021 08:03:12 -0500 Subject: [PATCH] feat: tool to graphically display plans (#803) * feat: tool to graphically display plans * docs: Add note to CONTRIBUTING.md Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- CONTRIBUTING.md | 38 +++++++++++++++++++++++++++++++++++ tools/iplan | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100755 tools/iplan diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ff7848003f..daf59984e6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -118,6 +118,44 @@ can use all the features of that crate. For example, to disable the RUST_LOG=debug,hyper::proto::h1=info,h2=info cargo test --workspace ``` +### Visually showing explain plans + +Some query plans are output in the log in [graphviz](https://graphviz.org/) format. To display them you can use the `tools/iplan` helper. + +For example, if you want to display this plan: + +``` +// Begin DataFusion GraphViz Plan (see https://graphviz.org) +digraph { + subgraph cluster_1 + { + graph[label="LogicalPlan"] + 2[shape=box label="SchemaPivot"] + 3[shape=box label="Projection: "] + 2 -> 3 [arrowhead=none, arrowtail=normal, dir=back] + 4[shape=box label="Filter: Int64(0) LtEq #time And #time Lt Int64(10000) And #host Eq Utf8(_server01_)"] + 3 -> 4 [arrowhead=none, arrowtail=normal, dir=back] + 5[shape=box label="TableScan: attributes projection=None"] + 4 -> 5 [arrowhead=none, arrowtail=normal, dir=back] + } + subgraph cluster_6 + { + graph[label="Detailed LogicalPlan"] + 7[shape=box label="SchemaPivot\nSchema: [non_null_column:Utf8]"] + 8[shape=box label="Projection: \nSchema: []"] + 7 -> 8 [arrowhead=none, arrowtail=normal, dir=back] + 9[shape=box label="Filter: Int64(0) LtEq #time And #time Lt Int64(10000) And #host Eq Utf8(_server01_)\nSchema: [color:Utf8;N, time:Int64]"] + 8 -> 9 [arrowhead=none, arrowtail=normal, dir=back] + 10[shape=box label="TableScan: attributes projection=None\nSchema: [color:Utf8;N, time:Int64]"] + 9 -> 10 [arrowhead=none, arrowtail=normal, dir=back] + } +} +// End DataFusion GraphViz Plan +``` + +You can pipe it to `iplan` and render as a .pdf + + ## Running `rustfmt` and `clippy` CI will check the code formatting with [`rustfmt`] and Rust best practices with [`clippy`]. diff --git a/tools/iplan b/tools/iplan new file mode 100755 index 0000000000..321a39949e --- /dev/null +++ b/tools/iplan @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# +# This script extracts all plans from its stdin, invokes dot on them +# to make a multi-page pdf / open to display them +# +import os +import fileinput + +files = [] + +in_graphviz = False + +# Note we combine multiple digraphs +for line in fileinput.input(): + line = line.strip() + + if line.startswith("// Begin DataFusion GraphViz Plan"): + filename = "/tmp/plan{}.ps".format(len(files)) + files.append(filename) + f = open(filename, "w") + in_graphviz = True + + if in_graphviz: + f.write(line) + f.write("\n") + + if line.startswith("// End DataFusion GraphViz Plan"): + in_graphviz = False + f = None + +# try to ensure any open is closed +f = None + + +# TODO make this not mac specific +if len(files) > 0: + #print("Found files: {}".format(files)) + print("Found {} graphs, converting to ps".format(len(files))) + # convert them to ps files + ps_files = [] + for file in files: + output_file = "{}.pdf".format(file) + os.system("dot -Tpdf < {} > {}.pdf".format(file, output_file)) + ps_files.append(output_file) + + # combine all the postscript files together + cmd = "gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=/tmp/plan.pdf {}".format(" ".join(ps_files)) + + print("Running command:", cmd) + os.system(cmd) + + print("Opening with system viewer") + os.system('open /tmp/plan.pdf')