Add column sort ctlListView. Add Partition statistics.

Добавлена сортировка при щелчке по заголовку таблице.
Дабавлена заполнение статистики при выборе секчионированной таблицы.
Статистика берётся по всем уровням иерархии.
This commit is contained in:
lsv 2020-12-31 14:08:21 +05:00 committed by levinsv
parent 81228dd6a5
commit f64972182a
7 changed files with 269 additions and 35 deletions

View file

@ -19,13 +19,106 @@
#include "ctl/ctlListView.h"
#include "utils/misc.h"
ctlListView::ctlListView(wxWindow *p, int id, wxPoint pos, wxSize siz, long attr)
: wxListView(p, id, pos, siz, attr | wxLC_REPORT)
int wxCALLBACK
MyCompareFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr WXUNUSED(sortData))
{
// inverse the order
if (item1 < item2)
return 1;
if (item1 > item2)
return -1;
return 0;
}
ctlListView::ctlListView(wxWindow* p, int id, wxPoint pos, wxSize siz, long attr)
: wxListView(p, id, pos, siz, attr | wxLC_REPORT)
{
nosort = false;
order = 1;
prev_col = -1;
Connect(wxID_ANY, wxEVT_LIST_COL_CLICK, wxListEventHandler(ctlListView::OnSortGrid));
}
#include <map>
void ctlListView::OnSortGrid(wxListEvent& event)
{
if (!nosort) {
int col = event.GetColumn();
if (col == prev_col) order = order * -1;
int rows = GetItemCount();
wxListItem listitem;
listitem.SetMask(wxLIST_MASK_TEXT);
GetColumn(col, listitem);
wxString label = listitem.GetText();
bool issize = label == _("Size");
bool astext = true;
if (label == _("CFS fragmentation") ||
label == (_("Tuples inserted")) ||
label == (_("Tuples updated")) ||
label == (_("Tuples deleted")) ||
label == (_("Tuples HOT updated")) ||
label == (_("Live tuples")) ||
label == (_("Dead tuples")) ||
label == (_("CFS %")) ||
label == (_("Autovacuum counter")) ||
label == (_("Autoanalyze counter")) ||
label == (_("Index Scans")) ||
label == (_("Index Tuples Read")) ||
label == (_("Index Tuples Fetched")) ||
issize
) {
astext = false;
}
//sort numeric column
if (astext) {
std::multimap<wxString, int> mp;
for (int i = 0; i < rows; i++) {
wxString val = GetItemText(i, col);
mp.insert(std::pair<wxString, int>(val, i));
}
// ñîïîñòàâèì ñîðòèðîâàííûì çíà÷åíèÿì ïîñëåäîâàòåëüíîñòü ÷èñåë äëÿ ñîðòèðîâêè SortItems
std::multimap<wxString, int>::iterator it = mp.begin();
for (int i = 1; it != mp.end(); it++, i++) { // âûâîäèì èõ
int row = it->second; // row
wxListView::SetItemData(row, (long)i * order);
}
}
else
{
std::multimap<double, int> mp;
double d;
for (int i = 0; i < rows; i++) {
wxString val = GetItemText(i, col);
if (val == "NaN") val = "0";
if (val.ToCDouble(&d))
{
// ýòî ÷èñëî
}
else
{
if (issize)
if (val.Right(2) == "kB") d = d / 1024;
else if (val.Right(2) == "GB") d = d * 1024;
else if (val.Right(5) == "bytes") d = d / 1024 / 1024;
}
mp.insert(std::pair<double, int>(d, i));
}
// ñîïîñòàâèì ñîðòèðîâàííûì çíà÷åíèÿì ïîñëåäîâàòåëüíîñòü ÷èñåë äëÿ ñîðòèðîâêè SortItems
std::multimap<double, int>::iterator it = mp.begin();
for (int i = 1; it != mp.end(); it++, i++) { // âûâîäèì èõ
int row = it->second; // row
wxListView::SetItemData(row, (long)i * order);
}
}
SortItems(MyCompareFunction, 0);
Refresh();
prev_col = col;
}
}
long ctlListView::GetSelection()
{
return GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
@ -43,7 +136,7 @@ wxString ctlListView::GetText(long row, long col)
};
void ctlListView::AddColumn(const wxString &text, int size, int format)
void ctlListView::AddColumn(const wxString& text, int size, int format)
{
if (size == wxLIST_AUTOSIZE || size == wxLIST_AUTOSIZE_USEHEADER)
{
@ -56,7 +149,7 @@ void ctlListView::AddColumn(const wxString &text, int size, int format)
}
long ctlListView::AppendItem(int icon, const wxString &val, const wxString &val2, const wxString &val3, const wxString &val4)
long ctlListView::AppendItem(int icon, const wxString& val, const wxString& val2, const wxString& val3, const wxString& val4)
{
long pos = InsertItem(GetItemCount(), val, icon);
if (!val2.IsEmpty())
@ -70,7 +163,7 @@ long ctlListView::AppendItem(int icon, const wxString &val, const wxString &val2
}
void ctlListView::CreateColumns(wxImageList *images, const wxString &left, const wxString &right, int leftSize)
void ctlListView::CreateColumns(wxImageList* images, const wxString& left, const wxString& right, int leftSize)
{
int rightSize;
if (leftSize < 0)
@ -107,7 +200,7 @@ void ctlListView::CreateColumns(wxImageList *images, const wxString &left, const
}
void ctlListView::CreateColumns(wxImageList *images, const wxString &str1, const wxString &str2, const wxString &str3, int leftSize)
void ctlListView::CreateColumns(wxImageList* images, const wxString& str1, const wxString& str2, const wxString& str3, int leftSize)
{
int rightSize;
if (leftSize < 0)

View file

@ -21,46 +21,53 @@ class frmMain;
class ctlListView : public wxListView
{
private:
void OnSortGrid(wxListEvent& event);
bool nosort; // åñëè êòî òî ïîëüçóåòñÿ SetItemData òî íå áóäåì ñîðòèðîâàòü òàêèå ctlListView
int order, prev_col;
public:
ctlListView(wxWindow *p, int id, wxPoint pos, wxSize siz, long attr = 0);
bool SetItemData(long item, long data) {
nosort = true;
return wxListView::SetItemData(item, data);
}
ctlListView(wxWindow* p, int id, wxPoint pos, wxSize siz, long attr = 0);
long GetSelection();
wxString GetText(long row, long col = 0);
void CreateColumns(wxImageList* images, const wxString& left, const wxString& right, int leftSize = 60);
void CreateColumns(wxImageList* images, const wxString& str1, const wxString& str2, const wxString& str3, int leftSize = 60);
void CreateColumns(wxImageList *images, const wxString &left, const wxString &right, int leftSize = 60);
void CreateColumns(wxImageList *images, const wxString &str1, const wxString &str2, const wxString &str3, int leftSize = 60);
void AddColumn(const wxString& text, int size = wxLIST_AUTOSIZE_USEHEADER, int format = wxLIST_FORMAT_LEFT);
void AddColumn(const wxString &text, int size = wxLIST_AUTOSIZE_USEHEADER, int format = wxLIST_FORMAT_LEFT);
long AppendItem(int icon, const wxString &val, const wxString &val2 = wxString(), const wxString &val3 = wxString(), const wxString &val4 = wxString());
long AppendItem(const wxString &val, const wxString &val2 = wxString(), const wxString &val3 = wxString())
long AppendItem(int icon, const wxString& val, const wxString& val2 = wxString(), const wxString& val3 = wxString(), const wxString& val4 = wxString());
long AppendItem(const wxString& val, const wxString& val2 = wxString(), const wxString& val3 = wxString())
{
return AppendItem(PGICON_PROPERTY, val, val2, val3);
}
void AppendItem(const wxString &str, long l)
void AppendItem(const wxString& str, long l)
{
AppendItem(str, NumToStr(l));
}
void AppendItem(const wxString &str, double d)
void AppendItem(const wxString& str, double d)
{
AppendItem(str, NumToStr(d));
}
void AppendItem(const wxString &str, OID o)
void AppendItem(const wxString& str, OID o)
{
AppendItem(str, NumToStr(o));
}
void AppendItem(const wxString &str, const wxDateTime &d)
void AppendItem(const wxString& str, const wxDateTime& d)
{
AppendItem(str, DateToStr(d));
}
void AppendItem(const wxString &str, const wxLongLong &l)
void AppendItem(const wxString& str, const wxLongLong& l)
{
AppendItem(str, l.ToString());
}
void AppendItem(const wxString &str, const wxULongLong &l)
void AppendItem(const wxString& str, const wxULongLong& l)
{
AppendItem(str, l.ToString());
}
void AppendYesNoItem(const wxString &str, bool b)
void AppendYesNoItem(const wxString& str, bool b)
{
AppendItem(str, BoolToYesNo(b));
}
@ -69,6 +76,7 @@ public:
{
DeleteItem(GetSelection());
}
};

View file

@ -269,6 +269,7 @@ public:
virtual void ShowTreeDetail(ctlTree *browser, frmMain *form = 0, ctlListView *properties = 0, ctlSQLBox *sqlPane = 0) = 0;
virtual void ShowStatistics(frmMain *form, ctlListView *statistics);
virtual void ShowStatisticsTables(frmMain* form, ctlListView* statistics, pgObject* obj);
virtual void ShowDependencies(frmMain *form, ctlListView *Dependencies, const wxString &where = wxEmptyString);
virtual void ShowDependents(frmMain *form, ctlListView *referencedBy, const wxString &where = wxEmptyString);
virtual pgObject *Refresh(ctlTree *browser, const wxTreeItemId item)

View file

@ -787,4 +787,5 @@ void pgIndexBaseCollection::ShowStatistics(frmMain *form, ctlListView *statistic
delete stats;
}
statistics->SetColumnWidth(0, wxLIST_AUTOSIZE);
}

View file

@ -67,6 +67,8 @@
#include "agent/pgaSchedule.h"
#include "agent/pgaStep.h"
#include "schema/pgPartition.h"
#include "utils/pgfeatures.h"
int pgObject::GetType() const
{
@ -224,6 +226,120 @@ void pgObject::ShowStatistics(frmMain *form, ctlListView *statistics)
{
}
void pgObject::ShowStatisticsTables(frmMain* form, ctlListView* statistics, pgObject *obj)
{
wxLogInfo(wxT("Displaying statistics for tables on %s"), obj->GetSchema()->GetIdentifier().c_str());
bool tabcoll = false;
bool partcoll = false;
bool onetable = false;
if (IsCollection()) {
wxString t = obj->GetFactory()->GetTypeName();
if (t == ("Tables")
//obj->IsCreatedBy(tableFactory)
) tabcoll = true;
if ( t == ("Partitions")) partcoll = true;
}
else onetable = true;
bool hasSize = obj->GetConnection()->HasFeature(FEATURE_SIZE);
// Add the statistics view columns
statistics->ClearAll();
statistics->AddColumn(_("Table Name"));
if (hasSize)
statistics->AddColumn(_("Size"));
if (obj->GetConnection()->GetIsPgProEnt()) statistics->AddColumn(_("CFS %"));
statistics->AddColumn(_("Tuples inserted"));
statistics->AddColumn(_("Tuples updated"));
statistics->AddColumn(_("Tuples deleted"));
if (obj->GetConnection()->BackendMinimumVersion(8, 3))
{
statistics->AddColumn(_("Tuples HOT updated"));
statistics->AddColumn(_("Live tuples"));
statistics->AddColumn(_("Dead tuples"));
}
if (obj->GetConnection()->BackendMinimumVersion(8, 2))
{
statistics->AddColumn(_("Last vacuum"));
statistics->AddColumn(_("Last autovacuum"));
statistics->AddColumn(_("Last analyze"));
statistics->AddColumn(_("Last autoanalyze"));
}
if (obj->GetConnection()->BackendMinimumVersion(9, 1))
{
statistics->AddColumn(_("Vacuum counter"));
statistics->AddColumn(_("Autovacuum counter"));
statistics->AddColumn(_("Analyze counter"));
statistics->AddColumn(_("Autoanalyze counter"));
}
wxString sql = wxT("SELECT st.relname, n_tup_ins, n_tup_upd, n_tup_del");
if (obj->GetConnection()->BackendMinimumVersion(8, 3))
sql += wxT(", n_tup_hot_upd, n_live_tup, n_dead_tup");
if (obj->GetConnection()->BackendMinimumVersion(8, 2))
sql += wxT(", last_vacuum, last_autovacuum, last_analyze, last_autoanalyze");
if (obj->GetConnection()->BackendMinimumVersion(9, 1))
sql += wxT(", vacuum_count, autovacuum_count, analyze_count, autoanalyze_count");
if (hasSize)
sql += wxT(", pg_size_pretty(pg_relation_size(st.relid)")
wxT(" + CASE WHEN cl.reltoastrelid = 0 THEN 0 ELSE pg_relation_size(cl.reltoastrelid) + COALESCE((SELECT SUM(pg_relation_size(indexrelid)) FROM pg_index WHERE indrelid=cl.reltoastrelid)::int8, 0) END")
wxT(" + COALESCE((SELECT SUM(pg_relation_size(indexrelid)) FROM pg_index WHERE indrelid=st.relid)::int8, 0)) AS size");
if (obj->GetConnection()->GetIsPgProEnt()) sql += wxT(",left((cfs_fragmentation(cl.oid)*100)::text,5)::text AS cfs_ratio");
sql += wxT("\n FROM pg_stat_all_tables st")
wxT(" JOIN pg_class cl on cl.oid=st.relid\n");
if (partcoll) sql += wxT(" JOIN pg_inherits i ON (cl.oid = i.inhrelid) WHERE ");
if (tabcoll) sql += wxT(" WHERE schemaname = ")+obj->qtDbString(obj->GetSchema()->GetName());
if (partcoll) sql += wxT(" i.inhparent = ") + obj->GetOidStr();
//+ obj->qtDbString(obj->GetSchema()->GetName()) + wxT(" AND i.inhparent = ") + obj->GetOidStr()
if (onetable) sql += wxT("join (select t.relid::oid oid from pg_partition_tree(")+ (obj->GetOidStr())+wxT("::regclass) t where t.isleaf = 't') t on t.oid=cl.oid");
sql += wxT("\n ORDER BY relname");
pgSet* stats = obj->GetDatabase()->ExecuteSet(sql);
if (stats)
{
long pos = 0;
int i;
while (!stats->Eof())
{
i = 1;
statistics->InsertItem(pos, stats->GetVal(wxT("relname")), PGICON_STATISTICS);
if (hasSize)
statistics->SetItem(pos, i++, stats->GetVal(wxT("size")));
if (obj->GetConnection()->GetIsPgProEnt()) statistics->SetItem(pos, i++, stats->GetVal(wxT("cfs_ratio")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_ins")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_upd")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_del")));
if (obj->GetConnection()->BackendMinimumVersion(8, 3))
{
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_hot_upd")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_live_tup")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_dead_tup")));
}
if (obj->GetConnection()->BackendMinimumVersion(8, 2))
{
statistics->SetItem(pos, i++, stats->GetVal(wxT("last_vacuum")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("last_autovacuum")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("last_analyze")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("last_autoanalyze")));
}
if (obj->GetConnection()->BackendMinimumVersion(9, 1))
{
statistics->SetItem(pos, i++, stats->GetVal(wxT("vacuum_count")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("autovacuum_count")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("analyze_count")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("autoanalyze_count")));
}
stats->MoveNext();
pos++;
}
delete stats;
}
statistics->SetColumnWidth(0, wxLIST_AUTOSIZE);
}
bool pgObject::UpdateIcon(ctlTree *browser)
{

View file

@ -97,6 +97,8 @@ pgPartitionCollection::pgPartitionCollection(pgaFactory *factory, pgPartition *_
}
void pgPartitionCollection::ShowStatistics(frmMain *form, ctlListView *statistics)
{
ShowStatisticsTables(form, statistics, this);
return;
wxLogInfo(wxT("Displaying statistics for tables on %s"), GetSchema()->GetIdentifier().c_str());
bool hasSize = GetConnection()->HasFeature(FEATURE_SIZE);
@ -104,6 +106,9 @@ void pgPartitionCollection::ShowStatistics(frmMain *form, ctlListView *statistic
// Add the statistics view columns
statistics->ClearAll();
statistics->AddColumn(_("Table Name"));
if (hasSize)
statistics->AddColumn(_("Size"));
if (GetConnection()->GetIsPgProEnt()) statistics->AddColumn(_("CFS %"));
statistics->AddColumn(_("Tuples inserted"));
statistics->AddColumn(_("Tuples updated"));
statistics->AddColumn(_("Tuples deleted"));
@ -127,8 +132,6 @@ void pgPartitionCollection::ShowStatistics(frmMain *form, ctlListView *statistic
statistics->AddColumn(_("Analyze counter"));
statistics->AddColumn(_("Autoanalyze counter"));
}
if (hasSize)
statistics->AddColumn(_("Size"), 50);
wxString sql = wxT("SELECT st.relname, n_tup_ins, n_tup_upd, n_tup_del");
if (GetConnection()->BackendMinimumVersion(8, 3))
@ -141,7 +144,7 @@ void pgPartitionCollection::ShowStatistics(frmMain *form, ctlListView *statistic
sql += wxT(", pg_size_pretty(pg_relation_size(st.relid)")
wxT(" + CASE WHEN cl.reltoastrelid = 0 THEN 0 ELSE pg_relation_size(cl.reltoastrelid) + COALESCE((SELECT SUM(pg_relation_size(indexrelid)) FROM pg_index WHERE indrelid=cl.reltoastrelid)::int8, 0) END")
wxT(" + COALESCE((SELECT SUM(pg_relation_size(indexrelid)) FROM pg_index WHERE indrelid=st.relid)::int8, 0)) AS size");
if (GetConnection()->GetIsPgProEnt()) sql += wxT(",left((cfs_fragmentation(cl.oid)*100)::text,5)::text AS cfs_ratio");
sql += wxT("\n FROM pg_stat_all_tables st")
wxT(" JOIN pg_class cl on cl.oid=st.relid\n")
wxT(" JOIN pg_inherits i ON (cl.oid = i.inhrelid)")
@ -156,11 +159,14 @@ void pgPartitionCollection::ShowStatistics(frmMain *form, ctlListView *statistic
int i;
while (!stats->Eof())
{
i = 4;
i = 1;
statistics->InsertItem(pos, stats->GetVal(wxT("relname")), PGICON_STATISTICS);
statistics->SetItem(pos, 1, stats->GetVal(wxT("n_tup_ins")));
statistics->SetItem(pos, 2, stats->GetVal(wxT("n_tup_upd")));
statistics->SetItem(pos, 3, stats->GetVal(wxT("n_tup_del")));
if (hasSize)
statistics->SetItem(pos, i++, stats->GetVal(wxT("size")));
if (GetConnection()->GetIsPgProEnt()) statistics->SetItem(pos, i++, stats->GetVal(wxT("cfs_ratio")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_ins")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_upd")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_del")));
if (GetConnection()->BackendMinimumVersion(8, 3))
{
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_hot_upd")));
@ -181,14 +187,13 @@ void pgPartitionCollection::ShowStatistics(frmMain *form, ctlListView *statistic
statistics->SetItem(pos, i++, stats->GetVal(wxT("analyze_count")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("autoanalyze_count")));
}
if (hasSize)
statistics->SetItem(pos, i, stats->GetVal(wxT("size")));
stats->MoveNext();
pos++;
}
delete stats;
}
statistics->SetColumnWidth(0, wxLIST_AUTOSIZE);
}

View file

@ -1290,6 +1290,8 @@ wxString pgTableCollection::GetTranslatedMessage(int kindOfMessage) const
void pgTableCollection::ShowStatistics(frmMain *form, ctlListView *statistics)
{
ShowStatisticsTables(form, statistics, this);
return;
wxLogInfo(wxT("Displaying statistics for tables on %s"), GetSchema()->GetIdentifier().c_str());
bool hasSize = GetConnection()->HasFeature(FEATURE_SIZE);
@ -1299,6 +1301,8 @@ void pgTableCollection::ShowStatistics(frmMain *form, ctlListView *statistics)
statistics->AddColumn(_("Table Name"));
if (hasSize)
statistics->AddColumn(_("Size"), 50);
if (GetConnection()->GetIsPgProEnt()) statistics->AddColumn(_("CFS %"));
statistics->AddColumn(_("Tuples inserted"));
statistics->AddColumn(_("Tuples updated"));
@ -1335,6 +1339,7 @@ void pgTableCollection::ShowStatistics(frmMain *form, ctlListView *statistics)
sql += wxT(", pg_size_pretty(pg_relation_size(st.relid)")
wxT(" + CASE WHEN cl.reltoastrelid = 0 THEN 0 ELSE pg_relation_size(cl.reltoastrelid) + COALESCE((SELECT SUM(pg_relation_size(indexrelid)) FROM pg_index WHERE indrelid=cl.reltoastrelid)::int8, 0) END")
wxT(" + COALESCE((SELECT SUM(pg_relation_size(indexrelid)) FROM pg_index WHERE indrelid=st.relid)::int8, 0)) AS size");
if (GetConnection()->GetIsPgProEnt()) sql += wxT(",left((cfs_fragmentation(cl.oid)*100)::text,5)::text AS cfs_ratio");
sql += wxT("\n FROM pg_stat_all_tables st")
wxT(" JOIN pg_class cl on cl.oid=st.relid\n")
@ -1349,13 +1354,14 @@ void pgTableCollection::ShowStatistics(frmMain *form, ctlListView *statistics)
int i;
while (!stats->Eof())
{
i = 5;
i = 1;
statistics->InsertItem(pos, stats->GetVal(wxT("relname")), PGICON_STATISTICS);
if (hasSize)
statistics->SetItem(pos, 1, stats->GetVal(wxT("size")));
statistics->SetItem(pos, 2, stats->GetVal(wxT("n_tup_ins")));
statistics->SetItem(pos, 3, stats->GetVal(wxT("n_tup_upd")));
statistics->SetItem(pos, 4, stats->GetVal(wxT("n_tup_del")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("size")));
if (GetConnection()->GetIsPgProEnt()) statistics->SetItem(pos, i++, stats->GetVal(wxT("cfs_ratio")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_ins")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_upd")));
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_del")));
if (GetConnection()->BackendMinimumVersion(8, 3))
{
statistics->SetItem(pos, i++, stats->GetVal(wxT("n_tup_hot_upd")));
@ -1391,6 +1397,10 @@ void pgTableCollection::ShowStatistics(frmMain *form, ctlListView *statistics)
void pgTable::ShowStatistics(frmMain *form, ctlListView *statistics)
{
if (GetIsPartitioned()) {
ShowStatisticsTables(form,statistics,this);
return;
}
wxString sql =
wxT("SELECT seq_scan AS ") + qtIdent(_("Sequential Scans")) +
wxT(", seq_tup_read AS ") + qtIdent(_("Sequential Tuples Read")) +