Cloud Spanner Query Plan Visualizer using goccy/go-graphviz.
(Possibly) remote calls are rendered as dashed lines.
go install github.com/apstndb/spannerplanviz@latestIt can read various types in JSON and YAML.
- QueryPlan
- Can get easily by client libraries
- ResultSetStats
- Output of DOWNLOAD JSON in the official query plan visualizer
- ResultSet
- Output of
gcloud spanner databases execute-sqland execspansql
- Output of
$ gcloud spanner databases execute-sql --instance=sampleinstance sampledb --query-mode=PLAN --format=yaml \
--sql="SELECT SongName FROM Songs WHERE STARTS_WITH(SongName, @prefix)" |
spannerplanviz --full --type=svg --output plan.svg
You see verbose profile information. (Currently, histogram is not shown.)
$ gcloud spanner databases execute-sql --instance=sampleinstance sampledb --query-mode=PROFILE --format=yaml \
--sql "SELECT * FROM Singers JOIN Songs USING(SingerId) WHERE SongName LIKE 'Th%e'" |
spannerplanviz --full --type=svg --output profile.svg
You can emit Mermaid.js using --type mermaid (EXPERIMENTAL).
spannerplanviz --full --type=mermaid --output profile.mermaid < dca_profile.json
The generated source uses HTML labels and a browser-friendly init block (htmlLabels: true, useMaxWidth: false). See visualize/testdata/dca_profile.golden.mermaid for a full example output.
--type dot emits pure Graphviz DOT source (unlaid-out, no pos/bb layout attributes) via the dot package. Pipe it into dot -Tsvg (or any Graphviz tool) to lay it out:
spannerplanviz --full --type=dot < dca_profile.json | dot -Tsvg -o profile.svg
--type svg and --type png still produce fully laid-out output; they generate the same DOT source internally and render it with the embedded Graphviz runtime.
You can emit D2 source using --type d2 (EXPERIMENTAL). Lay it out with the d2 CLI:
spannerplanviz --full --type=d2 --output profile.d2 < dca_profile.json
d2 profile.d2 profile.svg
Node labels use D2 markdown blocks (|md ... |), tooltips carry the canonical plan YAML in |yaml ... | block strings, possible remote calls are drawn with a dashed stroke, and --show-query/--show-query-stats add a rounded query node linked from the root. See visualize/testdata/dca_profile.golden.d2 for a full example.
--type |
Output |
|---|---|
svg (default), png |
Fully laid-out Graphviz raster/vector, rendered with the embedded runtime |
dot |
Unlaid-out Graphviz DOT source; lay out with dot -Tsvg |
mermaid |
Mermaid.js flowchart source |
d2 |
D2 source; lay out with d2 in.d2 out.svg |
--show-query and --show-query-stats add a query-text node and are honored by the svg, png, dot, mermaid, and d2 types.
Build a diagram model once, then render with the backend of your choice:
stats, rowType, err := spannerplan.ExtractQueryPlan(input)
plan, err := visualize.BuildPlan(rowType, stats, visualize.StructureBuildOptions())
src, err := mermaid.Source(plan)Presets:
visualize.StructureBuildOptions()— operator structure for interactive viewers (lighter than--full)visualize.FullBuildOptions()— same detail level as CLI--full
Renderers:
mermaid.Source(plan)— Mermaid.js source usingplan.Buildmermaid.SourceWithOptions(plan, opts)— overrideplan.Buildat render time (including disabling flags)mermaid.NewRenderer(opts).Render(ctx, w, plan)— streaming renderdot.Source(plan)/dot.SourceWithOptions(plan, opts)— DOT source text without a Graphviz runtime (nogoccy/go-graphvizdependency); lay it out elsewhere, e.g. with a browser-side Graphviz build. This is the single source of truth for graph construction.d2.Source(plan)/d2.SourceWithOptions(plan, opts)— D2 source text; lay it out with thed2CLId2.NewRenderer(opts).Render(ctx, w, plan)— streaming rendergraphviz.NewRenderer(opts).Render(ctx, w, plan)— SVG/PNG via Graphviz; internally generates thedotpackage's source and hands it to the Graphviz runtime, so both paths describe an identical graph
The dot, mermaid and d2 backends all consume a shared backend-neutral graph model (visualize.BuildGraph), so they describe the same nodes, edges and labels.
When rendering Mermaid in the browser (for example from Go WASM):
- Call
visualize.BuildPlanandmermaid.Source(plan). - Pass the returned source to mermaid.js
render(). - The source includes a
%%{ init: ... }%%block withhtmlLabels: trueanduseMaxWidth: false. Keep your globalmermaid.initialize()consistent with those settings if you set defaults separately. - Prefer
StructureBuildOptions()for large plans;--fulloutput can be slow to lay out in the browser.
BuildOptions fields mirror CLI flags (metadata, execution-stats, hide-metadata, and so on). See option.Options.BuildOptions() for the full mapping.
- The
spannerplanvizCLI flags and behavior are treated as stable. - The Go library API (
visualize.BuildPlan,mermaid.Source,graphviz.NewRenderer,dot.Source, and related types) is experimental and may change between releases. - This module follows v0 semver: breaking changes may appear in minor releases. See GitHub release notes for details.
- Text plan rendering moved to
spannerplan/cmd/rendertree; the deprecated shim in this repository has been removed.
This tool is Alpha quality.

