diff --git a/.gitignore b/.gitignore index f12386e..fc524d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ Debug/* x64/* Release/* +Debug_(3.0)/* *.sbr *.obj *.cod @@ -8,3 +9,5 @@ Release/* *.7z *.bsc *.pdb +ctl/ctlMonitorWidget.cpp +include/ctl/ctlMonitorWidget.h diff --git a/README.md b/README.md index 251c33d..a414464 100644 --- a/README.md +++ b/README.md @@ -133,10 +133,27 @@ This text Russian language. В родной схеме, секции как таблицы увидеть нельзя. * мелкие улучшения +02.09.2020 + - добавлена возможность копировать в буфер обмена выделенные ячейки результата запроса в формате IN списка и Where конструкций. Вызывается из контекстного меню. + - в Server status окне добавлена возможность фильтровать строки по щелчку правой кнопкой мыши. + * иправлено issues #8 (dropping overloaded procedures) + +05.09.2020 + - при сравнении объектов добавлена возможность исключать сравнение привелегий и комментариев. + * исправлено копирование текста запроса из под фильтра в Server status окне. При сравнении текста из колонки Client порт не учитывается. +05.12.2020 + * много мелких исправлений + * добавлены некоторые новые возможности PG13. Описание в коммитах. + - добавлена проверка btree индекса. Проверка выполняется функцией bt_index_parent_check(regclass,true) из расширения amcheck. + Расширение должно быть установлено в БД к которой происходит подключение pgadmin3. - - - +02.01.2021 + - добавлена сортировка на вкладках "свойства" , "Статистика" и других. + - по секционированным таблицам в статистеке выводятся все потомки. + + + + diff --git a/Release_(3.0)/pgAdmin3.exe b/Release_(3.0)/pgAdmin3.exe index 4e3e259..dfdf415 100644 Binary files a/Release_(3.0)/pgAdmin3.exe and b/Release_(3.0)/pgAdmin3.exe differ diff --git a/ctl/ctlListView.cpp b/ctl/ctlListView.cpp index 4f3b76d..6909922 100644 --- a/ctl/ctlListView.cpp +++ b/ctl/ctlListView.cpp @@ -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 +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 mp; + for (int i = 0; i < rows; i++) { + wxString val = GetItemText(i, col); + mp.insert(std::pair(val, i)); + } + // SortItems + std::multimap::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 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(d, i)); + } + // SortItems + std::multimap::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) diff --git a/ctl/ctlSQLGrid.cpp b/ctl/ctlSQLGrid.cpp index d2338f1..fae2513 100644 --- a/ctl/ctlSQLGrid.cpp +++ b/ctl/ctlSQLGrid.cpp @@ -217,10 +217,12 @@ wxString ctlSQLGrid::GetExportLine(int row, wxArrayInt cols) if (GetNumberCols() == 0 || GetRowSize(row)==0) return str; wxString colsep=settings->GetCopyColSeparator(); - if (generatesql) colsep=wxT(","); + if (generatesql == 2|| generatesql == 1) colsep=wxT(","); + if (generatesql == 3) colsep = wxT(" and "); wxString qtsimbol=settings->GetCopyQuoteChar(); - if (generatesql) qtsimbol=wxT("'"); + if (generatesql>0) qtsimbol=wxT("'"); wxString head=wxT("insert into tbl("); + if (cols.Count()>1 && generatesql > 1) str.Append("("); for (col = 0 ; col < cols.Count() ; col++) { if (col > 0) @@ -228,6 +230,7 @@ wxString ctlSQLGrid::GetExportLine(int row, wxArrayInt cols) if (col > 0) head.Append(colsep); head=head+GetColumnName(cols[col]); wxString text = GetCellValue(row, cols[col]); + wxString cname = GetColumnName(cols[col]); bool needQuote = false; if (settings->GetCopyQuoting() == 1) { @@ -236,22 +239,29 @@ wxString ctlSQLGrid::GetExportLine(int row, wxArrayInt cols) else if (settings->GetCopyQuoting() == 2) /* Quote everything */ needQuote = true; - if (text.Length()==0&&generatesql) {needQuote = false;} else - if (generatesql) needQuote = IsColText(cols[col]); + if (text.Length()==0&&generatesql>0) {needQuote = false;} else + if (generatesql>0) needQuote = IsColText(cols[col]); + + if (generatesql > 0) { + if (text.Length() != 0) { + text.Replace(wxT("'"), wxT("''")); + } + else text = wxT("null"); + + } + if (generatesql == 3) if (text == "null") str.Append(cname).Append(" is "); else + str.Append(cname).Append("="); + + if (needQuote) str.Append(qtsimbol); - - if (generatesql) { - if (text.Length()!=0) { - text.Replace(wxT("'"),wxT("''")); - } else text=wxT("null"); - - } str.Append(text); if (needQuote) str.Append(qtsimbol); } - if (generatesql) str=head+wxT(") values (")+str+");"; + if (cols.Count() > 1 && generatesql > 1) str.Append(")"); + if (generatesql == 1) str=head+wxT(") values (")+str+");"; + return str; } @@ -278,35 +288,54 @@ void ctlSQLGrid::AppendColumnHeader(wxString &str, int start, int end) void ctlSQLGrid::AppendColumnHeader(wxString &str, wxArrayInt columns) { - if(settings->GetColumnNames()&&!generatesql) + if(settings->GetColumnNames()|| generatesql == 2) { bool CopyQuoting = (settings->GetCopyQuoting() == 1 || settings->GetCopyQuoting() == 2); size_t i; - + wxString fielddelim = ","; + if (generatesql == 3) return; + if (generatesql == 1) return; for(i = 0; i < columns.Count() ; i++) { long columnPos = columns.Item(i); - if(i > 0) - str.Append(settings->GetCopyColSeparator()); + if (generatesql == 2) { + if (i > 0) str.Append(fielddelim); + if (i == 0 && columns.Count() > 1) str.Append("("); + str.Append(GetColumnName(columnPos)); + if (i == columns.Count()-1 && columns.Count() > 1 && generatesql == 2) str.Append(") in ("); + if (columns.Count() == 1 && generatesql == 2) str.Append(" in ("); - if(CopyQuoting) - str.Append(settings->GetCopyQuoteChar()); - str.Append(GetColumnName(columnPos)); - if(CopyQuoting) - str.Append(settings->GetCopyQuoteChar()); + } + else + { + if (i > 0) + str.Append(settings->GetCopyColSeparator()); + if (CopyQuoting) + str.Append(settings->GetCopyQuoteChar()); + str.Append(GetColumnName(columnPos)); + if (CopyQuoting) + str.Append(settings->GetCopyQuoteChar()); + } } + str.Append(END_OF_LINE); } } - -int ctlSQLGrid::Copy(bool gensql) +int compare_int(int* a, int* b) { - wxString str,tmp; + if (*a > * b) return 1; + else if (*a < *b) return -1; + else return 0; +} +int ctlSQLGrid::Copy(int gensql) +{ + wxString str,tmp,linedelim=""; int copied = 0; size_t i; generatesql=gensql; - + if (gensql == 2) linedelim = ","; + else if(gensql == 3) linedelim = " or "; if (GetSelectedRows().GetCount()) { @@ -319,6 +348,7 @@ int ctlSQLGrid::Copy(bool gensql) tmp=GetExportLine(rows.Item(i)); if (tmp.IsEmpty()) continue; str.Append(tmp); + if (i < rows.GetCount() - 1 && (generatesql > 1)) str.Append(linedelim); if (rows.GetCount() > 1) str.Append(END_OF_LINE); } @@ -335,48 +365,93 @@ int ctlSQLGrid::Copy(bool gensql) for (i = 0 ; i < numRows ; i++) { str.Append(GetExportLine(i, cols)); - + if (i<(numRows-1) && (generatesql > 1)) str.Append(linedelim); if (numRows > 1) str.Append(END_OF_LINE); } - copied = numRows; } else if (GetSelectionBlockTopLeft().GetCount() > 0 && GetSelectionBlockBottomRight().GetCount() > 0) { unsigned int x1, x2, y1, y2; + int count = GetSelectionBlockTopLeft().GetCount(); x1 = GetSelectionBlockTopLeft()[0].GetCol(); x2 = GetSelectionBlockBottomRight()[0].GetCol(); y1 = GetSelectionBlockTopLeft()[0].GetRow(); y2 = GetSelectionBlockBottomRight()[0].GetRow(); - + copied = 0; AppendColumnHeader(str, x1, x2); - - for (i = y1; i <= y2; i++) + for (size_t n = 0; n < count; n++) { - str.Append(GetExportLine(i, x1, x2)); + x1 = GetSelectionBlockTopLeft()[n].GetCol(); + x2 = GetSelectionBlockBottomRight()[n].GetCol(); + y1 = GetSelectionBlockTopLeft()[n].GetRow(); + y2 = GetSelectionBlockBottomRight()[n].GetRow(); - if (y2 > y1) - str.Append(END_OF_LINE); + for (i = y1; i <= y2; i++) + { + str.Append(GetExportLine(i, x1, x2)); + if (i < y2 && (generatesql > 1)) str.Append(linedelim); + if (y2 > y1) + str.Append(END_OF_LINE); + } + copied = copied+(y2 - y1 + 1); } - - copied = y2 - y1 + 1; } else { int row, col; + if (generatesql > 1) { + wxGridCellCoordsArray cord = GetSelectedCells(); + int count = cord.GetCount(); + int curr_row = 1000000000; + int next_row = 1000000000; + + for (size_t n = 0; n < count; n++) + { + wxGridCellCoords& coords = cord[n]; + if (coords.GetRow() < curr_row) curr_row = coords.GetRow(); + } + while (curr_row != 1000000000) + { + wxArrayInt colsNum; + next_row = 1000000000; + for (size_t n = 0; n < count; n++) + { + wxGridCellCoords& coords = cord[n]; + //wxString msg; + //msg.Printf(wxT(" row=%d col=%d.\n"), coords.GetRow(), coords.GetCol()); + //wxMessageBox(msg, wxT("About coord"), wxOK | wxICON_INFORMATION, this); + int r = coords.GetRow(); + if (r == curr_row) { + colsNum.Add(coords.GetCol()); + } + else if (r > curr_row && r < next_row) next_row = r; - row = GetGridCursorRow(); - col = GetGridCursorCol(); + } + colsNum.Sort(compare_int); + if (generatesql == 2) AppendColumnHeader(str, colsNum); + str.Append(GetExportLine(curr_row, colsNum)); + if ((next_row != 1000000000) && (generatesql == 3)) str.Append(linedelim); + if ((next_row != 1000000000) && (generatesql == 2)) str.Append(") or "); + str.Append(END_OF_LINE); + curr_row = next_row; + copied++; + } + } + else { + row = GetGridCursorRow(); + col = GetGridCursorCol(); - AppendColumnHeader(str, col, col); + AppendColumnHeader(str, col, col); - str.Append(GetExportLine(row, col, col)); - copied = 1; + str.Append(GetExportLine(row, col, col)); + copied = 1; + } } - + if (copied && (generatesql == 2)) str.Append(")"); if (copied && wxTheClipboard->Open()) { wxTheClipboard->SetData(new wxTextDataObject(str)); diff --git a/db/pgConn.cpp b/db/pgConn.cpp index 3a8705a..8ecb72d 100644 --- a/db/pgConn.cpp +++ b/db/pgConn.cpp @@ -486,11 +486,13 @@ bool pgConn::BackendMinimumVersion(int major, int minor) if (!majorVersion) { wxString version = GetVersionString(); + minorVersion = 0; + patchVersion = 0; sscanf(version.ToAscii(), "%*s %d.%d.%d", &majorVersion, &minorVersion, &patchVersion); isEdb = version.Upper().Matches(wxT("ENTERPRISEDB*")); - if (version.Upper().Matches(wxT("*11BETA*"))) { - majorVersion=11; - minorVersion=0; + if (!majorVersion) { + wxString vers=ExecuteScalar(wxT("SELECT current_setting('server_version_num');")); + sscanf(vers.ToAscii(), "%2d%04d", &majorVersion, &minorVersion); } // EnterpriseDB 8.3 beta 1 & 2 and possibly later actually have PostgreSQL 8.2 style // catalogs. This is expected to change either before GA, but in the meantime we @@ -545,7 +547,7 @@ bool pgConn::HasFeature(int featureNo, bool forceCheck) wxT(" JOIN pg_namespace n ON n.oid=pronamespace\n") wxT(" WHERE proname IN ('pg_tablespace_size', 'pg_file_read', 'pg_logfile_rotate',") wxT( " 'pg_postmaster_starttime', 'pg_terminate_backend', 'pg_reload_conf',") - wxT( " 'pgstattuple', 'pgstatindex')\n") + wxT( " 'pgstattuple', 'pgstatindex','bt_index_parent_check')\n") wxT(" AND nspname IN ('pg_catalog', 'public')"); pgSet *set = ExecuteSet(sql); @@ -556,6 +558,7 @@ bool pgConn::HasFeature(int featureNo, bool forceCheck) { wxString proname = set->GetVal(wxT("proname")); long pronargs = set->GetLong(wxT("pronargs")); + if (BackendMinimumVersion(9, 6)) features[FEATURE_FILEREAD] = true; if (proname == wxT("pg_tablespace_size") && pronargs == 1 && set->GetLong(wxT("arg0")) == 26) features[FEATURE_SIZE] = true; @@ -574,6 +577,8 @@ bool pgConn::HasFeature(int featureNo, bool forceCheck) features[FEATURE_PGSTATTUPLE] = true; else if (proname == wxT("pgstatindex") && pronargs == 1 && set->GetLong(wxT("arg0")) == 25) features[FEATURE_PGSTATINDEX] = true; + else if (proname == wxT("bt_index_parent_check") && pronargs == 2 ) + features[FEATURE_PGCHECKINDEX] = true; set->MoveNext(); } diff --git a/dlg/dlgLanguage.cpp b/dlg/dlgLanguage.cpp index 7c8c5bc..af62b1f 100644 --- a/dlg/dlgLanguage.cpp +++ b/dlg/dlgLanguage.cpp @@ -114,7 +114,7 @@ int dlgLanguage::Go(bool modal) else { // create mode - if (connection->BackendMinimumVersion(8, 1)) + if (connection->BackendMinimumVersion(8, 1)&&(!connection->BackendMinimumVersion(13, 0))) { pgSetIterator languages(connection, wxT("SELECT tmplname FROM pg_pltemplate\n") diff --git a/frm/frmAbout.cpp b/frm/frmAbout.cpp index 0e88b6a..202f3f2 100644 --- a/frm/frmAbout.cpp +++ b/frm/frmAbout.cpp @@ -20,9 +20,8 @@ // Copyright text #include "copyright.h" #include "version.h" -#include "svnversion.h" -#define VERSION_WITH_DATE_AND_SVN wxT("Version ") VERSION_STR wxT(" (") __TDATE__ wxT(", rev: ") wxT(VERSION_SVN) wxT(")") +#define VERSION_WITH_DATE_AND_SVN wxT("Version ") VERSION_STR wxT(" (") __TDATE__ wxT(", rev: ") wxT("") wxT(")") BEGIN_EVENT_TABLE(frmAbout, wxFrame) diff --git a/frm/frmMain.cpp b/frm/frmMain.cpp index c9cb432..0a875cb 100644 --- a/frm/frmMain.cpp +++ b/frm/frmMain.cpp @@ -315,6 +315,7 @@ void frmMain::CreateMenus() new refreshConcurrentlyMatViewFactory(menuFactories, viewMenu, 0); new executePgstattupleFactory(menuFactories, viewMenu, 0); new executePgstatindexFactory(menuFactories, viewMenu, 0); + new executePgcheckindexFactory(menuFactories, viewMenu, 0); new enabledisableRuleFactory(menuFactories, toolsMenu, 0); new enabledisableTriggerFactory(menuFactories, toolsMenu, 0); new enabledisableEventTriggerFactory(menuFactories, toolsMenu, 0); @@ -432,7 +433,8 @@ void frmMain::CreateMenus() new reportObjectDependentsFactory(menuFactories, reportMenu, 0); new reportObjectListFactory(menuFactories, reportMenu, 0); new reportCompareFactory(menuFactories, reportMenu, 0); - + choiceSelectOpts.Add(0); + choiceSelectOpts.Add(1); toolsMenu->AppendSeparator(); diff --git a/frm/frmQuery.cpp b/frm/frmQuery.cpp index 72aa405..02d90c4 100644 --- a/frm/frmQuery.cpp +++ b/frm/frmQuery.cpp @@ -121,6 +121,8 @@ BEGIN_EVENT_TABLE(frmQuery, pgFrame) EVT_MENU(MNU_CLEAR, frmQuery::OnClear) EVT_MENU(MNU_SUMMARY_COL, frmQuery::OnSummary_Column) EVT_MENU(MNU_COPY_INSERT, frmQuery::OnCopy_Insert) + EVT_MENU(MNU_COPY_INLIST, frmQuery::OnCopy_InList) + EVT_MENU(MNU_COPY_WHERELIST, frmQuery::OnCopy_WhereList) EVT_MENU(MNU_CLEAR_FILTER, frmQuery::OnClear_Filter) EVT_MENU(MNU_FIND, frmQuery::OnSearchReplace) EVT_MENU(MNU_UNDO, frmQuery::OnUndo) @@ -1544,7 +1546,7 @@ void frmQuery::OnCopy(wxCommandEvent &ev) { if (obj == sqlResult) { - sqlResult->Copy(false); + sqlResult->Copy(0); break; } obj = obj->GetParent(); @@ -2011,6 +2013,9 @@ void frmQuery::OnLabelRightClick(wxGridEvent &event) xmenu->Append(MNU_DELETE, _("&Delete"), _("Delete selected rows.")); xmenu->Append(MNU_SUMMARY_COL, _("Summary"), _("Summary selected cells.")); xmenu->Append(MNU_COPY_INSERT, _("Copy Insert format"), _("Copy Insert format.")); + xmenu->Append(MNU_COPY_INLIST, _("IN LIST format copy"), _("Copy In list format.")); + xmenu->Append(MNU_COPY_WHERELIST, _("WHERE LIST format copy"), _("Copy where list format.")); + xmenu->Append(MNU_CLEAR_FILTER, _("Clear filter"), _("Clear filter")); if ((rows.GetCount())) @@ -2033,11 +2038,29 @@ void frmQuery::OnCopy_Insert(wxCommandEvent &ev) // if (currentControl() == sqlResult) { wxString s=wxT("Insert into format copy buffer."); - sqlResult->Copy(true); + sqlResult->Copy(1); SetStatusText(s, STATUSPOS_POS); } } -void frmQuery::OnCellLeftDClick(wxGridEvent &event) +void frmQuery::OnCopy_InList(wxCommandEvent& ev) +{ + // if (currentControl() == sqlResult) + { + wxString s = wxT("In list format copy buffer."); + sqlResult->Copy(2); + SetStatusText(s, STATUSPOS_POS); + } +} +void frmQuery::OnCopy_WhereList(wxCommandEvent& ev) +{ + // if (currentControl() == sqlResult) + { + wxString s = wxT("Where list format copy buffer."); + sqlResult->Copy(3); + SetStatusText(s, STATUSPOS_POS); + } +} +void frmQuery::OnCellLeftDClick(wxGridEvent &event) { int row=event.GetRow(); int col=event.GetCol(); diff --git a/frm/frmReport.cpp b/frm/frmReport.cpp index 01a97b4..fe0c827 100644 --- a/frm/frmReport.cpp +++ b/frm/frmReport.cpp @@ -14,6 +14,7 @@ // App headers #include "pgAdmin3.h" #include +#include #include "frm/frmMain.h" #include "frm/frmReport.h" @@ -1222,6 +1223,59 @@ wxString reportCompareFactory::GetNodePath(wxTreeItemId node) { return path; +} +wxString reportCompareFactory::ApplyCompareOpts(wxString sql,int metatype) { + if (sql.IsEmpty()) return ""; + bool no_comment = parent->getChoiceSelect(1); + bool no_priv = parent->getChoiceSelect(0); + if (no_comment) { + int last = 0; + while (last!=-1) { + last = sql.Find("\nCOMMENT ON"); + int start = last; + int end = -1; + while (last!=-1) { + last++; + if (sql.Length() == last) { last = -1; continue; } + wxChar c = sql.GetChar(last); + if (c == '\'') { + last++; + if (sql.GetChar(last) == '\'') { continue; } + if (sql.GetChar(last) == ';') { end = last; last = -1; continue; } + + } + }; + if (end != -1) { + sql=sql.Remove(start, end - start+1); + last = 0; + } + } + + } + if (no_priv &&(metatype != PGM_ROLE)) { + int last = 0; + while (last != -1) { + last = sql.Find("\nGRANT "); + if (last==-1) last = sql.Find("\nREVOKE "); + int start = last; + int end = -1; + while (last != -1) { + last++; + if (sql.Length() == last) { last = -1; continue; } + wxChar c = sql.GetChar(last); + if (c == ';') { + end = last; last = -1; + } + }; + if (end != -1) { + sql = sql.Remove(start, end - start + 1); + last = 0; + } + } + + } + return sql; + } void reportCompareFactory::GetExpandedChildNodes(wxTreeItemId node, wxArrayString &expandedNodes, ArraySQL &list, time_t *t, wxBusyInfo *w, MyHashSQL &h_path,int lvl) { @@ -1312,6 +1366,7 @@ void reportCompareFactory::GetExpandedChildNodes(wxTreeItemId node, wxArrayStrin if (obj ) { wxString s=obj->GetSql(browser); if (obj->GetMetaType()==PGM_SEQUENCE) s=""; + s = ApplyCompareOpts(s, obj->GetMetaType()); int c=browser->GetChildrenCount(child,false); if (size>0) { wxString srcpath(path); @@ -1492,6 +1547,7 @@ if (lastdb!=NULL) { wxMessageBox(msg, _("Error"), wxOK | wxICON_INFORMATION); return 0; } +if (!parent->StartChoiceDialog()) return 0; time_t timer=wxDateTime::GetTimeNow(); wxArrayString expandedNodes; ArraySQL list; @@ -1499,12 +1555,14 @@ time_t timer=wxDateTime::GetTimeNow(); wxWindowDisabler disableAll; { +#ifndef DEBUG wxBusyInfo waiting(wxString::Format(" Path = %s , = %s", browser->GetItemText(obj->GetServer()->GetId()).c_str(), obj->GetName().c_str(),parent)); // Give the UI a chance to redraw wxSafeYield(); wxMilliSleep(50); wxSafeYield(); +#endif // waiting->~wxBusyInfo(); GetExpandedChildNodes(obj->GetId(),expandedNodes,list,&timer, NULL,h_path,0); } @@ -1809,7 +1867,7 @@ std::wstring reportCompareFactory::printdiff(std::wstring str1, std::wstring str ++it; } #ifdef _DEBUG - wxFileName fn("D:\\PostgreSQL\\cmp.txt"); + wxFileName fn("cmp_debug.txt"); fn.MakeAbsolute(); wxFile file(fn.GetFullPath(), wxFile::write); diff --git a/frm/frmStatus.cpp b/frm/frmStatus.cpp index 97bf1b7..4166e29 100644 --- a/frm/frmStatus.cpp +++ b/frm/frmStatus.cpp @@ -44,6 +44,7 @@ #include "images/terminate_backend.pngc" #include "images/delete.pngc" #include "images/storedata.pngc" +#include "images/sortfilterclear.pngc" #include "images/down.pngc" #include "images/up.pngc" @@ -83,6 +84,7 @@ BEGIN_EVENT_TABLE(frmStatus, pgFrame) EVT_MENU(MNU_TERMINATE, frmStatus::OnTerminateBtn) EVT_MENU(MNU_COMMIT, frmStatus::OnCommit) EVT_MENU(MNU_ROLLBACK, frmStatus::OnRollback) + EVT_MENU(MNU_CLEAR_FILTER_SERVER_STATUS, frmStatus::OnClearFilter) EVT_COMBOBOX(CTL_LOGCBO, frmStatus::OnLoadLogfile) EVT_BUTTON(CTL_ROTATEBTN, frmStatus::OnRotateLogfile) @@ -91,6 +93,7 @@ BEGIN_EVENT_TABLE(frmStatus, pgFrame) EVT_TIMER(TIMER_STATUS_ID, frmStatus::OnRefreshStatusTimer) EVT_LIST_ITEM_SELECTED(CTL_STATUSLIST, frmStatus::OnSelStatusItem) EVT_LIST_ITEM_DESELECTED(CTL_STATUSLIST, frmStatus::OnSelStatusItem) + EVT_LIST_ITEM_RIGHT_CLICK(CTL_STATUSLIST, frmStatus::OnRightClickStatusItem) EVT_LIST_COL_CLICK(CTL_STATUSLIST, frmStatus::OnSortStatusGrid) EVT_LIST_COL_RIGHT_CLICK(CTL_STATUSLIST, frmStatus::OnRightClickStatusGrid) EVT_LIST_COL_END_DRAG(CTL_STATUSLIST, frmStatus::OnChgColSizeStatusGrid) @@ -219,6 +222,12 @@ frmStatus::frmStatus(frmMain *form, const wxString &_title, pgConn *conn) : pgFr initquery = wxT("SET log_statement='off';SET log_duration='off';SET log_min_duration_statement=-1;"); connection->ExecuteVoid(initquery, false); } + //pg_is_in_recovery() + wxString v = connection->ExecuteScalar(wxT("select pg_is_in_recovery()")); + + isrecovery = (v == wxT("t")); + v = connection->ExecuteScalar(wxT("select current_setting('track_commit_timestamp')")); + track_commit_timestamp = (v == wxT("on")); delete user; } @@ -314,6 +323,9 @@ frmStatus::frmStatus(frmMain *form, const wxString &_title, pgConn *conn) : pgFr toolBar->AddTool(MNU_COMMIT, wxEmptyString, *storedata_png_bmp, _("Commit transaction"), wxITEM_NORMAL); toolBar->AddTool(MNU_ROLLBACK, wxEmptyString, *delete_png_bmp, _("Rollback transaction"), wxITEM_NORMAL); toolBar->AddSeparator(); + toolBar->AddTool(MNU_CLEAR_FILTER_SERVER_STATUS, wxEmptyString, *sortfilterclear_png_bmp, _("Clear filter"), wxITEM_NORMAL); + toolBar->AddSeparator(); + cbLogfiles = new wxComboBox(toolBar, CTL_LOGCBO, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY | wxCB_DROPDOWN); toolBar->AddControl(cbLogfiles); @@ -344,6 +356,7 @@ frmStatus::frmStatus(frmMain *form, const wxString &_title, pgConn *conn) : pgFr toolBar->EnableTool(MNU_TERMINATE, false); toolBar->EnableTool(MNU_COMMIT, false); toolBar->EnableTool(MNU_ROLLBACK, false); + toolBar->EnableTool(MNU_CLEAR_FILTER_SERVER_STATUS, false); actionMenu->Enable(MNU_CANCEL, false); actionMenu->Enable(MNU_TERMINATE, false); actionMenu->Enable(MNU_COMMIT, false); @@ -1024,7 +1037,7 @@ void frmStatus::AddLogPane() // Add the log list logList = (ctlListView *)lstLog; - + bgColor = logList->GetBackgroundColour(); // We don't need this report (but we need the pane) // if server release is less than 8.0 or if server has no adminpack if (!(connection->BackendMinimumVersion(8, 0) && @@ -1640,10 +1653,22 @@ void frmStatus::OnRefreshStatusTimer(wxTimerEvent &event) q += wxT("false"); } q += wxT("AS slowquery\n"); - if (connection->BackendMinimumVersion(9, 6)) + bool iswalsend = false; + if (connection->BackendMinimumVersion(10, 0)) { - q += wxT(",wait_event_type,wait_event,v.heap_blks_total,v.heap_blks_vacuumed,v.heap_blks_scanned,v.phase\n"); - q += wxT("FROM pg_stat_activity p LEFT JOIN pg_stat_progress_vacuum v ON p.pid=v.pid "); + q += wxT(",backend_type,wait_event_type,wait_event,v.heap_blks_total,v.heap_blks_vacuumed,v.heap_blks_scanned,v.phase\n"); + wxString xact=""; + if (!track_commit_timestamp) + q += wxT(",coalesce(sl.xmin,sl.catalog_xmin)::text xmin_slot,':'||slot_name||'['||sl.slot_type||']' slotinfo,'LagSent:'||pg_size_pretty(pg_wal_lsn_diff(pg_last_wal_receive_lsn(),coalesce(confirmed_flush_lsn,restart_lsn)))||' LagXmin: -1'||' s' xminlag,-1 xminslotdelta\n"); + else if (isrecovery) + q += wxT(",coalesce(sl.xmin,sl.catalog_xmin)::text xmin_slot,':'||slot_name||'['||sl.slot_type||']' slotinfo,'LagSent:'||pg_size_pretty(pg_wal_lsn_diff(pg_last_wal_receive_lsn(),coalesce(confirmed_flush_lsn,restart_lsn)))||' LagXmin: '||coalesce(extract(epoch from (pg_last_committed_xact()).timestamp - pg_xact_commit_timestamp(xmin))::int,0)||' s' xminlag,coalesce(extract(epoch from (pg_last_committed_xact()).timestamp - pg_xact_commit_timestamp(xmin))::int,0) xminslotdelta\n"); + else + q += wxT(",coalesce(sl.xmin,sl.catalog_xmin)::text xmin_slot,':'||slot_name||'['||sl.slot_type||']' slotinfo,'LagSent:'||pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(),coalesce(confirmed_flush_lsn,restart_lsn)))||' LagXmin: '||coalesce(extract(epoch from (pg_last_committed_xact()).timestamp - pg_xact_commit_timestamp(xmin))::int,0)||' s' xminlag,coalesce(extract(epoch from (pg_last_committed_xact()).timestamp - pg_xact_commit_timestamp(xmin))::int,0) xminslotdelta\n"); + + q += wxT("FROM pg_stat_activity p LEFT JOIN pg_stat_progress_vacuum v ON p.pid=v.pid\n"); + q += wxT("LEFT JOIN pg_replication_slots sl ON p.pid=sl.active_pid "); + iswalsend = true; + //backend_type } else { q += wxT("FROM pg_stat_activity p "); @@ -1666,12 +1691,14 @@ void frmStatus::OnRefreshStatusTimer(wxTimerEvent &event) while (!dataSet1->Eof()) { pid = dataSet1->GetLong(wxT("pid")); + wxString slinfo=wxEmptyString; + if (iswalsend) { + slinfo = dataSet1->GetVal(wxT("slotinfo")); + } // Update the UI if (pid != backend_pid) { - pids.Add(pid); - // Add the query content to the queries array - queries.Add(dataSet1->GetVal(wxT("query"))); + if (row >= statusList->GetItemCount()) { @@ -1705,7 +1732,7 @@ void frmStatus::OnRefreshStatusTimer(wxTimerEvent &event) str.Printf(wxT("%s %5.2f%%"), phase, proc); app_name=str; } - + if (!slinfo.IsEmpty()) app_name += slinfo; } int colpos = 1; if (connection->BackendMinimumVersion(8, 5)) @@ -1735,7 +1762,9 @@ void frmStatus::OnRefreshStatusTimer(wxTimerEvent &event) if (connection->BackendMinimumVersion(9, 4)) { statusList->SetItem(row, colpos++, dataSet1->GetVal(wxT("backend_xid"))); - statusList->SetItem(row, colpos++, dataSet1->GetVal(wxT("backend_xmin"))); + if (!slinfo.IsEmpty()) statusList->SetItem(row, colpos++, dataSet1->GetVal(wxT("xmin_slot"))); + else + statusList->SetItem(row, colpos++, dataSet1->GetVal(wxT("backend_xmin"))); } if (connection->BackendMinimumVersion(9, 6)) { @@ -1745,7 +1774,11 @@ void frmStatus::OnRefreshStatusTimer(wxTimerEvent &event) } statusList->SetItem(row, colpos++, dataSet1->GetVal(wxT("blockedby"))); - statusList->SetItem(row, colpos, qry); + if (!slinfo.IsEmpty()) { + statusList->SetItem(row, colpos, dataSet1->GetVal(wxT("xminlag"))); + } + else + statusList->SetItem(row, colpos, qry); // Colorize the new line if (viewMenu->IsChecked(MNU_HIGHLIGHTSTATUS)) @@ -1771,11 +1804,46 @@ void frmStatus::OnRefreshStatusTimer(wxTimerEvent &event) blocked += dataSet1->GetVal(wxT("blockedby")); blocked += wxT(","); } + if (!slinfo.IsEmpty()) { + // walsender + long xmindelta = dataSet1->GetLong(wxT("xminslotdelta")); + + if (xmindelta>=1800) + statusList->SetItemBackgroundColour(row,wxColour(wxT("#FF8028"))); // orange + else + statusList->SetItemBackgroundColour(row, wxColour(settings->GetIdleProcessColour())); // idle + } + if (app_name.StartsWith(wxT("pgp-s super"))||app_name.StartsWith(wxT("pgp-s manager"))) statusList->SetItemBackgroundColour(row, wxColour(settings->GetIdleProcessColour())); // idle + } else statusList->SetItemBackgroundColour(row, *wxWHITE); - row++; + // filter apply + bool flt = false; + for (int i = 0; i < filterColumn.size(); i++) { + int col = filterColumn[i]; + wxListItem listitem; + listitem.SetMask(wxLIST_MASK_TEXT); + statusList->GetColumn(col, listitem); + wxString label = listitem.GetText(); + wxString tabval=statusList->GetItemText(row, col); + wxString fval = filterValue[i]; + if (label == _("Client")) { + tabval = tabval.BeforeLast(':'); + fval = fval.BeforeLast(':'); + } + if (tabval != fval) { + flt = true; + break; + } + } + if (!flt) { + // Add the query content to the queries array + queries.Add(dataSet1->GetVal(wxT("query"))); + pids.Add(pid); + row++; + } } dataSet1->MoveNext(); } @@ -2244,7 +2312,12 @@ void frmStatus::OnRefreshLogTimer(wxTimerEvent &event) if (isCurrent) { // check if the current logfile changed - pgSet *set = connection->ExecuteSet(wxT("SELECT pg_file_length(") + connection->qtDbString(logfileName) + wxT(") AS len")); + + pgSet* set; + if ((connection->BackendMinimumVersion(10, 0))) + set = connection->ExecuteSet(wxT("select size len from pg_stat_file(") + connection->qtDbString(logfileName) + wxT(")")); + else + set = connection->ExecuteSet(wxT("SELECT pg_file_length(") + connection->qtDbString(logfileName) + wxT(") AS len")); if (set) { newlen = set->GetLong(wxT("len")); @@ -2257,10 +2330,24 @@ void frmStatus::OnRefreshLogTimer(wxTimerEvent &event) } if (newlen > logfileLength) { + + long lastrow = logList->GetItemCount(); + bool remote = lastrow-1 == logList->GetFocusedItem(); statusBar->SetStatusText(_("Refreshing log list.")); addLogFile(logfileName, logfileTimestamp, newlen, logfileLength, false); statusBar->SetStatusText(_("Done.")); - + if (bgColor == logList->GetBackgroundColour()) bgColor = wxColour("#afafaf"); else + { + bgColor = logList->GetBackgroundColour(); + } + if (remote) { + //logList->Select(lastrow - 1,false); + //logList->SetScrollPos(wxGA_VERTICAL, logList->GetItemCount() - 1, true); + //logList->Select(logList->GetItemCount() - 1); + logList->Focus(logList->GetItemCount() - 1); + + //logList->Show + } // as long as there was new data, the logfile is probably the current // one so we don't need to check for rotation return; @@ -2344,7 +2431,13 @@ void frmStatus::checkConnection() void frmStatus::addLogFile(wxDateTime *dt, bool skipFirst) { - pgSet *set = connection->ExecuteSet( + pgSet* set; + if (settings->GetASUTPstyle()) { + set = connection->ExecuteSet( + wxT("select current_setting('log_directory')||'/'||name filename,modification filetime,size len\n") + wxT(" FROM pg_ls_logdir() where modification = '") + DateToAnsiStr(*dt) + wxT("'::timestamp")); + } else + set = connection->ExecuteSet( wxT("SELECT filetime, filename, pg_file_length(filename) AS len ") wxT(" FROM pg_logdir_ls() AS A(filetime timestamp, filename text) ") wxT(" WHERE filetime = '") + DateToAnsiStr(*dt) + wxT("'::timestamp")); @@ -2395,11 +2488,12 @@ void frmStatus::addLogFile(const wxString &filename, const wxDateTime timestamp, else line = savedPartialLine; } - + wxString funcname = "pg_read_file("; + if (!settings->GetASUTPstyle()) funcname = "pg_file_read("; while (len > read) { statusBar->SetStatusText(_("Reading log from server...")); - pgSet *set = connection->ExecuteSet(wxT("SELECT pg_file_read(") + + pgSet *set = connection->ExecuteSet(wxT("SELECT ") + funcname + connection->qtDbString(filename) + wxT(", ") + NumToStr(read) + wxT(", 50000)")); if (!set) { @@ -2417,10 +2511,13 @@ void frmStatus::addLogFile(const wxString &filename, const wxDateTime timestamp, read += strlen(raw); wxString str; - if (wxString(wxString(raw, wxConvLibc).wx_str(), wxConvUTF8).Len() > 0) - str = line + wxString(wxString(raw, wxConvLibc).wx_str(), wxConvUTF8); - else - str = line + wxTextBuffer::Translate(wxString(raw, set->GetConversion()), wxTextFileType_Unix); + str = line + wxTextBuffer::Translate(wxString(raw, set->GetConversion()), wxTextFileType_Unix); + //if (wxString(wxString(raw, wxConvLibc).wx_str(), wxConvUTF8).Len() > 0) + // str = line + wxString(wxString(raw, wxConvLibc).wx_str(), wxConvUTF8); + //else { + // str = line + wxTextBuffer::Translate(wxString(raw, set->GetConversion()), wxTextFileType_Unix); + //} + delete set; @@ -2563,8 +2660,11 @@ void frmStatus::addLogLine(const wxString &str, bool formatted, bool csv_log_for } } - if (!logFormatKnown) + if (!logFormatKnown) { logList->AppendItem(-1, str); + if (bgColor != logList->GetBackgroundColour()) logList->SetItemBackgroundColour(row, bgColor); + + } else if ((!csv_log_format) && str.Find(':') < 0) { // Must be a continuation of a previous line. @@ -2847,8 +2947,9 @@ void frmStatus::addLogLine(const wxString &str, bool formatted, bool csv_log_for int pos = rest.Find(':'); - if (pos < 0) + if (pos < 0) { logList->InsertItem(row, rest, -1); + } else { logList->InsertItem(row, rest.BeforeFirst(':'), -1); @@ -2882,11 +2983,19 @@ int frmStatus::fillLogfileCombo() cbLogfiles->Append(_("Current log")); else count--; + pgSet* set; + if (settings->GetASUTPstyle()) + set = connection->ExecuteSet( + wxT("select name filename,modification filetime\n") + wxT(" FROM pg_ls_logdir() where name ~ '.csv'\n") + wxT(" ORDER BY modification DESC")); + + else set = connection->ExecuteSet( + wxT("SELECT filename, filetime\n") + wxT(" FROM pg_logdir_ls() AS A(filetime timestamp, filename text) \n") + wxT(" ORDER BY filetime DESC")); + - pgSet *set = connection->ExecuteSet( - wxT("SELECT filename, filetime\n") - wxT(" FROM pg_logdir_ls() AS A(filetime timestamp, filename text)\n") - wxT(" ORDER BY filetime DESC")); if (set) { if (set->NumRows() <= count) @@ -3252,7 +3361,6 @@ void frmStatus::OnCommit(wxCommandEvent &event) OnSelXactItem(ev); } - void frmStatus::OnRollback(wxCommandEvent &event) { long item = xactList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); @@ -3513,6 +3621,51 @@ void frmStatus::SetColumnImage(ctlListView *list, int col, int image) list->SetColumn(col, item); } +void frmStatus::OnRightClickStatusItem(wxListEvent& event) +{ + int row = event.GetIndex(); + //wxString txt = event.GetText(); + + wxRect r; + //statusList->GetItemRect(row, r); + wxString ss = wxEmptyString; + int col = -1; + for (int cc = 0; cc < statusList->GetColumnCount();cc++) { + statusList->GetSubItemRect(row, cc, r, wxLIST_RECT_BOUNDS); + if (r.Contains(event.GetPoint())) { + ss = wxString::Format("\rBounding rect of item %ld column %d is (%d, %d)-(%d, %d)", row,cc, r.x, r.y, r.x + r.width, r.y + r.height); + col = cc; + break; + } + } + if (col == -1) return; + wxString val=statusList->GetItemText(row, col); + wxString txt = wxString::Format("gettext=%s\r index=%d\r column=%d", val.c_str(), row, col); + txt = txt + ss; + + wxListItem listitem; + listitem.SetMask(wxLIST_MASK_TEXT); + statusList->GetColumn(col, listitem); + wxString label = listitem.GetText()+" = "+val; + //wxMessageBox(txt, "test", wxICON_WARNING | wxOK); + wxString hint=label; + if (filterColumn.size() > 0) hint = toolBar->GetToolShortHelp(MNU_CLEAR_FILTER_SERVER_STATUS)+"\n"+label; + filterColumn.Add(col); + filterValue.Add(val); + toolBar->SetToolShortHelp(MNU_CLEAR_FILTER_SERVER_STATUS, hint); + toolBar->EnableTool(MNU_CLEAR_FILTER_SERVER_STATUS, true); + wxTimerEvent evt; + OnRefreshStatusTimer(evt); +} +void frmStatus::OnClearFilter(wxCommandEvent& event) { + toolBar->EnableTool(MNU_CLEAR_FILTER_SERVER_STATUS, false); + toolBar->SetToolShortHelp(MNU_CLEAR_FILTER_SERVER_STATUS, "Clear filter"); + filterColumn.Clear(); + filterValue.Clear(); + wxTimerEvent evt; + OnRefreshStatusTimer(evt); + +} void frmStatus::OnSortStatusGrid(wxListEvent &event) { diff --git a/include/ctl/ctlListView.h b/include/ctl/ctlListView.h index d2a9e4e..db2736f 100644 --- a/include/ctl/ctlListView.h +++ b/include/ctl/ctlListView.h @@ -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()); } + }; diff --git a/include/ctl/ctlSQLGrid.h b/include/ctl/ctlSQLGrid.h index d60c8fb..d726958 100644 --- a/include/ctl/ctlSQLGrid.h +++ b/include/ctl/ctlSQLGrid.h @@ -30,7 +30,7 @@ public: { return false; } - int Copy(bool gensql); + int Copy(int gensql); virtual bool CheckRowPresent(int row) { @@ -55,7 +55,7 @@ public: wxString GetRowLabelValue( int row ); void SetRowGroup(int row); GroupRows *grp; - bool generatesql; + int generatesql; // 0 -, 1 - insert , 2 - in_list WX_DECLARE_STRING_HASH_MAP( int, ColKeySizeHashMap ); DECLARE_DYNAMIC_CLASS(ctlSQLGrid) diff --git a/include/frm/frmMain.h b/include/frm/frmMain.h index 90cea9a..1187370 100644 --- a/include/frm/frmMain.h +++ b/include/frm/frmMain.h @@ -107,7 +107,28 @@ public: currentObject = data; } bool CheckAlive(); - + // + bool getChoiceSelect(int index) { + for (size_t n = 0; n < choiceSelectOpts.GetCount(); n++) { + if (choiceSelectOpts[n] == index) return true; + } + return false; + } + bool StartChoiceDialog() { + choiceCmpOpts.Clear(); + choiceCmpOpts.Add("Priveleges ignore"); + choiceCmpOpts.Add("Comment ignore"); + wxMultiChoiceDialog dialog(this, + wxT("A multi-choice convenience dialog"), + wxT("Please select several compare options"), + choiceCmpOpts); + dialog.SetSelections(choiceSelectOpts); + if (dialog.ShowModal() == wxID_OK) { + choiceSelectOpts = dialog.GetSelections(); + return true; + } + return false; + } void execSelChange(wxTreeItemId item, bool currentNode); void Refresh(pgObject *data); void ExecDrop(bool cascaded); @@ -203,6 +224,8 @@ private: long msgLevel; bool m_refreshing; + wxArrayString choiceCmpOpts; + wxArrayInt choiceSelectOpts; wxTreeItemId denyCollapseItem; pgObject *currentObject; diff --git a/include/frm/frmQuery.h b/include/frm/frmQuery.h index 1659183..5c367ff 100644 --- a/include/frm/frmQuery.h +++ b/include/frm/frmQuery.h @@ -212,6 +212,8 @@ private: void OnClear(wxCommandEvent &event); void OnSummary_Column(wxCommandEvent &event); void OnCopy_Insert(wxCommandEvent &event); + void OnCopy_InList(wxCommandEvent& event); + void OnCopy_WhereList(wxCommandEvent& event); void OnClear_Filter(wxCommandEvent &event); void OnSearchReplace(wxCommandEvent &event); void OnUndo(wxCommandEvent &event); diff --git a/include/frm/frmReport.h b/include/frm/frmReport.h index 3eb0bbf..455178c 100644 --- a/include/frm/frmReport.h +++ b/include/frm/frmReport.h @@ -141,6 +141,7 @@ private: protected: //reportCompareFactory(menuFactoryList *list) : actionFactory(list) {} wxString reportCompareFactory::GetNodePath(wxTreeItemId node); + wxString ApplyCompareOpts(wxString sql, int metatype); wxWindow *StartDialog(frmMain *form, pgObject *obj); void reportCompareFactory::GetExpandedChildNodes(wxTreeItemId node, wxArrayString &expandedNodes, ArraySQL &list,time_t *t,wxBusyInfo *w, MyHashSQL &h_path,int lvl); std::wstring reportCompareFactory::printdiff(std::wstring str1, std::wstring str2 ); diff --git a/include/frm/frmStatus.h b/include/frm/frmStatus.h index d0dc020..ed851d2 100644 --- a/include/frm/frmStatus.h +++ b/include/frm/frmStatus.h @@ -50,6 +50,7 @@ enum MNU_COMMIT, MNU_ROLLBACK, MNU_COPY_QUERY, + MNU_CLEAR_FILTER_SERVER_STATUS, MNU_COPY_QUERY_PLAN, MNU_HIGHLIGHTSTATUS, MNU_QUERYSTATEVERBOSE, @@ -122,9 +123,10 @@ private: long backend_pid; int wait_event_type_col; - + bool isrecovery,track_commit_timestamp; bool loaded; long logfileLength; + wxColour bgColor; int currentPane; @@ -157,6 +159,8 @@ private: wxMenu *querystatePopupMenu; wxString queryplan; wxArrayString queries; + wxArrayInt filterColumn; + wxArrayString filterValue; int statusColWidth[12], lockColWidth[10], xactColWidth[5], querystateColWidth[5]; @@ -198,6 +202,7 @@ private: void SetColumnImage(ctlListView *list, int col, int image); void OnSortStatusGrid(wxListEvent &event); + void OnRightClickStatusItem(wxListEvent& event); void OnSortLockGrid(wxListEvent &event); void OnSortXactGrid(wxListEvent &event); @@ -237,6 +242,7 @@ private: void OnRotateLogfile(wxCommandEvent &event); void OnCommit(wxCommandEvent &event); void OnRollback(wxCommandEvent &event); + void OnClearFilter(wxCommandEvent& event); void OnChangeDatabase(wxCommandEvent &ev); diff --git a/include/frm/menu.h b/include/frm/menu.h index b575fe6..817bc38 100644 --- a/include/frm/menu.h +++ b/include/frm/menu.h @@ -77,6 +77,8 @@ enum MNU_AUTOSELECTQUERY, MNU_SUMMARY_COL, MNU_COPY_INSERT, + MNU_COPY_INLIST, + MNU_COPY_WHERELIST, MNU_CLEAR_FILTER, MNU_AUTOROLLBACK, MNU_AUTOCOMMIT, diff --git a/include/images/sortfilterclear.png b/include/images/sortfilterclear.png new file mode 100644 index 0000000..2fad39d Binary files /dev/null and b/include/images/sortfilterclear.png differ diff --git a/include/images/sortfilterclear.pngc b/include/images/sortfilterclear.pngc new file mode 100644 index 0000000..2a1ef0c --- /dev/null +++ b/include/images/sortfilterclear.pngc @@ -0,0 +1,106 @@ +#ifndef SORTFILTERCLEAR_PNG_H +#define SORTFILTERCLEAR_PNG_H + +static const unsigned char sortfilterclear_png_data[] = { +0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, +0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, +0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, +0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, +0x61, 0x00, 0x00, 0x01, 0xb6, 0x49, 0x44, 0x41, +0x54, 0x78, 0xda, 0xa5, 0x93, 0x4b, 0x28, 0x04, +0x71, 0x1c, 0xc7, 0xbf, 0x7f, 0xb4, 0xd2, 0xa2, +0xbc, 0x72, 0x71, 0x20, 0x8f, 0x90, 0x76, 0xcb, +0xc1, 0x23, 0xef, 0x94, 0x1c, 0xb6, 0x2c, 0x89, +0x83, 0xc7, 0xd1, 0x23, 0x7b, 0x20, 0x07, 0xa5, +0x1c, 0x70, 0x44, 0x39, 0x28, 0x5c, 0x5c, 0x48, +0x49, 0xb9, 0x70, 0xd0, 0x2e, 0xa5, 0x24, 0xb6, +0x75, 0x11, 0x21, 0xd6, 0xee, 0xc5, 0x23, 0x9b, +0x57, 0xb1, 0xe6, 0xb1, 0x3b, 0xf3, 0x37, 0x33, +0x1a, 0x8d, 0xb5, 0x56, 0xda, 0xa9, 0x5f, 0xd3, +0x6f, 0xfe, 0xf3, 0xf9, 0xf4, 0xfd, 0xff, 0x66, +0xfe, 0x04, 0x61, 0x5e, 0x64, 0x71, 0x69, 0x97, +0x9e, 0x5f, 0xbc, 0xc1, 0xeb, 0x15, 0xfe, 0x05, +0xea, 0xf5, 0x91, 0xc8, 0xcd, 0x89, 0x05, 0x19, +0x1d, 0xdf, 0xa6, 0x96, 0xbe, 0x52, 0x44, 0xb2, +0x31, 0x60, 0xdf, 0x28, 0xfc, 0x7e, 0x01, 0xa2, +0x28, 0x4a, 0x77, 0x11, 0x82, 0x20, 0x28, 0xfd, +0x67, 0xf9, 0xe1, 0xf3, 0x09, 0x10, 0x44, 0x01, +0xf1, 0x89, 0x3a, 0xa4, 0xa6, 0xeb, 0x31, 0x33, +0x7b, 0xf0, 0x99, 0xc0, 0xe5, 0xe6, 0xd1, 0xd9, +0x5c, 0x02, 0xef, 0x4d, 0x14, 0x5e, 0x9f, 0x7d, +0x60, 0x59, 0x1f, 0x38, 0x8e, 0x07, 0x2b, 0x17, +0x2b, 0x17, 0x07, 0x86, 0xe1, 0xc1, 0xf1, 0x3c, +0x62, 0xe2, 0x22, 0x50, 0x50, 0x9a, 0x0c, 0xdb, +0xfe, 0x15, 0x32, 0x33, 0x74, 0x20, 0x72, 0x1c, +0x55, 0xd2, 0x66, 0x2e, 0xc2, 0x93, 0x9b, 0xe2, +0xf9, 0x81, 0x51, 0x80, 0xbf, 0xe0, 0x8e, 0xf6, +0x0a, 0x42, 0xd4, 0x3d, 0xa9, 0x92, 0x16, 0x53, +0x21, 0x6e, 0xcf, 0x38, 0x3c, 0xdc, 0xbf, 0x83, +0x79, 0x67, 0x43, 0xc2, 0xca, 0x10, 0xb5, 0x83, +0x51, 0x25, 0xe6, 0x3a, 0x03, 0x9c, 0x87, 0x2f, +0xf0, 0xdc, 0x79, 0xc1, 0x48, 0x09, 0xa2, 0xf5, +0x04, 0x86, 0xb2, 0x14, 0x6c, 0xdb, 0x5d, 0xdf, +0xe0, 0x1f, 0x02, 0xad, 0xc4, 0x32, 0xdf, 0xad, +0xf4, 0x73, 0x0d, 0x0b, 0xc8, 0x36, 0x24, 0xe0, +0xd4, 0xe3, 0xf9, 0x82, 0x1f, 0xd3, 0xb2, 0xa8, +0xbc, 0x96, 0x74, 0xed, 0x24, 0x24, 0xd8, 0x27, +0xea, 0x1f, 0xdc, 0xa0, 0xcd, 0xb5, 0x46, 0xe4, +0x75, 0xd5, 0x28, 0xbd, 0x7d, 0x72, 0x03, 0x56, +0x87, 0x13, 0xd3, 0x53, 0xa6, 0x6f, 0x70, 0xd0, +0x04, 0xaa, 0xa0, 0xb1, 0xa6, 0x00, 0xb6, 0xb5, +0x4b, 0x0c, 0x58, 0x7b, 0x95, 0x67, 0xe3, 0xad, +0xd3, 0x18, 0x59, 0xe9, 0x87, 0x16, 0x0e, 0x29, +0x30, 0x95, 0xe7, 0x62, 0x73, 0xf5, 0x5c, 0x1a, +0x22, 0x87, 0x31, 0xc7, 0xd0, 0xd7, 0x9a, 0x16, +0x56, 0x04, 0x94, 0xd2, 0x7c, 0x42, 0xc8, 0x69, +0xa0, 0xa0, 0xbe, 0x24, 0x1b, 0xeb, 0xcb, 0x27, +0xd2, 0x10, 0x79, 0x4c, 0x1c, 0x8f, 0x84, 0x14, +0x18, 0x25, 0xc1, 0x51, 0xa0, 0x60, 0x78, 0xb0, +0x0a, 0x7b, 0x5b, 0x6e, 0x54, 0x0e, 0x37, 0xfd, +0x7f, 0x0b, 0x73, 0xf3, 0x3b, 0xca, 0xf9, 0x50, +0x01, 0x19, 0x96, 0xff, 0xfb, 0x9e, 0xee, 0xea, +0xe0, 0x43, 0x94, 0x52, 0x14, 0x4b, 0x29, 0xec, +0x5a, 0x49, 0xe0, 0x8b, 0xbf, 0xad, 0x91, 0x70, +0x8f, 0xf3, 0x07, 0xda, 0x28, 0x0c, 0xf1, 0xdd, +0xa4, 0x8c, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x49, +0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; + +#include "wx/mstream.h" + +static wxImage *sortfilterclear_png_img() +{ + if (!wxImage::FindHandler(wxT("PNG file"))) + wxImage::AddHandler(new wxPNGHandler()); + static wxImage *img_sortfilterclear_png = new wxImage(); + if (!img_sortfilterclear_png || !img_sortfilterclear_png->IsOk()) + { + wxMemoryInputStream img_sortfilterclear_pngIS(sortfilterclear_png_data, sizeof(sortfilterclear_png_data)); + img_sortfilterclear_png->LoadFile(img_sortfilterclear_pngIS, wxBITMAP_TYPE_PNG); + } + return img_sortfilterclear_png; +} +#define sortfilterclear_png_img sortfilterclear_png_img() + +static wxBitmap *sortfilterclear_png_bmp() +{ + static wxBitmap *bmp_sortfilterclear_png; + if (!bmp_sortfilterclear_png || !bmp_sortfilterclear_png->IsOk()) + bmp_sortfilterclear_png = new wxBitmap(*sortfilterclear_png_img); + return bmp_sortfilterclear_png; +} +#define sortfilterclear_png_bmp sortfilterclear_png_bmp() + +static wxIcon *sortfilterclear_png_ico() +{ + static wxIcon *ico_sortfilterclear_png; + if (!ico_sortfilterclear_png || !ico_sortfilterclear_png->IsOk()) + { + ico_sortfilterclear_png = new wxIcon(); + ico_sortfilterclear_png->CopyFromBitmap(*sortfilterclear_png_bmp); + } + return ico_sortfilterclear_png; +} +#define sortfilterclear_png_ico sortfilterclear_png_ico() + +#endif // SORTFILTERCLEAR_PNG_H diff --git a/include/pgAdmin3.h b/include/pgAdmin3.h index 8b28e7c..dc27d82 100644 --- a/include/pgAdmin3.h +++ b/include/pgAdmin3.h @@ -60,8 +60,8 @@ // Supported server minimum and maximum values. const short SERVER_MIN_VERSION_N = 0x0804; const wxString SERVER_MIN_VERSION_T = wxT("8.4"); -const short SERVER_MAX_VERSION_N = 0x0D00; -const wxString SERVER_MAX_VERSION_T = wxT("13"); +const short SERVER_MAX_VERSION_N = 0x0E00; +const wxString SERVER_MAX_VERSION_T = wxT("14"); // Supported Greenplum Database and Greenplum HAWQ minimum and maximum values. const short GP_MIN_VERSION_N = 0x0802; const wxString GP_MIN_VERSION_T = wxT("8.2"); diff --git a/include/schema/pgIndex.h b/include/schema/pgIndex.h index 2ee1553..9a56318 100644 --- a/include/schema/pgIndex.h +++ b/include/schema/pgIndex.h @@ -287,6 +287,7 @@ public: return true; } bool HasPgstatindex(); + bool HasPgcheckindex(); protected: void ReadColumnDetails(); @@ -345,6 +346,13 @@ public: bool CheckEnable(pgObject *obj); bool CheckChecked(pgObject *obj); }; +class executePgcheckindexFactory : public contextActionFactory +{ +public: + executePgcheckindexFactory(menuFactoryList* list, wxMenu* mnu, ctlMenuToolbar* toolbar); + wxWindow* StartDialog(frmMain* form, pgObject* obj); + bool CheckEnable(pgObject* obj); +}; #endif diff --git a/include/schema/pgObject.h b/include/schema/pgObject.h index da7cd24..d18af0f 100644 --- a/include/schema/pgObject.h +++ b/include/schema/pgObject.h @@ -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) diff --git a/include/schema/pgPublication.h b/include/schema/pgPublication.h index e1ce300..4f6bf9b 100644 --- a/include/schema/pgPublication.h +++ b/include/schema/pgPublication.h @@ -81,6 +81,15 @@ public: { return del; } + bool GetIsViaRoot() const + { + return publish_via_partition_root; + } + void iSetIsViaRoot(const bool b) + { + publish_via_partition_root = b; + } + wxString GetStrOper() const { wxString s = wxT(""); @@ -115,7 +124,7 @@ public: private: wxString tables, version; - bool all,ins,upd,del; + bool all,ins,upd,del, publish_via_partition_root; }; class pgPublicationCollection : public pgDatabaseObjCollection diff --git a/include/utils/pgfeatures.h b/include/utils/pgfeatures.h index 0e68e38..d3b2381 100644 --- a/include/utils/pgfeatures.h +++ b/include/utils/pgfeatures.h @@ -24,6 +24,7 @@ enum FEATURE_RELOAD_CONF, FEATURE_PGSTATTUPLE, FEATURE_PGSTATINDEX, + FEATURE_PGCHECKINDEX, FEATURE_FUNCTION_DEFAULTS, FEATURE_LAST }; diff --git a/include/version.h b/include/version.h index 7af7e0a..0e487b7 100644 --- a/include/version.h +++ b/include/version.h @@ -13,9 +13,9 @@ #define VERSION_H // Application Versions -#define VERSION_STR wxT("1.25.0 Dev ASUTP version with support PG12") -#define VERSION_NUM 1,25,0,0 -#define VERSION_PACKAGE 1.25.0-dev +#define VERSION_STR wxT("1.26 Dev ASUTP support PG12") +#define VERSION_NUM 1,26,0,0 +#define VERSION_PACKAGE 1.26.0-dev #define PRERELEASE 1 // #define BUILD "..." diff --git a/pgAdmin3.vcxproj b/pgAdmin3.vcxproj index 617ae5e..e76af1d 100644 --- a/pgAdmin3.vcxproj +++ b/pgAdmin3.vcxproj @@ -74,7 +74,7 @@ Application false Unicode - v120_xp + v142 Application @@ -158,6 +158,9 @@ .\wxwidgets\include;$(IncludePath) $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);wxwidgets\lib + + false + Extracting source code repository version @@ -479,7 +482,7 @@ .\Debug/ .\Debug/ .\Debug/ - true + false .\Debug/ Level3 true @@ -491,10 +494,9 @@ $(WXWIN)/include;%(AdditionalIncludeDirectories) - wxbase30ud.lib;wxbase30ud_xml.lib;wxbase30ud_net.lib;wxmsw30ud_adv.lib;wxmsw30ud_core.lib;wxmsw30ud_html.lib;wxmsw30ud_aui.lib;wxregexud.lib;wxpngd.lib;wxzlibd.lib;wxjpegd.lib;wxtiffd.lib;wxmsw30ud_stc.lib;wxmsw30ud_xrc.lib;wxexpatd.lib;libeay32MD.lib;libpq.lib;libxml2.lib;libxslt.lib;iconv.lib;comctl32.lib;rpcrt4.lib;wsock32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;oleaut32.lib;ole32.lib;shell32.lib;odbc32.lib;%(AdditionalDependencies) - .\Debug/pgAdmin3.exe + wxbase30ud.lib;wxbase30ud_xml.lib;wxbase30ud_net.lib;wxmsw30ud_adv.lib;wxmsw30ud_core.lib;wxmsw30ud_html.lib;wxmsw30ud_aui.lib;wxregexud.lib;wxpngd.lib;wxzlibd.lib;wxjpegd.lib;wxtiffd.lib;wxmsw30ud_stc.lib;wxmsw30ud_xrc.lib;wxexpatd.lib;libssh2.lib;libeay32MD.lib;libpq.lib;libxml2.lib;libxslt.lib;iconv.lib;comctl32.lib;rpcrt4.lib;wsock32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;oleaut32.lib;ole32.lib;shell32.lib;odbc32.lib;%(AdditionalDependencies) true - $(OPENSSL)/lib/VC;$(WXWIN)/lib/vc_dll;$(PGDIR)/lib;$(PGBUILD)/lib;$(PGBUILD)/libxml2/lib;$(PGBUILD)/libxslt/lib;$(PGBUILD)/iconv/lib;%(AdditionalLibraryDirectories) + $(PROJECTDIR)/../libssh2/src/Release;$(OPENSSL)/lib/VC;$(WXWIN)/lib/vc_x64_dll;$(PGDIR)/lib;$(PGBUILD)/lib;$(PGBUILD)/libxml2/lib;$(PGBUILD)/libxslt/lib;$(PGBUILD)/iconv/lib;%(AdditionalLibraryDirectories) libcd.lib;libcid.lib;msvcrt.lib;%(IgnoreSpecificDefaultLibraries) true .\Debug/pgAdmin3.pdb @@ -2236,6 +2238,7 @@ + diff --git a/pgAdmin3.vcxproj.filters b/pgAdmin3.vcxproj.filters index a5c6d8f..d18d8cf 100644 --- a/pgAdmin3.vcxproj.filters +++ b/pgAdmin3.vcxproj.filters @@ -4435,6 +4435,9 @@ include\images + + include\images + diff --git a/pgAdmin3.vcxproj.user b/pgAdmin3.vcxproj.user index 437c493..78f1da1 100644 --- a/pgAdmin3.vcxproj.user +++ b/pgAdmin3.vcxproj.user @@ -13,4 +13,14 @@ -s mi -q WindowsLocalDebugger + + + + WindowsLocalDebugger + + + + + WindowsLocalDebugger + \ No newline at end of file diff --git a/schema/pgFunction.cpp b/schema/pgFunction.cpp index 80b503e..2e2a694 100644 --- a/schema/pgFunction.cpp +++ b/schema/pgFunction.cpp @@ -509,7 +509,7 @@ bool pgProcedure::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded) if (!GetConnection()->BackendMinimumVersion(11, 0)) return pgFunction::DropObject(frame, browser, cascaded); - wxString sql = wxT("DROP PROCEDURE ") + this->GetSchema()->GetQuotedIdentifier() + wxT(".") + this->GetQuotedIdentifier(); + wxString sql = wxT("DROP PROCEDURE ") + this->GetSchema()->GetQuotedIdentifier() + wxT(".") + this->GetQuotedIdentifier() + wxT("(") + this->GetArgSigList() + wxT(")");; return GetDatabase()->ExecuteVoid(sql); } diff --git a/schema/pgIndex.cpp b/schema/pgIndex.cpp index 47538b2..b40ba7c 100644 --- a/schema/pgIndex.cpp +++ b/schema/pgIndex.cpp @@ -473,6 +473,36 @@ bool pgIndexBase::HasPgstatindex() return GetConnection()->HasFeature(FEATURE_PGSTATINDEX); } +bool pgIndexBase::HasPgcheckindex() +{ + return GetConnection()->HasFeature(FEATURE_PGCHECKINDEX); +} + +executePgcheckindexFactory::executePgcheckindexFactory(menuFactoryList* list, wxMenu* mnu, ctlMenuToolbar* toolbar) : contextActionFactory(list) +{ + mnu->Append(id, _("Check Btree index"), _("Check Btree index function bt_index_parent_check(oid,true)")); +} + + +wxWindow* executePgcheckindexFactory::StartDialog(frmMain* form, pgObject* obj) +{ + pgIndexBase* i = (pgIndexBase*)obj; + form->StartMsg(_("Check Btree index "+i->GetName()+" ")); + wxString sql = "SELECT bt_index_parent_check("+i->GetOidStr()+",true);"; + i->GetConnection()->ExecuteVoid(sql); + form->EndMsg(true); + return 0; +} + + +bool executePgcheckindexFactory::CheckEnable(pgObject* obj) +{ + + return obj && + (obj->IsCreatedBy(indexFactory) || obj->IsCreatedBy(primaryKeyFactory) + || obj->IsCreatedBy(uniqueFactory) || obj->IsCreatedBy(excludeFactory)) && ((pgIndexBase*)obj)->GetIndexType()=="btree" && + ((pgIndexBase*)obj)->HasPgcheckindex(); +} executePgstatindexFactory::executePgstatindexFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list) { @@ -757,4 +787,5 @@ void pgIndexBaseCollection::ShowStatistics(frmMain *form, ctlListView *statistic delete stats; } + statistics->SetColumnWidth(0, wxLIST_AUTOSIZE); } diff --git a/schema/pgObject.cpp b/schema/pgObject.cpp index 7495119..c4a2fb0 100644 --- a/schema/pgObject.cpp +++ b/schema/pgObject.cpp @@ -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) { @@ -452,6 +568,52 @@ void pgObject::CreateListColumns(ctlListView *list, const wxString &left, const list->AddColumn(right, list->GetSize().GetWidth() - 140); } +wxString GetClassTableName(int metatype) { + switch (metatype) + { + case PGM_CONSTRAINT: + return "pg_constraint"; + case PGM_FOREIGNKEY: + return "pg_constraint"; + case PGM_PRIMARYKEY: + return "pg_constraint"; + case PGM_UNIQUE: + return "pg_constraint"; + case PGM_EXCLUDE: + return "pg_constraint"; + case PGM_OPCLASS: + return "pg_amop"; + case PGM_COLUMN: + return "pg_attribute"; + case PGM_FUNCTION: + return "pg_proc"; + case PGM_INDEX: + return "pg_class"; + case PGM_TABLE: + return "pg_class"; + case PGM_VIEW: + return "pg_class"; + case PG_PARTITION: + return "pg_class"; + case PGM_LANGUAGE: + return "pg_language"; + case PGM_ROLE: + return "pg_authid"; + case PGM_SCHEMA: + return "pg_namespace"; + case PGM_SEQUENCE: + return "pg_class"; + case PGM_TABLESPACE: + return "pg_tablespace"; + case PGM_TRIGGER: + return "pg_trigger"; + case PGM_OPFAMILY: + return "pg_opfamily"; + default: + break; + } + return ""; +} void pgObject::ShowDependencies(frmMain *form, ctlListView *Dependencies, const wxString &wh) { @@ -465,8 +627,12 @@ void pgObject::ShowDependencies(frmMain *form, ctlListView *Dependencies, const wxString where; if (wh.IsEmpty()) { - if(!GetOidStr().IsSameAs(wxT("0"))) + if (!GetOidStr().IsSameAs(wxT("0"))) + { + wxString ts = GetClassTableName(GetMetaType()); where = wxT(" WHERE dep.objid=") + GetOidStr(); + if (!ts.IsEmpty()) where += " and dep.classid IN(select oid from pg_class where oid=dep.classid and relname='" + ts + "')"; + } else return; } @@ -510,25 +676,25 @@ void pgObject::ShowDependencies(frmMain *form, ctlListView *Dependencies, const wxT(" ELSE COALESCE(ext.extname,cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname,pub.prrelid::regclass::text)\n") wxT(" END AS refname,\n") wxT(" COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname) AS nspname\n") - wxT(" FROM pg_depend dep\n") - wxT(" LEFT JOIN pg_class cl ON dep.refobjid=cl.oid\n") - wxT(" LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum\n") + wxT(" FROM pg_depend dep join pg_class nc on nc.oid=dep.refclassid\n") + wxT(" LEFT JOIN pg_class cl ON dep.refobjid=cl.oid and nc.relname='pg_class'\n") + wxT(" LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum and nc.relname='pg_attribute'\n") wxT(" LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid\n") - wxT(" LEFT JOIN pg_proc pr ON dep.refobjid=pr.oid\n") + wxT(" LEFT JOIN pg_proc pr ON dep.refobjid=pr.oid and nc.relname='pg_proc'\n") wxT(" LEFT JOIN pg_namespace nsp ON pr.pronamespace=nsp.oid\n") - wxT(" LEFT JOIN pg_trigger tg ON dep.refobjid=tg.oid\n") - wxT(" LEFT JOIN pg_type ty ON dep.refobjid=ty.oid\n") + wxT(" LEFT JOIN pg_trigger tg ON dep.refobjid=tg.oid and nc.relname='pg_trigger'\n") + wxT(" LEFT JOIN pg_type ty ON dep.refobjid=ty.oid and nc.relname='pg_type'\n") wxT(" LEFT JOIN pg_namespace nst ON ty.typnamespace=nst.oid\n") - wxT(" LEFT JOIN pg_constraint co ON dep.refobjid=co.oid\n") + wxT(" LEFT JOIN pg_constraint co ON dep.refobjid=co.oid and nc.relname='pg_constraint'\n") wxT(" LEFT JOIN pg_class coc ON co.conrelid=coc.oid\n") wxT(" LEFT JOIN pg_namespace nso ON co.connamespace=nso.oid\n") - wxT(" LEFT JOIN pg_rewrite rw ON dep.refobjid=rw.oid\n") + wxT(" LEFT JOIN pg_rewrite rw ON dep.refobjid=rw.oid and nc.relname='pg_rewrite'\n") wxT(" LEFT JOIN pg_class clrw ON clrw.oid=rw.ev_class\n") wxT(" LEFT JOIN pg_namespace nsrw ON clrw.relnamespace=nsrw.oid\n") - wxT(" LEFT JOIN pg_language la ON dep.refobjid=la.oid\n") - wxT(" LEFT JOIN pg_namespace ns ON dep.refobjid=ns.oid\n") + wxT(" LEFT JOIN pg_language la ON dep.refobjid=la.oid and nc.relname='pg_language'\n") + wxT(" LEFT JOIN pg_namespace ns ON dep.refobjid=ns.oid and nc.relname='pg_namespace'\n") wxT(" LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum\n") - wxT(" LEFT JOIN pg_publication_rel pub ON dep.objid=pub.oid AND pub.prpubid=dep.refobjid\n") + wxT(" LEFT JOIN pg_publication_rel pub ON dep.objid=pub.oid AND pub.prpubid=dep.refobjid and nc.relname='pg_publication_rel'\n") wxT(" LEFT JOIN pg_extension ext ON ext.oid=dep.refobjid\n") + where, wxT("refclassid")); @@ -627,9 +793,7 @@ void pgObject::ShowDependencies(frmMain *form, ctlListView *Dependencies, const } } - - -void pgObject::ShowDependents(frmMain *form, ctlListView *referencedBy, const wxString &wh) +void pgObject::ShowDependents(frmMain* form, ctlListView* referencedBy, const wxString& wh) { if (this->IsCollection()) return; @@ -640,7 +804,11 @@ void pgObject::ShowDependents(frmMain *form, ctlListView *referencedBy, const wx wxString where; if (wh.IsEmpty()) + { + wxString ts = GetClassTableName(GetMetaType()); where = wxT(" WHERE dep.refobjid=") + GetOidStr(); + if (!ts.IsEmpty()) where += " and dep.refclassid IN(select oid from pg_class where oid=dep.refclassid and relname='" + ts + "')"; + } else where = wh; /* @@ -681,26 +849,26 @@ void pgObject::ShowDependents(frmMain *form, ctlListView *referencedBy, const wx wxT(" ELSE COALESCE(ext.extname,cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname,pub.prrelid::regclass::text) \n") wxT(" END AS refname,\n") wxT(" COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname) AS nspname\n") - wxT(" FROM pg_depend dep\n") - wxT(" LEFT JOIN pg_class cl ON dep.objid=cl.oid\n") - wxT(" LEFT JOIN pg_attribute att ON dep.objid=att.attrelid AND dep.objsubid=att.attnum\n") + wxT(" FROM pg_depend dep join pg_class nc on nc.oid=dep.classid\n") + wxT(" LEFT JOIN pg_class cl ON dep.objid=cl.oid and nc.relname='pg_class'\n") + wxT(" LEFT JOIN pg_attribute att ON dep.objid=att.attrelid AND dep.objsubid=att.attnum and nc.relname='pg_attribute'\n") wxT(" LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid\n") - wxT(" LEFT JOIN pg_proc pr ON dep.objid=pr.oid\n") + wxT(" LEFT JOIN pg_proc pr ON dep.objid=pr.oid and nc.relname='pg_proc'\n") wxT(" LEFT JOIN pg_namespace nsp ON pr.pronamespace=nsp.oid\n") - wxT(" LEFT JOIN pg_trigger tg ON dep.objid=tg.oid\n") - wxT(" LEFT JOIN pg_type ty ON dep.objid=ty.oid\n") + wxT(" LEFT JOIN pg_trigger tg ON dep.objid=tg.oid and nc.relname='pg_trigger'\n") + wxT(" LEFT JOIN pg_type ty ON dep.objid=ty.oid and nc.relname='pg_type'\n") wxT(" LEFT JOIN pg_namespace nst ON ty.typnamespace=nst.oid\n") - wxT(" LEFT JOIN pg_constraint co ON dep.objid=co.oid\n") + wxT(" LEFT JOIN pg_constraint co ON dep.objid=co.oid and nc.relname='pg_constraint'\n") wxT(" LEFT JOIN pg_class coc ON co.conrelid=coc.oid\n") wxT(" LEFT JOIN pg_namespace nso ON co.connamespace=nso.oid\n") - wxT(" LEFT JOIN pg_rewrite rw ON dep.objid=rw.oid\n") + wxT(" LEFT JOIN pg_rewrite rw ON dep.objid=rw.oid and nc.relname='pg_rewrite'\n") wxT(" LEFT JOIN pg_class clrw ON clrw.oid=rw.ev_class\n") wxT(" LEFT JOIN pg_namespace nsrw ON clrw.relnamespace=nsrw.oid\n") - wxT(" LEFT JOIN pg_language la ON dep.objid=la.oid\n") - wxT(" LEFT JOIN pg_namespace ns ON dep.objid=ns.oid\n") - wxT(" LEFT JOIN pg_attrdef ad ON ad.oid=dep.objid\n") - wxT(" LEFT JOIN pg_extension ext ON ext.oid=dep.objid\n") - wxT(" LEFT JOIN pg_publication_rel pub ON dep.objid=pub.oid AND pub.prpubid=dep.refobjid\n") + wxT(" LEFT JOIN pg_language la ON dep.objid=la.oid and nc.relname='pg_language'\n") + wxT(" LEFT JOIN pg_namespace ns ON dep.objid=ns.oid and nc.relname='pg_namespace'\n") + wxT(" LEFT JOIN pg_attrdef ad ON ad.oid=dep.objid and nc.relname='pg_attrdef'\n") + wxT(" LEFT JOIN pg_extension ext ON ext.oid=dep.objid and nc.relname='pg_extension'\n") + wxT(" LEFT JOIN pg_publication_rel pub ON dep.objid=pub.oid AND pub.prpubid=dep.refobjid and nc.relname='pg_publication_rel'\n") + where, wxT("classid")); /* @@ -2055,7 +2223,7 @@ if (1==0) { pgaFactory *ff=oo->GetFactory(); fn=ff->GetTypeName(); fn=oo->GetName(); - if (fn==wxT("debug*history__0102")) { + if (fn==wxT("debug**info_history2")) { fn=oo->GetFullName(); pgaFactory *ff=oo->GetFactory(); fn=ff->GetTypeName(); diff --git a/schema/pgPartition.cpp b/schema/pgPartition.cpp index f94d0d5..0b1a058 100644 --- a/schema/pgPartition.cpp +++ b/schema/pgPartition.cpp @@ -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); } @@ -254,7 +259,18 @@ pgObject *pgPartitionFactory::CreateObjects(pgCollection *coll, ctlTree *browser query += wxT(",case when lk.relation=rel.oid then 'AccessExclusiveLock' else pg_get_expr(rel.relpartbound, rel.oid) end \n AS partexp"); query += wxT(",(select count(*)from pg_inherits ii where ii.inhparent=rel.oid)>0 AS ispartitioned"); - + if (collection->GetDatabase()->BackendMinimumVersion(10, 0)) + { + //query += wxT(",\n pg_get_statisticsobjdef(stat_ext.oid) AS stat_stmt"); + //query += wxT(",\nCASE WHEN stat_ext.stxowner<>rel.relowner then 'ALTER STATISTICS '||substring(pg_get_statisticsobjdef(stat_ext.oid) from 'ICS (.+?)\\s\\(')||' OWNER TO '||stat_ext.stxowner::regrole else null end AS alter_stmt"); + query += ",(select string_agg(pg_get_statisticsobjdef(stat_ext.oid)||CASE WHEN stat_ext.stxowner<>rl.relowner then E';\\nALTER STATISTICS '||substring(pg_get_statisticsobjdef(stat_ext.oid) from 'ICS (.+?)\\s')||' OWNER TO '||stat_ext.stxowner::regrole else '' end" + ; + if (collection->GetDatabase()->BackendMinimumVersion(13, 0)) { + query += "||CASE WHEN stat_ext.stxstattarget<>-1 then E';\\nALTER STATISTICS '||substring(pg_get_statisticsobjdef(stat_ext.oid) from 'ICS (.+?)\\s')||' SET STATISTICS '||stat_ext.stxstattarget else '' end"; + } + query += ",E';\\n' order by stat_ext.stxrelid) stat_stmt from pg_class rl join pg_statistic_ext stat_ext on rl.oid=stat_ext.stxrelid where stat_ext.stxrelid=rel.oid) stat_stmt"; + } + query += wxT(" FROM pg_class rel\n") wxT(" JOIN pg_inherits i ON (rel.oid = i.inhrelid) \n") wxT(" LEFT JOIN pg_locks lk ON locktype='relation' and granted=true and mode='AccessExclusiveLock' and relation=rel.oid\n") @@ -386,6 +402,11 @@ pgObject *pgPartitionFactory::CreateObjects(pgCollection *coll, ctlTree *browser table->iSetPartKeyDef(tables->GetVal(wxT("partkeydef"))); table->iSetPartExp(tables->GetVal(wxT("partexp"))); table->iSetIsPartitioned(tables->GetBool(wxT("ispartitioned"))); + wxString st = tables->GetVal(wxT("stat_stmt")); + //wxString at = tables->GetVal(wxT("alter_stmt")); + if (!st.IsEmpty()) if ((st.Right(1) != ";")) st = st + wxT(";\n"); + table->iSetStatExt(st); + } if (collection->GetConnection()->GetIsGreenplum()) { diff --git a/schema/pgPublication.cpp b/schema/pgPublication.cpp index e9cbee7..e60e101 100644 --- a/schema/pgPublication.cpp +++ b/schema/pgPublication.cpp @@ -110,12 +110,21 @@ wxString pgPublication::GetSql(ctlTree *browser) if (!GetTablesStr().IsEmpty()) sql += wxT("") + GetTablesStr(); } - if (GetIsIns()&&GetIsUpd()&&GetIsDel()) + if (GetIsIns()&&GetIsUpd()&&GetIsDel()&&!GetIsViaRoot()) { - } else + } + else { - sql += wxT("\n WITH (publish = '") + GetStrOper() + wxT("')"); + sql += wxT("\n WITH ("); + wxString opts = wxEmptyString; + if (!(GetIsIns() && GetIsUpd() && GetIsDel())) opts =opts+ "publish = '" + GetStrOper() + wxT("'"); + if (GetIsViaRoot()) { + if (!opts.IsEmpty()) opts += ","; + opts += "publish_via_partition_root = on"; + } + if (!opts.IsEmpty()) opts += ")"; + sql += opts; } sql += wxT(";\n"); @@ -158,8 +167,13 @@ pgObject *pgPublicationFactory::CreateObjects(pgCollection *collection, ctlTree { wxString sql; pgPublication *publication = 0; + wxString pg13=""; + if (collection->GetDatabase()->BackendMinimumVersion(13, 0)) { + pg13 = ", pubviaroot"; + } sql = wxT("select x.oid,x.pubname, pg_get_userbyid(x.pubowner) AS owner, x.puballtables, x.pubinsert, x.pubupdate, x.pubdelete, obj_description(x.oid,'pg_publication') AS comment, t.t") + + pg13 + wxT("\n") wxT(" FROM pg_publication x\n") wxT(" LEFT JOIN LATERAL (select string_agg(schemaname||'.'||tablename,', ') t from pg_publication_tables where pubname=x.pubname) t on true \n") wxT(" ") @@ -183,6 +197,7 @@ pgObject *pgPublicationFactory::CreateObjects(pgCollection *collection, ctlTree publication->iSetIsDel(publications->GetBool(wxT("pubdelete"))); //publication->iSetVersion(publications->GetVal(wxT("extversion"))); publication->iSetComment(publications->GetVal(wxT("comment"))); + if (!pg13.IsEmpty()) publication->iSetIsViaRoot(publications->GetBool(wxT("pubviaroot"))); if (browser) { diff --git a/schema/pgRole.cpp b/schema/pgRole.cpp index 4da8504..7f3e29a 100644 --- a/schema/pgRole.cpp +++ b/schema/pgRole.cpp @@ -354,19 +354,21 @@ wxT(") aa where aa.grantee='")+GetName()+("' group by type,objname,grantee order wxString addgrant=wxEmptyString; while (!roles->Eof()) { + bool ad = true; if (wxT("tables")==roles->GetVal(wxT("type")) ) { addgrant += wxT("\nGRANT ") + roles->GetVal(wxT("priv")) + wxT(" ON TABLE ") + qtIdent(roles->GetVal(wxT("objname")).BeforeFirst('.'))+wxT(".")+qtIdent(roles->GetVal(wxT("objname")).AfterFirst('.')) + wxT(" TO ") + qtIdent(GetName()); - } - if (wxT("routine")==roles->GetVal(wxT("type")) ) { - addgrant += wxT("\nGRANT EXECUTE ON FUNCTION ") + qtIdent(roles->GetVal(wxT("objname")).BeforeFirst('.'))+wxT(".")+qtIdent(roles->GetVal(wxT("objname")).AfterFirst('.')) - + wxT(" TO ") + qtIdent(GetName()); - } - if (wxT("schema")==roles->GetVal(wxT("type")) ) { - addgrant += wxT("\nGRANT USAGE ON SCHEMA ") + qtIdent(roles->GetVal(wxT("objname"))) - + wxT(" TO ") + qtIdent(GetName()); - } - addgrant += wxT(";"); + } else if (wxT("routine")==roles->GetVal(wxT("type")) ) { + addgrant += wxT("\nGRANT EXECUTE ON FUNCTION ") + qtIdent(roles->GetVal(wxT("objname")).BeforeFirst('.'))+wxT(".")+qtIdent(roles->GetVal(wxT("objname")).AfterFirst('.')) + + wxT(" TO ") + qtIdent(GetName()); + } else if (wxT("schema")==roles->GetVal(wxT("type")) ) { + addgrant += wxT("\nGRANT USAGE ON SCHEMA ") + qtIdent(roles->GetVal(wxT("objname"))) + + wxT(" TO ") + qtIdent(GetName()); + } + else { + ad = false; + } + if (ad) addgrant += wxT(";"); roles->MoveNext(); } sql += wxT("\n")+ addgrant + wxT("\n"); diff --git a/schema/pgServer.cpp b/schema/pgServer.cpp index 7821b0b..a2f7492 100644 --- a/schema/pgServer.cpp +++ b/schema/pgServer.cpp @@ -1460,6 +1460,7 @@ bool pgServer::PauseReplay() { SetReplayPaused(true); wxString sql = wxT("SELECT pg_xlog_replay_pause()"); + if (conn->BackendMinimumVersion(10, 0)) sql = wxT("SELECT pg_wal_replay_pause()"); return conn->ExecuteVoid(sql); } @@ -1468,6 +1469,7 @@ bool pgServer::ResumeReplay() { SetReplayPaused(false); wxString sql = wxT("SELECT pg_xlog_replay_resume()"); + if (conn->BackendMinimumVersion(10, 0)) sql = wxT("SELECT pg_wal_replay_resume()"); return conn->ExecuteVoid(sql); } diff --git a/schema/pgTable.cpp b/schema/pgTable.cpp index 9931dae..aead2af 100644 --- a/schema/pgTable.cpp +++ b/schema/pgTable.cpp @@ -618,7 +618,7 @@ wxString pgTable::GetSql(ctlTree *browser) } } } - wxRegEx reg("vacuum_index_cleanup=[a-z]+|vacuum_truncate=[a-z]+|parallel_workers=[0-9]+|toast_tuple_target=[0-9]+|log_autovacuum_min_duration=[0-9]+|user_catalog_table=[a-z]+"); + wxRegEx reg("autovacuum_vacuum_insert_scale_factor=[0-9.]+|autovacuum_vacuum_insert_threshold=[0-9]+|vacuum_index_cleanup=[a-z]+|vacuum_truncate=[a-z]+|parallel_workers=[0-9]+|toast_tuple_target=[0-9]+|log_autovacuum_min_duration=[0-9]+|user_catalog_table=[a-z]+"); wxString relopt=GetRelOptions(); wxString o; size_t start, len; @@ -726,7 +726,7 @@ wxString pgTable::GetSql(ctlTree *browser) else sql += GetGrant(wxT("arwdRxt")); wxString st=GetStatExt(); - if (!st.IsEmpty()) sql += st + wxT(";\n"); + if (!st.IsEmpty()) sql += st + wxT("\n"); sql += GetCommentSql(); if (GetConnection()->BackendMinimumVersion(9, 1)) @@ -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")) + @@ -1572,8 +1582,14 @@ pgObject *pgTableFactory::CreateObjects(pgCollection *collection, ctlTree *brows if (collection->GetDatabase()->BackendMinimumVersion(10, 0)) { - query += wxT(",\n pg_get_statisticsobjdef(stat_ext.oid) AS stat_stmt"); - query += wxT(",\nCASE WHEN stat_ext.stxowner<>rel.relowner then 'ALTER STATISTICS '||substring(pg_get_statisticsobjdef(stat_ext.oid) from 'ICS (.+?)\\s\\(')||' OWNER TO '||stat_ext.stxowner::regrole else null end AS alter_stmt"); + //query += wxT(",\n pg_get_statisticsobjdef(stat_ext.oid) AS stat_stmt"); + //query += wxT(",\nCASE WHEN stat_ext.stxowner<>rel.relowner then 'ALTER STATISTICS '||substring(pg_get_statisticsobjdef(stat_ext.oid) from 'ICS (.+?)\\s\\(')||' OWNER TO '||stat_ext.stxowner::regrole else null end AS alter_stmt"); + query += ",(select string_agg(pg_get_statisticsobjdef(stat_ext.oid)||CASE WHEN stat_ext.stxowner<>rl.relowner then E';\\nALTER STATISTICS '||substring(pg_get_statisticsobjdef(stat_ext.oid) from 'ICS (.+?)\\s')||' OWNER TO '||stat_ext.stxowner::regrole else '' end" + ; + if (collection->GetDatabase()->BackendMinimumVersion(13, 0)) { + query += "||CASE WHEN stat_ext.stxstattarget<>-1 then E';\\nALTER STATISTICS '||substring(pg_get_statisticsobjdef(stat_ext.oid) from 'ICS (.+?)\\s')||' SET STATISTICS '||stat_ext.stxstattarget else '' end"; + } + query += ",E';\\n' order by stat_ext.stxrelid) stat_stmt from pg_class rl join pg_statistic_ext stat_ext on rl.oid=stat_ext.stxrelid where stat_ext.stxrelid=rel.oid) stat_stmt"; } query += wxT(" FROM pg_class rel\n") wxT(" LEFT JOIN pg_locks lk ON locktype='relation' and granted=true and mode='AccessExclusiveLock' and relation=rel.oid\n") @@ -1591,8 +1607,6 @@ pgObject *pgTableFactory::CreateObjects(pgCollection *collection, ctlTree *brows if (collection->GetConnection()->BackendMinimumVersion(9, 0)) query += wxT("LEFT JOIN pg_type typ ON rel.reloftype=typ.oid\n"); - if (collection->GetConnection()->BackendMinimumVersion(10, 0)) - query += wxT("LEFT JOIN pg_statistic_ext stat_ext ON rel.oid=stat_ext.stxrelid\n"); query += wxT(" WHERE ")+pg10+wxT(" rel.relkind IN ('r','s','t','p') AND rel.relnamespace = ") + collection->GetSchema()->GetOidStr() + wxT("\n"); @@ -1733,8 +1747,8 @@ pgObject *pgTableFactory::CreateObjects(pgCollection *collection, ctlTree *brows table->iSetPartExp(tables->GetVal(wxT("partexp"))); table->iSetIsPartitioned(tables->GetBool(wxT("ispartitioned"))); wxString st = tables->GetVal(wxT("stat_stmt")); - wxString at = tables->GetVal(wxT("alter_stmt")); - if (!st.IsEmpty()&&!at.IsEmpty()) st=st+wxT(";\n")+at; + //wxString at = tables->GetVal(wxT("alter_stmt")); + if (!st.IsEmpty()) if ((st.Right(1) != ";") ) st=st+wxT(";\n"); table->iSetStatExt(st); } diff --git a/ui/embed-xrc_v3.bat b/ui/embed-xrc_v3.bat index 14255fb..d8906ad 100644 --- a/ui/embed-xrc_v3.bat +++ b/ui/embed-xrc_v3.bat @@ -9,5 +9,5 @@ REM # embed-xrc.bat - convert xrc files to c++ files REM # REM ####################################################################### -"D:\PostgreSQL\pgadmin3\pgadmin\Debug_(3.0)\wxrc.exe" -c -o xrcDialogs.cpp *.xrc +"C:\Users\lsv\Source\Repos\wxrc\x64\Release\wxrc.exe" -c -o xrcDialogs.cpp *.xrc