Skip to content

Commit

Permalink
Simplify template usage
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcus Gartner committed Oct 24, 2019
1 parent fa5eb43 commit cfde2e2
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 106 deletions.
110 changes: 30 additions & 80 deletions pkg/html/html.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package html

import (
"encoding/json"
"bytes"
"fmt"
"html/template"
"io"
"strings"

"pg_flame/pkg/plan"
)
Expand All @@ -20,63 +18,49 @@ type Flame struct {
Children []Flame `json:"children"`
}

const tableHeader = `<table class="table table-striped table-bordered"><tbody>`
const rowTemplate = "<tr><th>%s</th><td>%v</td></tr>"
const tableFooter = `</tbody></table>`

const detailTemplate = "<span>%s</span>"
const detailSpan = "<span>%s</span>"

const colorPlan = "#00C05A"
const colorInit = "#C0C0C0"

func Generate(w io.Writer, p plan.Plan) error {
f := buildFlame(p)

t, err := template.New("pg_flame").Parse(templateHTML)
err, f := buildFlame(p)
if err != nil {
return err
}

flameJSON, err := json.Marshal(f)
if err != nil {
return err
}

data := struct {
Data template.JS
}{
Data: template.JS(flameJSON),
}

err = t.Execute(w, data)
err = templateHTML.Execute(w, f)
if err != nil {
return err
}

return nil
}

func buildFlame(p plan.Plan) Flame {
func buildFlame(p plan.Plan) (error, Flame) {
planningFlame := Flame{
Name: "Query Planning",
Value: p.PlanningTime,
Time: p.PlanningTime,
Detail: fmt.Sprintf(detailTemplate, "Time to generate the query plan"),
Detail: fmt.Sprintf(detailSpan, "Time to generate the query plan"),
Color: colorPlan,
}

executionFlame := convertPlanNode(p.ExecutionTree, "")
err, executionFlame := convertPlanNode(p.ExecutionTree, "")
if err != nil {
return err, Flame{}
}

return Flame{
return nil, Flame{
Name: "Total",
Value: planningFlame.Value + executionFlame.Value,
Time: planningFlame.Time + executionFlame.Time,
Detail: fmt.Sprintf(detailTemplate, "Includes planning and execution time"),
Detail: fmt.Sprintf(detailSpan, "Includes planning and execution time"),
Children: []Flame{planningFlame, executionFlame},
}
}

func convertPlanNode(n plan.Node, color string) Flame {
func convertPlanNode(n plan.Node, color string) (error, Flame) {
initPlan := n.ParentRelationship == "InitPlan"
value := n.TotalTime

Expand All @@ -88,7 +72,10 @@ func convertPlanNode(n plan.Node, color string) Flame {
for _, childNode := range n.Children {

// Pass the color forward for grey InitPlan trees
f := convertPlanNode(childNode, color)
err, f := convertPlanNode(childNode, color)
if err != nil {
return err, Flame{}
}

// Add to the total value if the child is an InitPlan node
if f.InitPlan {
Expand All @@ -98,11 +85,16 @@ func convertPlanNode(n plan.Node, color string) Flame {
childFlames = append(childFlames, f)
}

return Flame{
err, d := detail(n)
if err != nil {
return err, Flame{}
}

return nil, Flame{
Name: name(n),
Value: value,
Time: n.TotalTime,
Detail: detail(n),
Detail: d,
Color: color,
InitPlan: initPlan,
Children: childFlames,
Expand All @@ -120,55 +112,13 @@ func name(n plan.Node) string {
}
}

func detail(n plan.Node) string {
var b strings.Builder
b.WriteString(tableHeader)

if n.ParentRelationship != "" {
fmt.Fprintf(&b, rowTemplate, "Parent Relationship", n.ParentRelationship)
}

if n.Filter != "" {
fmt.Fprintf(&b, rowTemplate, "Filter", n.Filter)
}

if n.JoinFilter != "" {
fmt.Fprintf(&b, rowTemplate, "Join Filter", n.JoinFilter)
}

if n.HashCond != "" {
fmt.Fprintf(&b, rowTemplate, "Hash Cond", n.HashCond)
}

if n.IndexCond != "" {
fmt.Fprintf(&b, rowTemplate, "Index Cond", n.IndexCond)
}

if n.RecheckCond != "" {
fmt.Fprintf(&b, rowTemplate, "Recheck Cond", n.RecheckCond)
}

if n.BuffersHit != 0 {
fmt.Fprintf(&b, rowTemplate, "Buffers Shared Hit", n.BuffersHit)
}

if n.BuffersRead != 0 {
fmt.Fprintf(&b, rowTemplate, "Buffers Shared Read", n.BuffersRead)
}

if n.HashBuckets != 0 {
fmt.Fprintf(&b, rowTemplate, "Hash Buckets", n.HashBuckets)
}
func detail(n plan.Node) (error, string) {
var b bytes.Buffer

if n.HashBatches != 0 {
fmt.Fprintf(&b, rowTemplate, "Hash Batches", n.HashBatches)
}

if n.MemoryUsage != 0 {
fmt.Fprintf(&b, rowTemplate, "Memory Usage", fmt.Sprintf("%vkB", n.MemoryUsage))
err := templateTable.Execute(&b, n)
if err != nil {
return err, ""
}

b.WriteString(tableFooter)

return b.String()
return nil, b.String()
}
36 changes: 13 additions & 23 deletions pkg/html/html_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package html

import (
"bytes"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -47,7 +46,9 @@ func Test_buildFlame(t *testing.T) {
},
}

f := buildFlame(p)
err, f := buildFlame(p)

assert.NoError(t, err)

assert.Equal(t, "Total", f.Name)
assert.Equal(t, 0.133, f.Value)
Expand Down Expand Up @@ -91,7 +92,9 @@ func Test_buildFlame(t *testing.T) {
},
}

f := buildFlame(p)
err, f := buildFlame(p)

assert.NoError(t, err)

assert.Equal(t, "Total", f.Name)
assert.Equal(t, 0.32, f.Value)
Expand Down Expand Up @@ -147,28 +150,15 @@ func Test_detail(t *testing.T) {

t.Run("returns a table of details", func(t *testing.T) {
n := plan.Node{
ParentRelationship: "InitPlan",
Filter: "(id = 123)",
BuffersHit: 8,
BuffersRead: 5,
HashBuckets: 1024,
HashBatches: 1,
MemoryUsage: 12,
Filter: "(id = 123)",
MemoryUsage: 12,
}

expected := strings.Join([]string{
"<table class=\"table table-striped table-bordered\"><tbody>",
"<tr><th>Parent Relationship</th><td>InitPlan</td></tr>",
"<tr><th>Filter</th><td>(id = 123)</td></tr>",
"<tr><th>Buffers Shared Hit</th><td>8</td></tr>",
"<tr><th>Buffers Shared Read</th><td>5</td></tr>",
"<tr><th>Hash Buckets</th><td>1024</td></tr>",
"<tr><th>Hash Batches</th><td>1</td></tr>",
"<tr><th>Memory Usage</th><td>12kB</td></tr>",
"</tbody></table>",
}, "")

assert.Equal(t, expected, detail(n))
err, d := detail(n)

assert.NoError(t, err)
assert.Contains(t, d, n.Filter)
assert.Contains(t, d, "12kB")
})

}
83 changes: 80 additions & 3 deletions pkg/html/template.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package html

const templateHTML = `
import (
"html/template"
)

var templateHTML *template.Template = template.Must(template.New("html").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
Expand Down Expand Up @@ -121,7 +125,7 @@ const templateHTML = `
}
flameGraph.label(label);
var data = {{.Data}};
var data = {{.}};
d3.select("#chart")
.datum(data)
Expand All @@ -148,4 +152,77 @@ const templateHTML = `
</script>
</body>
</html>
`
`))

var templateTable *template.Template = template.Must(template.New("table").Parse(`
<table class="table table-striped table-bordered">
<tbody>
{{if .Filter}}
<tr>
<th>Filter</th>
<td>{{.Filter}}</td>
</tr>
{{end}}
{{if .ParentRelationship}}
<tr>
<th>Parent Relationship</th>
<td>{{.ParentRelationship}}</td>
</tr>
{{end}}
{{if .JoinFilter}}
<tr>
<th>Join Filter</th>
<td>{{.JoinFilter}}</td>
</tr>
{{end}}
{{if .HashCond}}
<tr>
<th>Hash Cond</th>
<td>{{.HashCond}}</td>
</tr>
{{end}}
{{if .IndexCond}}
<tr>
<th>Index Cond</th>
<td>{{.IndexCond}}</td>
</tr>
{{end}}
{{if .RecheckCond}}
<tr>
<th>Recheck Cond</th>
<td>{{.RecheckCond}}</td>
</tr>
{{end}}
{{if .BuffersHit}}
<tr>
<th>Buffers Shared Hit</th>
<td>{{.BuffersHit}}</td>
</tr>
{{end}}
{{if .BuffersRead}}
<tr>
<th>Buffers Shared Read</th>
<td>{{.BuffersRead}}</td>
</tr>
{{end}}
{{if .HashBuckets}}
<tr>
<th>Hash Buckets</th>
<td>{{.HashBuckets}}</td>
</tr>
{{end}}
{{if .HashBatches}}
<tr>
<th>Hash Batches</th>
<td>{{.HashBatches}}</td>
</tr>
{{end}}
{{if .MemoryUsage}}
<tr>
<th>Memory Usage</th>
<td>{{.MemoryUsage}}kB</td>
</tr>
{{end}}
</tbody>
</table>
`))

0 comments on commit cfde2e2

Please sign in to comment.