From 0da52f233b01bd8dcaa9cfa0882726be12358092 Mon Sep 17 00:00:00 2001 From: Ben Burwell Date: Tue, 2 Jun 2020 23:42:42 -0400 Subject: Start node detail enhancements --- main.go | 10 +- plan_view.go | 407 ++++++++++++++++++++++++++++++++--------------------------- 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 { -- cgit v1.2.3