summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Burwell <ben@benburwell.com>2020-06-02 22:50:01 -0400
committerBen Burwell <ben@benburwell.com>2020-06-02 22:50:01 -0400
commit14aa01d4f1669dd385da2fb3dfeb8f1f55a4b8df (patch)
treecb8b5803f6c73756b6d5b04309a3cdc98fb0c1d5
parentc1dcb9245f288ddef7a213b7d2fd8a8ebe5d3ab3 (diff)
Further reorg
-rw-r--r--go.sum2
-rw-r--r--plan_view.go32
-rw-r--r--postgres/plan.go (renamed from plan.go)2
-rw-r--r--postgres/postgres.go35
4 files changed, 62 insertions, 9 deletions
diff --git a/go.sum b/go.sum
index 2beec68..2f0b933 100644
--- a/go.sum
+++ b/go.sum
@@ -5,6 +5,7 @@ github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdk
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.3.0 h1:r35w0JBADPZCVQijYebl6YMWWtHRqVEGt7kL2eBADRM=
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
+github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
@@ -33,6 +34,7 @@ github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/tview v0.0.0-20200528200248-fe953220389f h1:tRx/LLIP2PSA7johw9xhf+6NUCLC4BbMhpGdm110MGI=
diff --git a/plan_view.go b/plan_view.go
index 36b58c1..f2badfa 100644
--- a/plan_view.go
+++ b/plan_view.go
@@ -79,8 +79,29 @@ func (p *planView) SetPlan(as []*postgres.Explain) {
}
func buildNode(p *postgres.Plan) *tview.TreeNode {
- dur := f2d(p.ActualTotalTime)
- n := tview.NewTreeNode(fmt.Sprintf("%s (%s)", p.NodeType, dur.String()))
+ dur := p.EffectiveTotalTime()
+ var (
+ badEstimate string
+ sortBy string
+ scanOn string
+ scanIndex string
+ )
+ if p.IsBadEstimate() {
+ badEstimate = " [red](bad estimate)[white]"
+ }
+ if len(p.SortKey) > 0 {
+ sortBy = " by " + strings.Join(p.SortKey, ", ")
+ }
+ if p.RelationName != nil {
+ scanOn = " on " + *p.RelationName
+ }
+ if p.IndexName != nil {
+ scanIndex = " using " + *p.IndexName
+ }
+
+ label := fmt.Sprintf("%s (%s)%s%s%s%s", p.NodeType, dur.String(),
+ badEstimate, sortBy, scanOn, scanIndex)
+ n := tview.NewTreeNode(label)
n.SetSelectable(true)
n.SetReference(p)
for _, subplan := range p.Plans {
@@ -287,8 +308,7 @@ func f2a(f float32) string {
}
func f2d(f float32) time.Duration {
- return time.Duration(int(f)) * time.Millisecond
- // ms := time.Duration(int(f)) * time.Millisecond
- // ns := time.Duration(int((f-float32(ms))*1000)) * time.Nanosecond
- // return time.Duration(ms + ns)
+ // f is in milliseconds, multiply by 1k to get microseconds
+ // (thousandths of milliseconds)
+ return time.Duration(int(f)*1000) * time.Microsecond
}
diff --git a/plan.go b/postgres/plan.go
index 89606d6..9aa12f8 100644
--- a/plan.go
+++ b/postgres/plan.go
@@ -1,4 +1,4 @@
-package main
+package postgres
const testPlan = `
[
diff --git a/postgres/postgres.go b/postgres/postgres.go
index 3a5221e..5333f8d 100644
--- a/postgres/postgres.go
+++ b/postgres/postgres.go
@@ -3,6 +3,8 @@ package postgres
import (
"encoding/json"
"fmt"
+ "math"
+ "time"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
@@ -139,10 +141,39 @@ func (db *DB) Analyze(query string) ([]*Explain, error) {
}
var exp []*Explain
- if err := json.Unmarshal([]byte(plan.Plan), &exp); err != nil {
- // if err := json.Unmarshal([]byte(testPlan), &a); err != nil {
+ // if err := json.Unmarshal([]byte(plan.Plan), &exp); err != nil {
+ if err := json.Unmarshal([]byte(testPlan), &exp); err != nil {
return nil, fmt.Errorf("could not unmarshal plan: %v", err)
}
return exp, nil
}
+
+func (p *Plan) EffectiveTotalTime() time.Duration {
+ var childTime time.Duration
+ for _, p := range p.Plans {
+ childTime = childTime + f2d(p.ActualTotalTime)
+ }
+ return f2d(p.ActualTotalTime) - childTime
+}
+
+func (p *Plan) EstimateWrongness() (string, int) {
+ plan, actual := float64(p.PlanRows), float64(p.ActualRows)
+ bigger, smaller := math.Max(plan, actual), math.Min(plan, actual)
+ ratio := int(bigger / smaller)
+ if plan > actual {
+ return "over", ratio
+ }
+ return "under", ratio
+}
+
+func (p *Plan) IsBadEstimate() bool {
+ _, ratio := p.EstimateWrongness()
+ return ratio >= 100
+}
+
+func f2d(f float32) time.Duration {
+ // f is in milliseconds, multiply by 1k to get microseconds
+ // (thousandths of milliseconds)
+ return time.Duration(int(f)*1000) * time.Microsecond
+}