summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.go10
-rw-r--r--plan_view.go407
2 files changed, 227 insertions, 190 deletions
diff --git a/main.go b/main.go
index 6b266d0..5639ea4 100644
--- a/main.go
+++ b/main.go
@@ -46,7 +46,9 @@ func main() {
case tcell.KeyF2:
app.SetFocus(queries)
case tcell.KeyF3:
- app.SetFocus(pv)
+ app.SetFocus(pv.tree)
+ case tcell.KeyF4:
+ app.SetFocus(pv.detail)
case tcell.KeyEnter:
if app.GetFocus() == queries {
p, err := queries.Analyze()
@@ -57,12 +59,6 @@ func main() {
app.SetFocus(pv)
}
}
- case tcell.KeyTAB:
- if app.GetFocus() == pv.tree {
- app.SetFocus(pv.detail)
- } else if app.GetFocus() == pv.detail {
- app.SetFocus(pv.tree)
- }
}
return event
})
diff --git a/plan_view.go b/plan_view.go
index f2badfa..275763e 100644
--- a/plan_view.go
+++ b/plan_view.go
@@ -3,10 +3,11 @@ package main
import (
"fmt"
"log"
- "strconv"
+ // "strconv"
"strings"
"time"
+ "github.com/gdamore/tcell"
"github.com/rivo/tview"
"bnbl.io/pgqt/postgres"
@@ -16,16 +17,19 @@ type planView struct {
*tview.Flex
tree *tview.TreeView
- detail *tview.Table
+ detail *tview.Flex
}
func newPlanView() *planView {
f := tview.NewFlex()
- f.SetBorder(true).SetTitle("Plan")
f.SetDirection(tview.FlexRow)
t := tview.NewTreeView()
- d := tview.NewTable()
+ d := tview.NewFlex()
+ d.SetBackgroundColor(tcell.ColorBlack)
+
+ t.SetBorder(true).SetTitle("Plan")
+ d.SetBorder(true).SetTitle("Node Detail")
f.AddItem(t, 0, 50, true)
f.AddItem(d, 0, 50, false)
@@ -110,197 +114,234 @@ func buildNode(p *postgres.Plan) *tview.TreeNode {
return n
}
-func (pv *planView) ShowDetail(p *postgres.Plan) {
- pv.detail.Clear()
-
- if p == nil {
- return
- }
-
- pv.detail.SetCellSimple(0, 0, "Node Type")
- pv.detail.SetCellSimple(0, 1, p.NodeType)
-
- pv.detail.SetCellSimple(1, 0, "Startup Cost")
- pv.detail.SetCellSimple(1, 1, f2a(p.StartupCost))
-
- pv.detail.SetCellSimple(2, 0, "Total Cost")
- pv.detail.SetCellSimple(2, 1, f2a(p.TotalCost))
-
- pv.detail.SetCellSimple(3, 0, "Plan Rows")
- pv.detail.SetCellSimple(3, 1, strconv.Itoa(p.PlanRows))
-
- pv.detail.SetCellSimple(4, 0, "Actual Rows")
- pv.detail.SetCellSimple(4, 1, strconv.Itoa(p.ActualRows))
-
- pv.detail.SetCellSimple(5, 0, "Plan Width")
- pv.detail.SetCellSimple(5, 1, strconv.Itoa(p.PlanWidth))
-
- pv.detail.SetCellSimple(6, 0, "Actual Startup Time")
- pv.detail.SetCellSimple(6, 1, f2d(p.ActualStartupTime).String())
-
- pv.detail.SetCellSimple(7, 0, "Actual Total Time")
- pv.detail.SetCellSimple(7, 1, f2d(p.ActualTotalTime).String())
-
- pv.detail.SetCellSimple(8, 0, "Actual Loops")
- pv.detail.SetCellSimple(8, 1, strconv.Itoa(p.ActualLoops))
-
- pv.detail.SetCellSimple(9, 0, "Shared Hit Blocks")
- pv.detail.SetCellSimple(9, 1, strconv.Itoa(p.SharedHitBlocks))
-
- pv.detail.SetCellSimple(10, 0, "Shared Read Blocks")
- pv.detail.SetCellSimple(10, 1, strconv.Itoa(p.SharedReadBlocks))
-
- pv.detail.SetCellSimple(11, 0, "Shared Dirtied Blocks")
- pv.detail.SetCellSimple(11, 1, strconv.Itoa(p.SharedDirtiedBlocks))
-
- pv.detail.SetCellSimple(12, 0, "Shared Written Blocks")
- pv.detail.SetCellSimple(12, 1, strconv.Itoa(p.SharedWrittenBlocks))
-
- pv.detail.SetCellSimple(13, 0, "Local Hit Blocks")
- pv.detail.SetCellSimple(13, 1, strconv.Itoa(p.LocalHitBlocks))
-
- pv.detail.SetCellSimple(14, 0, "Local Read Blocks")
- pv.detail.SetCellSimple(14, 1, strconv.Itoa(p.LocalReadBlocks))
-
- pv.detail.SetCellSimple(15, 0, "Local Dirtied Blocks")
- pv.detail.SetCellSimple(15, 1, strconv.Itoa(p.LocalDirtiedBlocks))
-
- pv.detail.SetCellSimple(16, 0, "Local Written Blocks")
- pv.detail.SetCellSimple(16, 1, strconv.Itoa(p.LocalWrittenBlocks))
-
- pv.detail.SetCellSimple(17, 0, "Temp Read Blocks")
- pv.detail.SetCellSimple(17, 1, strconv.Itoa(p.TempReadBlocks))
-
- pv.detail.SetCellSimple(18, 0, "Temp Written Blocks")
- pv.detail.SetCellSimple(18, 1, strconv.Itoa(p.TempWrittenBlocks))
-
- pv.detail.SetCellSimple(19, 0, "I/O Read Time")
- pv.detail.SetCellSimple(19, 1, f2d(p.IOReadTime).String())
-
- pv.detail.SetCellSimple(20, 0, "I/O Write Time")
- pv.detail.SetCellSimple(20, 1, f2d(p.IOWriteTime).String())
-
- idx := 21
-
- if p.ParentRelationship != nil {
- pv.detail.SetCellSimple(idx, 0, "Parent Relationship")
- pv.detail.SetCellSimple(idx, 1, *p.ParentRelationship)
- idx++
- }
-
- if len(p.Output) > 0 {
- pv.detail.SetCellSimple(idx, 0, "Output")
- pv.detail.SetCellSimple(idx, 1, strings.Join(p.Output, ", "))
- idx++
- }
-
- if len(p.SortKey) > 0 {
- pv.detail.SetCellSimple(idx, 0, "Sort Key")
- pv.detail.SetCellSimple(idx, 1, strings.Join(p.SortKey, ", "))
- idx++
- }
-
- if p.SortMethod != nil {
- pv.detail.SetCellSimple(idx, 0, "Sort Method")
- pv.detail.SetCellSimple(idx, 1, *p.SortMethod)
- idx++
- }
-
- if p.SortSpaceUsed != nil {
- pv.detail.SetCellSimple(idx, 0, "Sort Space Used")
- pv.detail.SetCellSimple(idx, 1, strconv.Itoa(*p.SortSpaceUsed))
- idx++
- }
-
- if p.SortSpaceType != nil {
- pv.detail.SetCellSimple(idx, 0, "Sort Space Type")
- pv.detail.SetCellSimple(idx, 1, *p.SortSpaceType)
- idx++
- }
-
- if p.JoinType != nil {
- pv.detail.SetCellSimple(idx, 0, "Join Type")
- pv.detail.SetCellSimple(idx, 1, *p.JoinType)
- idx++
- }
-
- if p.Strategy != nil {
- pv.detail.SetCellSimple(idx, 0, "Strategy")
- pv.detail.SetCellSimple(idx, 1, *p.Strategy)
- idx++
- }
-
- if p.RelationName != nil {
- pv.detail.SetCellSimple(idx, 0, "Relation Name")
- pv.detail.SetCellSimple(idx, 1, *p.RelationName)
- idx++
- }
-
- if p.Schema != nil {
- pv.detail.SetCellSimple(idx, 0, "Schema")
- pv.detail.SetCellSimple(idx, 1, *p.Schema)
- idx++
- }
+func planHeader(p *postgres.Plan) tview.Primitive {
+ f := tview.NewFlex()
+ f.SetDirection(tview.FlexColumn)
+ f.AddItem(planHeaderRowStats(p), 0, 50, false)
+ f.AddItem(planHeaderBlocksTable(p), 0, 50, false)
+ return f
+}
- if p.Alias != nil {
- pv.detail.SetCellSimple(idx, 0, "Alias")
- pv.detail.SetCellSimple(idx, 1, *p.Alias)
- idx++
- }
+func planHeaderRowStats(p *postgres.Plan) tview.Primitive {
+ f := tview.NewFlex()
+ f.SetDirection(tview.FlexRow)
+ f.SetBackgroundColor(tcell.ColorBlack)
- if p.RecheckCond != nil {
- pv.detail.SetCellSimple(idx, 0, "Recheck Cond")
- pv.detail.SetCellSimple(idx, 1, *p.RecheckCond)
- idx++
- }
+ f.AddItem(tview.NewFlex().
+ SetDirection(tview.FlexColumn).
+ AddItem(tview.NewTextView().SetTextAlign(tview.AlignCenter).SetText(fmt.Sprintf("%d", p.PlanRows)), 0, 50, false).
+ AddItem(tview.NewTextView().SetTextAlign(tview.AlignCenter).SetText(fmt.Sprintf("%d", p.ActualRows)), 0, 50, false), 1, 0, false)
- if p.RowsRemovedByIndexRecheck != nil {
- pv.detail.SetCellSimple(idx, 0, "Rows Removed by Index Recheck")
- pv.detail.SetCellSimple(idx, 1, strconv.Itoa(*p.RowsRemovedByIndexRecheck))
- idx++
- }
+ f.AddItem(tview.NewFlex().
+ SetDirection(tview.FlexColumn).
+ AddItem(tview.NewTextView().SetTextAlign(tview.AlignCenter).SetText("planned rows"), 0, 50, false).
+ AddItem(tview.NewTextView().SetTextAlign(tview.AlignCenter).SetText("actual rows"), 0, 50, false), 1, 0, false)
- if p.ExactHeapBlocks != nil {
- pv.detail.SetCellSimple(idx, 0, "Exact Heap Blocks")
- pv.detail.SetCellSimple(idx, 1, strconv.Itoa(*p.ExactHeapBlocks))
- idx++
+ overUnder, wrongness := p.EstimateWrongness()
+ if wrongness > 1 {
+ f.AddItem(tview.NewTextView().SetTextAlign(tview.AlignCenter).SetText(fmt.Sprintf("%s estimate by %dx", overUnder, wrongness)), 1, 0, false)
}
- if p.LossyHeapBlocks != nil {
- pv.detail.SetCellSimple(idx, 0, "Lossy Heap Blocks")
- pv.detail.SetCellSimple(idx, 1, strconv.Itoa(*p.LossyHeapBlocks))
- idx++
- }
+ return f
+}
- if p.IndexName != nil {
- pv.detail.SetCellSimple(idx, 0, "Index Name")
- pv.detail.SetCellSimple(idx, 1, *p.IndexName)
- idx++
- }
+func planHeaderBlocksTable(p *postgres.Plan) tview.Primitive {
+ t := tview.NewTable()
+
+ const (
+ rowShared = 1
+ rowLocal = 2
+ rowTemp = 3
+ colHit = 1
+ colDirtied = 2
+ colRead = 3
+ colWritten = 4
+ )
- if p.IndexCond != nil {
- pv.detail.SetCellSimple(idx, 0, "Index Cond")
- pv.detail.SetCellSimple(idx, 1, *p.IndexCond)
- idx++
- }
+ t.SetCellSimple(0, colHit, "Hit")
+ t.SetCellSimple(0, colDirtied, "Dirtied")
+ t.SetCellSimple(0, colRead, "Read")
+ t.SetCellSimple(0, colWritten, "Written")
+ t.SetCellSimple(rowShared, 0, "Shared Blocks")
+ t.SetCellSimple(rowLocal, 0, "Local Blocks")
+ t.SetCellSimple(rowTemp, 0, "Temp Blocks")
+
+ t.SetCellSimple(rowShared, colHit, fmt.Sprintf("%d", p.SharedHitBlocks))
+ t.SetCellSimple(rowShared, colDirtied, fmt.Sprintf("%d", p.SharedDirtiedBlocks))
+ t.SetCellSimple(rowShared, colRead, fmt.Sprintf("%d", p.SharedReadBlocks))
+ t.SetCellSimple(rowShared, colWritten, fmt.Sprintf("%d", p.SharedWrittenBlocks))
+
+ t.SetCellSimple(rowLocal, colHit, fmt.Sprintf("%d", p.LocalHitBlocks))
+ t.SetCellSimple(rowLocal, colDirtied, fmt.Sprintf("%d", p.LocalDirtiedBlocks))
+ t.SetCellSimple(rowLocal, colRead, fmt.Sprintf("%d", p.LocalReadBlocks))
+ t.SetCellSimple(rowLocal, colWritten, fmt.Sprintf("%d", p.LocalWrittenBlocks))
+
+ t.SetCellSimple(rowTemp, colHit, "-")
+ t.SetCellSimple(rowTemp, colDirtied, "-")
+ t.SetCellSimple(rowTemp, colRead, fmt.Sprintf("%d", p.TempReadBlocks))
+ t.SetCellSimple(rowTemp, colWritten, fmt.Sprintf("%d", p.TempWrittenBlocks))
+
+ return t
+}
- if p.ScanDirection != nil {
- pv.detail.SetCellSimple(idx, 0, "Scan Direction")
- pv.detail.SetCellSimple(idx, 1, *p.ScanDirection)
- idx++
- }
+func (pv *planView) ShowDetail(p *postgres.Plan) {
+ pv.detail.Clear()
- if p.ParallelAware != nil {
- pv.detail.SetCellSimple(idx, 0, "Parallel Aware")
- pv.detail.SetCellSimple(idx, 1, fmt.Sprintf("%t", *p.ParallelAware))
- idx++
+ if p == nil {
+ return
}
- if p.FunctionName != nil {
- pv.detail.SetCellSimple(idx, 0, "Function Name")
- pv.detail.SetCellSimple(idx, 1, *p.FunctionName)
- idx++
- }
+ pv.detail.SetTitle(fmt.Sprintf("Node Detail: %s", p.NodeType))
+ pv.detail.SetDirection(tview.FlexRow)
+
+ pv.detail.AddItem(planHeader(p), 4, 0, false)
+ pv.detail.AddItem(tview.NewTextView().SetText("lorem ipsum blah blah"), 0, 100, false)
+
+ // pv.detail.SetCellSimple(1, 0, "Startup Cost")
+ // pv.detail.SetCellSimple(1, 1, f2a(p.StartupCost))
+
+ // pv.detail.SetCellSimple(2, 0, "Total Cost")
+ // pv.detail.SetCellSimple(2, 1, f2a(p.TotalCost))
+
+ // pv.detail.SetCellSimple(5, 0, "Plan Width")
+ // pv.detail.SetCellSimple(5, 1, strconv.Itoa(p.PlanWidth))
+
+ // pv.detail.SetCellSimple(6, 0, "Actual Startup Time")
+ // pv.detail.SetCellSimple(6, 1, f2d(p.ActualStartupTime).String())
+
+ // pv.detail.SetCellSimple(7, 0, "Actual Total Time")
+ // pv.detail.SetCellSimple(7, 1, f2d(p.ActualTotalTime).String())
+
+ // pv.detail.SetCellSimple(8, 0, "Actual Loops")
+ // pv.detail.SetCellSimple(8, 1, strconv.Itoa(p.ActualLoops))
+
+ // pv.detail.SetCellSimple(19, 0, "I/O Read Time")
+ // pv.detail.SetCellSimple(19, 1, f2d(p.IOReadTime).String())
+
+ // pv.detail.SetCellSimple(20, 0, "I/O Write Time")
+ // pv.detail.SetCellSimple(20, 1, f2d(p.IOWriteTime).String())
+
+ // idx := 21
+
+ // if p.ParentRelationship != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Parent Relationship")
+ // pv.detail.SetCellSimple(idx, 1, *p.ParentRelationship)
+ // idx++
+ // }
+
+ // if len(p.Output) > 0 {
+ // pv.detail.SetCellSimple(idx, 0, "Output")
+ // pv.detail.SetCellSimple(idx, 1, strings.Join(p.Output, ", "))
+ // idx++
+ // }
+
+ // if len(p.SortKey) > 0 {
+ // pv.detail.SetCellSimple(idx, 0, "Sort Key")
+ // pv.detail.SetCellSimple(idx, 1, strings.Join(p.SortKey, ", "))
+ // idx++
+ // }
+
+ // if p.SortMethod != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Sort Method")
+ // pv.detail.SetCellSimple(idx, 1, *p.SortMethod)
+ // idx++
+ // }
+
+ // if p.SortSpaceUsed != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Sort Space Used")
+ // pv.detail.SetCellSimple(idx, 1, strconv.Itoa(*p.SortSpaceUsed))
+ // idx++
+ // }
+
+ // if p.SortSpaceType != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Sort Space Type")
+ // pv.detail.SetCellSimple(idx, 1, *p.SortSpaceType)
+ // idx++
+ // }
+
+ // if p.JoinType != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Join Type")
+ // pv.detail.SetCellSimple(idx, 1, *p.JoinType)
+ // idx++
+ // }
+
+ // if p.Strategy != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Strategy")
+ // pv.detail.SetCellSimple(idx, 1, *p.Strategy)
+ // idx++
+ // }
+
+ // if p.RelationName != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Relation Name")
+ // pv.detail.SetCellSimple(idx, 1, *p.RelationName)
+ // idx++
+ // }
+
+ // if p.Schema != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Schema")
+ // pv.detail.SetCellSimple(idx, 1, *p.Schema)
+ // idx++
+ // }
+
+ // if p.Alias != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Alias")
+ // pv.detail.SetCellSimple(idx, 1, *p.Alias)
+ // idx++
+ // }
+
+ // if p.RecheckCond != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Recheck Cond")
+ // pv.detail.SetCellSimple(idx, 1, *p.RecheckCond)
+ // idx++
+ // }
+
+ // if p.RowsRemovedByIndexRecheck != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Rows Removed by Index Recheck")
+ // pv.detail.SetCellSimple(idx, 1, strconv.Itoa(*p.RowsRemovedByIndexRecheck))
+ // idx++
+ // }
+
+ // if p.ExactHeapBlocks != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Exact Heap Blocks")
+ // pv.detail.SetCellSimple(idx, 1, strconv.Itoa(*p.ExactHeapBlocks))
+ // idx++
+ // }
+
+ // if p.LossyHeapBlocks != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Lossy Heap Blocks")
+ // pv.detail.SetCellSimple(idx, 1, strconv.Itoa(*p.LossyHeapBlocks))
+ // idx++
+ // }
+
+ // if p.IndexName != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Index Name")
+ // pv.detail.SetCellSimple(idx, 1, *p.IndexName)
+ // idx++
+ // }
+
+ // if p.IndexCond != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Index Cond")
+ // pv.detail.SetCellSimple(idx, 1, *p.IndexCond)
+ // idx++
+ // }
+
+ // if p.ScanDirection != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Scan Direction")
+ // pv.detail.SetCellSimple(idx, 1, *p.ScanDirection)
+ // idx++
+ // }
+
+ // if p.ParallelAware != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Parallel Aware")
+ // pv.detail.SetCellSimple(idx, 1, fmt.Sprintf("%t", *p.ParallelAware))
+ // idx++
+ // }
+
+ // if p.FunctionName != nil {
+ // pv.detail.SetCellSimple(idx, 0, "Function Name")
+ // pv.detail.SetCellSimple(idx, 1, *p.FunctionName)
+ // idx++
+ // }
}
func f2a(f float32) string {