mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 06:05:49 -06:00
581 lines
16 KiB
C++
581 lines
16 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// pgAdmin III - PostgreSQL Tools
|
|
//
|
|
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
|
// This software is released under the PostgreSQL Licence
|
|
//
|
|
// explainShape.cpp - Explain Shapes
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// wxWindows headers
|
|
#include <wx/wx.h>
|
|
|
|
// App headers
|
|
#include "pgAdmin3.h"
|
|
#include "ctl/explainCanvas.h"
|
|
|
|
#include <wx/docview.h>
|
|
|
|
#include "images/ex_aggregate.pngc"
|
|
#include "images/ex_append.pngc"
|
|
#include "images/ex_bmp_and.pngc"
|
|
#include "images/ex_bmp_heap.pngc"
|
|
#include "images/ex_bmp_index.pngc"
|
|
#include "images/ex_bmp_or.pngc"
|
|
#include "images/ex_cte_scan.pngc"
|
|
#include "images/ex_delete.pngc"
|
|
#include "images/ex_foreign_scan.pngc"
|
|
#include "images/ex_group.pngc"
|
|
#include "images/ex_hash.pngc"
|
|
#include "images/ex_hash_anti_join.pngc"
|
|
#include "images/ex_hash_semi_join.pngc"
|
|
#include "images/ex_hash_setop_except.pngc"
|
|
#include "images/ex_hash_setop_except_all.pngc"
|
|
#include "images/ex_hash_setop_intersect.pngc"
|
|
#include "images/ex_hash_setop_intersect_all.pngc"
|
|
#include "images/ex_hash_setop_unknown.pngc"
|
|
#include "images/ex_index_only_scan.pngc"
|
|
#include "images/ex_index_scan.pngc"
|
|
#include "images/ex_insert.pngc"
|
|
#include "images/ex_lock_rows.pngc"
|
|
#include "images/ex_join.pngc"
|
|
#include "images/ex_limit.pngc"
|
|
#include "images/ex_materialize.pngc"
|
|
#include "images/ex_merge.pngc"
|
|
#include "images/ex_merge_anti_join.pngc"
|
|
#include "images/ex_merge_append.pngc"
|
|
#include "images/ex_merge_semi_join.pngc"
|
|
#include "images/ex_gatcher.pngc"
|
|
#include "images/ex_nested.pngc"
|
|
#include "images/ex_nested_loop_anti_join.pngc"
|
|
#include "images/ex_nested_loop_semi_join.pngc"
|
|
#include "images/ex_recursive_union.pngc"
|
|
#include "images/ex_result.pngc"
|
|
#include "images/ex_scan.pngc"
|
|
#include "images/ex_pscan.pngc"
|
|
#include "images/ex_seek.pngc"
|
|
#include "images/ex_setop.pngc"
|
|
#include "images/ex_sort.pngc"
|
|
#include "images/ex_subplan.pngc"
|
|
#include "images/ex_tid_scan.pngc"
|
|
#include "images/ex_unique.pngc"
|
|
#include "images/ex_unknown.pngc"
|
|
#include "images/ex_update.pngc"
|
|
#include "images/ex_values_scan.pngc"
|
|
#include "images/ex_window_aggregate.pngc"
|
|
#include "images/ex_worktable_scan.pngc"
|
|
|
|
// Greenplum images
|
|
#include "images/ex_broadcast_motion.pngc"
|
|
#include "images/ex_redistribute_motion.pngc"
|
|
#include "images/ex_gather_motion.pngc"
|
|
|
|
#define BMP_BORDER 3
|
|
|
|
ExplainShape::ExplainShape(const wxImage &bmp, const wxString &description, long tokenNo, long detailNo)
|
|
{
|
|
SetBitmap(wxBitmap(bmp));
|
|
SetLabel(description, tokenNo, detailNo);
|
|
kidCount = 0;
|
|
totalShapes = 0;
|
|
usedShapes = 0;
|
|
m_rootShape = false;
|
|
}
|
|
|
|
|
|
void ExplainShape::SetLabel(const wxString &str, int tokenNo, int detailNo)
|
|
{
|
|
if (tokenNo < 0)
|
|
{
|
|
description = str;
|
|
label = str;
|
|
}
|
|
else
|
|
{
|
|
wxStringTokenizer tokens(str, wxT(" "));
|
|
|
|
while (tokenNo-- >= 0)
|
|
{
|
|
label = tokens.GetNextToken();
|
|
|
|
if (detailNo <= 0)
|
|
{
|
|
if (!description.IsEmpty())
|
|
description.Append(wxT(" "));
|
|
|
|
description.Append(label);
|
|
}
|
|
}
|
|
if (detailNo > 0)
|
|
{
|
|
tokens.SetString(str, wxT(" "));
|
|
|
|
while (detailNo--)
|
|
{
|
|
if (!description.IsEmpty())
|
|
description.Append(wxT(" "));
|
|
|
|
description.Append(tokens.GetNextToken());
|
|
}
|
|
detail = tokens.GetString();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExplainShape::OnDraw(wxDC &dc)
|
|
{
|
|
wxBitmap &bmp = GetBitmap();
|
|
if (!bmp.Ok())
|
|
return;
|
|
|
|
// We do not draw the root shape
|
|
if (m_rootShape)
|
|
return;
|
|
|
|
int x, y;
|
|
x = WXROUND(m_xpos - bmp.GetWidth() / 2.0);
|
|
y = WXROUND(m_ypos - GetHeight() / 2.0);
|
|
|
|
dc.DrawBitmap(bmp, x, y, true);
|
|
|
|
int w, h;
|
|
dc.SetFont(GetCanvas()->GetFont());
|
|
dc.GetTextExtent(label, &w, &h);
|
|
|
|
x = WXROUND(m_xpos - w / 2.0);
|
|
y += bmp.GetHeight() + BMP_BORDER;
|
|
|
|
dc.DrawText(label, x, y);
|
|
}
|
|
|
|
|
|
void ExplainShape::OnLeftClick(double x, double y, int keys, int attachment)
|
|
{
|
|
((ExplainCanvas *)GetCanvas())->ShowPopup(this);
|
|
}
|
|
|
|
|
|
#define ARROWMARGIN 5
|
|
wxRealPoint ExplainShape::GetStartPoint()
|
|
{
|
|
wxRealPoint rp(GetX() + GetBitmap().GetWidth() / 2.0 + ARROWMARGIN, GetY() - (GetHeight() - GetBitmap().GetHeight()) / 2.);
|
|
return rp;
|
|
}
|
|
|
|
|
|
wxRealPoint ExplainShape::GetEndPoint(int kidNo)
|
|
{
|
|
wxRealPoint rp(GetX() - GetBitmap().GetWidth() / 2.0 - ARROWMARGIN, GetY() - (GetHeight() - GetBitmap().GetHeight()) / 2. + (kidCount > 1 ? GetBitmap().GetHeight() * 2. / 3. * kidNo / (2 * kidCount - 2) : 0 ));
|
|
return rp;
|
|
}
|
|
|
|
|
|
|
|
ExplainShape *ExplainShape::Create(long level, ExplainShape *last, const wxString &str)
|
|
{
|
|
ExplainShape *s = 0;
|
|
|
|
int costPos = str.Find(wxT("(cost="));
|
|
int actPos = str.Find(wxT("(actual"));
|
|
bool parallel=false;
|
|
int inc=0;
|
|
wxStringTokenizer tokens(str, wxT(" "));
|
|
wxString token = tokens.GetNextToken();
|
|
if (token == wxT("Parallel"))
|
|
{
|
|
token = tokens.GetNextToken();
|
|
parallel=true;
|
|
inc=1;
|
|
}
|
|
|
|
wxString token2 = tokens.GetNextToken();
|
|
wxString token3 = tokens.GetNextToken();
|
|
wxString token4;
|
|
if (tokens.HasMoreTokens())
|
|
token4 = tokens.GetNextToken();
|
|
wxString descr;
|
|
if (costPos > 0)
|
|
descr = str.Left(costPos);
|
|
else if (actPos > 0)
|
|
descr = str.Left(actPos);
|
|
else
|
|
descr = str;
|
|
|
|
// Requested an empty shape, which can be treated as a root shape
|
|
if (level == 0)
|
|
{
|
|
s = new ExplainShape(*ex_unknown_png_img, wxEmptyString);
|
|
s->SetDraggable(false);
|
|
s->m_rootShape = true;
|
|
s->level = level;
|
|
int w = 50, h = 20;
|
|
|
|
wxBitmap &bmp = s->GetBitmap();
|
|
if (w < bmp.GetWidth())
|
|
w = bmp.GetWidth();
|
|
|
|
s->SetHeight(bmp.GetHeight() + BMP_BORDER + h);
|
|
s->SetWidth(w);
|
|
|
|
s->upperShape = NULL;
|
|
s->kidNo = 0;
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
// possible keywords can be found in postgresql/src/backend/commands/explain.c
|
|
|
|
if (token == wxT("Total")) return 0;
|
|
else if (token == wxT("Trigger")) return 0;
|
|
else if (token == wxT("Settings:")) return 0; /* Greenplum */
|
|
else if (token == wxT("Slice")) return 0; /* Greenplum */
|
|
else if (token.Mid(0, 6) == wxT("(slice")) return 0; /* Greenplum */
|
|
else if (token == wxT("Result")) s = new ExplainShape(*ex_result_png_img, descr);
|
|
else if (token == wxT("Append")) s = new ExplainShape(*ex_append_png_img, descr);
|
|
else if (token == wxT("Gather")) s = new ExplainShape(*ex_gatcher_png_img, descr);
|
|
else if (token == wxT("Nested"))
|
|
{
|
|
if (token2 == wxT("Loop") && token4 == wxT("Join"))
|
|
{
|
|
// Nested Loop Anti Join
|
|
if (token3 == wxT("Anti"))
|
|
{
|
|
s = new ExplainShape(*ex_nested_loop_anti_join_png_img, descr);
|
|
}
|
|
// Nested Loop Semi Join
|
|
else
|
|
{
|
|
s = new ExplainShape(*ex_nested_loop_semi_join_png_img, descr);
|
|
}
|
|
}
|
|
if (!s)
|
|
s = new ExplainShape(*ex_nested_png_img, descr);
|
|
}
|
|
else if (token == wxT("Merge"))
|
|
{
|
|
if (token3 == wxT("Join"))
|
|
{
|
|
// Merge Anti Join
|
|
if (token2 == wxT("Anti"))
|
|
{
|
|
s = new ExplainShape(*ex_merge_anti_join_png_img, descr);
|
|
}
|
|
// Merge Semi Join
|
|
else
|
|
{
|
|
s = new ExplainShape(*ex_merge_semi_join_png_img, descr);
|
|
}
|
|
}
|
|
// Merge Append
|
|
else if (token2 == wxT("Append"))
|
|
{
|
|
s = new ExplainShape(*ex_merge_append_png_img, descr);
|
|
}
|
|
else
|
|
{
|
|
s = new ExplainShape(*ex_merge_png_img, descr);
|
|
}
|
|
}
|
|
else if (token == wxT("Hash"))
|
|
{
|
|
if (token2 == wxT("Join"))
|
|
{
|
|
s = new ExplainShape(*ex_join_png_img, descr);
|
|
}
|
|
else
|
|
{
|
|
if (token3 == wxT("Join"))
|
|
{
|
|
// Hash Anti Join
|
|
if (token2 == wxT("Anti"))
|
|
{
|
|
s = new ExplainShape(*ex_hash_anti_join_png_img, descr);
|
|
}
|
|
// Hash Semi Join
|
|
else if (token2 == wxT("Semi"))
|
|
{
|
|
s = new ExplainShape(*ex_hash_semi_join_png_img, descr);
|
|
}
|
|
else
|
|
{
|
|
s = new ExplainShape(*ex_hash_png_img, descr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s = new ExplainShape(*ex_hash_png_img, descr);
|
|
}
|
|
}
|
|
}
|
|
else if (token == wxT("HashSetOp"))
|
|
{
|
|
if (token2 == wxT("Except"))
|
|
{
|
|
// HashSetOp Except ALL
|
|
if (token3 == wxT("ALL"))
|
|
{
|
|
s = new ExplainShape(*ex_hash_setop_except_all_png_img, descr);
|
|
}
|
|
// HashSetOp Except
|
|
else
|
|
{
|
|
s = new ExplainShape(*ex_hash_setop_except_png_img, descr);
|
|
}
|
|
}
|
|
else if (token2 == wxT("Intersect"))
|
|
{
|
|
// HashSetOp Intersect ALL
|
|
if (token3 == wxT("ALL"))
|
|
{
|
|
s = new ExplainShape(*ex_hash_setop_intersect_all_png_img, descr);
|
|
}
|
|
// HashSetOp Intersect
|
|
else
|
|
{
|
|
s = new ExplainShape(*ex_hash_setop_intersect_png_img, descr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// HashSetOp ???
|
|
s = new ExplainShape(*ex_hash_setop_unknown_png_img, descr);
|
|
}
|
|
}
|
|
else if (token == wxT("Subquery")) s = new ExplainShape(*ex_subplan_png_img, descr, 0, 2);
|
|
else if (token == wxT("Function")) s = new ExplainShape(*ex_result_png_img, descr, 0, 2);
|
|
else if (token == wxT("Materialize")) s = new ExplainShape(*ex_materialize_png_img, descr);
|
|
else if (token == wxT("Sort")) s = new ExplainShape(*ex_sort_png_img, descr);
|
|
else if (token == wxT("Group")) s = new ExplainShape(*ex_group_png_img, descr);
|
|
else if (token == wxT("Aggregate") || token == wxT("GroupAggregate") || token == wxT("HashAggregate"))
|
|
s = new ExplainShape(*ex_aggregate_png_img, descr);
|
|
else if (token == wxT("Unique")) s = new ExplainShape(*ex_unique_png_img, descr);
|
|
else if (token == wxT("SetOp")) s = new ExplainShape(*ex_setop_png_img, descr);
|
|
else if (token == wxT("Limit")) s = new ExplainShape(*ex_limit_png_img, descr);
|
|
else if (token == wxT("LockRows")) s = new ExplainShape(*ex_lock_rows_png_img, descr);
|
|
else if (token == wxT("Bitmap"))
|
|
{
|
|
if (token2 == wxT("Index")) s = new ExplainShape(*ex_bmp_index_png_img, descr, 4+inc, 3+inc);
|
|
else s = new ExplainShape(*ex_bmp_heap_png_img, descr, 4+inc, 3+inc);
|
|
}
|
|
else if (token == wxT("BitmapAnd")) s = new ExplainShape(*ex_bmp_and_png_img, descr);
|
|
else if (token == wxT("BitmapOr")) s = new ExplainShape(*ex_bmp_or_png_img, descr);
|
|
else if (token2 == wxT("Scan"))
|
|
{
|
|
if (token == wxT("Index"))
|
|
// Scan Index Backward
|
|
if (token3 == wxT("Backward"))
|
|
s = new ExplainShape(*ex_index_scan_png_img, descr, 4+inc, 3+inc);
|
|
else
|
|
s = new ExplainShape(*ex_index_scan_png_img, descr, 3+inc, 2+inc);
|
|
// Tid Scan
|
|
else if (token == wxT("Tid"))
|
|
s = new ExplainShape(*ex_tid_scan_png_img, descr, 3+inc, 2+inc);
|
|
// WorkTable Scan
|
|
else if (token == wxT("WorkTable"))
|
|
s = new ExplainShape(*ex_worktable_scan_png_img, descr, 3+inc, 2+inc);
|
|
// CTE Scan
|
|
else if (token == wxT("CTE"))
|
|
s = new ExplainShape(*ex_cte_scan_png_img, descr, 3, 2);
|
|
// Foreign Scan
|
|
else if (token == wxT("Foreign"))
|
|
s = new ExplainShape(*ex_foreign_scan_png_img, descr, 3, 2);
|
|
// Values Scan
|
|
else if (token == wxT("Values"))
|
|
s = new ExplainShape(*ex_values_scan_png_img, descr, 3, 2);
|
|
else if (parallel) s = new ExplainShape(*ex_pscan_png_img, descr, 4, 3);
|
|
else
|
|
s = new ExplainShape(*ex_scan_png_img, descr, 3+inc, 2+inc);
|
|
}
|
|
else if (token == wxT("Index"))
|
|
{
|
|
// Index Only Scan
|
|
if (token2 == wxT("Only") && token3 == wxT("Scan"))
|
|
{
|
|
s = new ExplainShape(*ex_index_only_scan_png_img, descr, 4+inc, 3+inc);
|
|
}
|
|
}
|
|
else if (token2 == wxT("Seek")) s = new ExplainShape(*ex_seek_png_img, descr, 3, 2);
|
|
// Recursive Union
|
|
else if (token == wxT("Recursive") && token2 == wxT("Union"))
|
|
s = new ExplainShape(*ex_recursive_union_png_img, descr);
|
|
else if (token == wxT("WindowAgg"))
|
|
s = new ExplainShape(*ex_window_aggregate_png_img, descr);
|
|
|
|
// DML
|
|
else if (token == wxT("Insert"))
|
|
s = new ExplainShape(*ex_insert_png_img, descr, 2, 1);
|
|
else if (token == wxT("Update"))
|
|
s = new ExplainShape(*ex_update_png_img, descr, 2, 1);
|
|
else if (token == wxT("Delete"))
|
|
s = new ExplainShape(*ex_delete_png_img, descr, 2, 1);
|
|
|
|
// Greenplum additions
|
|
else if (token == wxT("Gather") && token2 == wxT("Motion"))
|
|
s = new ExplainShape(*ex_gather_motion_png_img, descr);
|
|
else if (token == wxT("Broadcast") && token2 == wxT("Motion"))
|
|
s = new ExplainShape(*ex_broadcast_motion_png_img, descr);
|
|
else if (token == wxT("Redistribute") && token2 == wxT("Motion"))
|
|
s = new ExplainShape(*ex_redistribute_motion_png_img, descr);
|
|
|
|
if (!s)
|
|
s = new ExplainShape(*ex_unknown_png_img, descr);
|
|
|
|
s->SetDraggable(false);
|
|
|
|
s->level = level;
|
|
|
|
if (costPos > 0)
|
|
{
|
|
if (actPos > 0)
|
|
{
|
|
s->actual = str.Mid(actPos);
|
|
s->cost = str.Mid(costPos, actPos - costPos);
|
|
}
|
|
else
|
|
s->cost = str.Mid(costPos);
|
|
}
|
|
else if (actPos > 0)
|
|
s->actual = str.Mid(actPos);
|
|
|
|
int w = 50, h = 20;
|
|
|
|
wxBitmap &bmp = s->GetBitmap();
|
|
if (w < bmp.GetWidth())
|
|
w = bmp.GetWidth();
|
|
|
|
s->SetHeight(bmp.GetHeight() + BMP_BORDER + h);
|
|
s->SetWidth(w);
|
|
|
|
s->upperShape = last;
|
|
if (last)
|
|
{
|
|
s->kidNo = last->kidCount;
|
|
last->kidCount++;
|
|
}
|
|
else
|
|
s->kidNo = 0;
|
|
|
|
if (costPos > 0)
|
|
{
|
|
wxChar *cl = const_cast<wxChar *>((const wxChar *)str + costPos + 6);
|
|
wxChar *ch = wxStrstr(cl, wxT(".."));
|
|
if (ch)
|
|
{
|
|
*ch = 0;
|
|
ch += 2;
|
|
}
|
|
s->costLow = StrToDouble(cl);
|
|
if (ch)
|
|
{
|
|
wxChar *r = wxStrstr(ch, wxT(" rows="));
|
|
if (r)
|
|
{
|
|
*r = 0;
|
|
r += 6;
|
|
}
|
|
s->costHigh = StrToDouble(ch);
|
|
if (r)
|
|
{
|
|
wxChar *w = wxStrstr(r, wxT(" width="));
|
|
if (w)
|
|
{
|
|
*w = 0;
|
|
w += 7;
|
|
}
|
|
s->rows = StrToLong(r);
|
|
if (w)
|
|
s->width = StrToLong(w);
|
|
}
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
|
|
ExplainLine::ExplainLine(ExplainShape *from, ExplainShape *to, double weight)
|
|
{
|
|
SetCanvas(from->GetCanvas());
|
|
from->AddLine(this, to);
|
|
MakeLineControlPoints(4);
|
|
|
|
width = (int) log(from->GetAverageCost());
|
|
if (width > 10)
|
|
width = 10;
|
|
|
|
// If we got an average cost of 0, width is probably negative
|
|
// which will look pretty darn ugly as an arrow width!
|
|
// This may happen on Greenplum.
|
|
if (width < 1)
|
|
width = 1;
|
|
|
|
wxNode *first = GetLineControlPoints()->GetFirst();
|
|
wxNode *last = GetLineControlPoints()->GetLast();
|
|
*(wxRealPoint *)first->GetData() = from->GetStartPoint();
|
|
*(wxRealPoint *)last->GetData() = to->GetEndPoint(from->GetKidno());
|
|
|
|
wxRealPoint *p1 = (wxRealPoint *)first->GetNext()->GetData();
|
|
wxRealPoint *p2 = (wxRealPoint *)last->GetPrevious()->GetData();
|
|
*p1 = from->GetStartPoint();
|
|
*p2 = to->GetEndPoint(from->GetKidno());
|
|
p1->x -= (p1->x - p2->x) / 3. + 8;
|
|
p2->x += (p1->x - p2->x) / 3. - 8;
|
|
|
|
Initialise();
|
|
}
|
|
|
|
|
|
#define ARROWWIDTH 4
|
|
|
|
|
|
void ExplainLine::OnDraw(wxDC &dc)
|
|
{
|
|
if (m_lineControlPoints)
|
|
{
|
|
dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 1, wxPENSTYLE_SOLID));
|
|
dc.SetBrush(*wxTheBrushList->FindOrCreateBrush(*wxLIGHT_GREY, wxBRUSHSTYLE_SOLID));
|
|
|
|
wxPoint *points = new wxPoint[11];
|
|
wxRealPoint *point0 = (wxRealPoint *) m_lineControlPoints->Item(0)->GetData();
|
|
wxRealPoint *point1 = (wxRealPoint *) m_lineControlPoints->Item(1)->GetData();
|
|
wxRealPoint *point2 = (wxRealPoint *) m_lineControlPoints->Item(2)->GetData();
|
|
wxRealPoint *point3 = (wxRealPoint *) m_lineControlPoints->Item(3)->GetData();
|
|
|
|
double phi = atan2(point2->y - point1->y, point2->x - point1->x);
|
|
double offs = width * tan(phi / 2);
|
|
|
|
points[0].x = WXROUND(point0->x);
|
|
points[0].y = WXROUND(point0->y) - width;
|
|
points[10].x = WXROUND(point0->x);
|
|
points[10].y = WXROUND(point0->y) + width;
|
|
|
|
points[1].x = WXROUND(point1->x + offs);
|
|
points[1].y = WXROUND(point1->y) - width;
|
|
points[9].x = WXROUND(point1->x - offs);
|
|
points[9].y = WXROUND(point1->y) + width;
|
|
|
|
points[2].x = WXROUND(point2->x + offs);
|
|
points[2].y = WXROUND(point2->y) - width;
|
|
points[8].x = WXROUND(point2->x - offs);
|
|
points[8].y = WXROUND(point2->y) + width;
|
|
|
|
points[3].x = WXROUND(point3->x) - width - ARROWWIDTH;
|
|
points[3].y = WXROUND(point3->y) - width;
|
|
points[7].x = WXROUND(point3->x) - width - ARROWWIDTH;
|
|
points[7].y = WXROUND(point3->y) + width;
|
|
|
|
points[4].x = points[3].x;
|
|
points[4].y = points[3].y - ARROWWIDTH;
|
|
points[6].x = points[7].x;
|
|
points[6].y = points[7].y + ARROWWIDTH;
|
|
|
|
points[5].x = WXROUND(point3->x);
|
|
points[5].y = WXROUND(point3->y);
|
|
|
|
dc.DrawPolygon(11, points, 0, 0);
|
|
}
|
|
}
|