mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 14:15:49 -06:00
Добавлена сортировка при щелчке по заголовку таблице. Дабавлена заполнение статистики при выборе секчионированной таблицы. Статистика берётся по всем уровням иерархии.
791 lines
26 KiB
C++
791 lines
26 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// pgAdmin III - PostgreSQL Tools
|
|
//
|
|
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
|
// This software is released under the PostgreSQL Licence
|
|
//
|
|
// pgIndex.cpp - Index class
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// wxWindows headers
|
|
#include <wx/wx.h>
|
|
|
|
// App headers
|
|
#include "pgAdmin3.h"
|
|
#include "frm/frmMain.h"
|
|
#include "utils/misc.h"
|
|
#include "utils/pgfeatures.h"
|
|
#include "schema/pgIndex.h"
|
|
#include "schema/pgConstraints.h"
|
|
#include "schema/pgIndexConstraint.h"
|
|
|
|
|
|
pgIndexBase::pgIndexBase(pgSchema *newSchema, pgaFactory &factory, const wxString &newName)
|
|
: pgSchemaObject(newSchema, factory, newName)
|
|
{
|
|
showExtendedStatistics = false;
|
|
}
|
|
|
|
wxString pgIndexBase::GetTranslatedMessage(int kindOfMessage) const
|
|
{
|
|
wxString message = wxEmptyString;
|
|
|
|
switch (kindOfMessage)
|
|
{
|
|
case RETRIEVINGDETAILS:
|
|
message = _("Retrieving details on index");
|
|
message += wxT(" ") + GetName();
|
|
break;
|
|
case REFRESHINGDETAILS:
|
|
message = _("Refreshing index");
|
|
message += wxT(" ") + GetName();
|
|
break;
|
|
case GRANTWIZARDTITLE:
|
|
message = _("Privileges for index");
|
|
message += wxT(" ") + GetName();
|
|
break;
|
|
case DROPINCLUDINGDEPS:
|
|
message = wxString::Format(_("Are you sure you wish to drop index \"%s\" including all objects that depend on it?"),
|
|
GetFullIdentifier().c_str());
|
|
break;
|
|
case DROPEXCLUDINGDEPS:
|
|
message = wxString::Format(_("Are you sure you wish to drop index \"%s\"?"),
|
|
GetFullIdentifier().c_str());
|
|
break;
|
|
case DROPCASCADETITLE:
|
|
message = _("Drop index cascaded?");
|
|
break;
|
|
case DROPTITLE:
|
|
message = _("Drop index?");
|
|
break;
|
|
case PROPERTIESREPORT:
|
|
message = _("Index properties report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case PROPERTIES:
|
|
message = _("Index properties");
|
|
break;
|
|
case DDLREPORT:
|
|
message = _("Index DDL report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case DDL:
|
|
message = _("Index DDL");
|
|
break;
|
|
case STATISTICSREPORT:
|
|
message = _("Index statistics report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case OBJSTATISTICS:
|
|
message = _("Index statistics");
|
|
break;
|
|
case DEPENDENCIESREPORT:
|
|
message = _("Index dependencies report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case DEPENDENCIES:
|
|
message = _("Index dependencies");
|
|
break;
|
|
case DEPENDENTSREPORT:
|
|
message = _("Index dependents report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case DEPENDENTS:
|
|
message = _("Index dependents");
|
|
break;
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
bool pgIndexBase::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
|
|
{
|
|
wxString sql = wxT("DROP INDEX ") + this->GetSchema()->GetQuotedIdentifier() + wxT(".") + this->GetQuotedIdentifier();
|
|
if (cascaded)
|
|
sql += wxT(" CASCADE");
|
|
return GetDatabase()->ExecuteVoid(sql);
|
|
}
|
|
|
|
wxString pgIndexBase::GetCreate()
|
|
{
|
|
wxString str;
|
|
// no functional indexes so far
|
|
|
|
str = wxT("CREATE ");
|
|
if (GetIsUnique())
|
|
str += wxT("UNIQUE ");
|
|
str += wxT("INDEX ");
|
|
str += qtIdent(GetName())
|
|
+ wxT("\n ON ") + GetQuotedSchemaPrefix(GetIdxSchema()) + qtIdent(GetIdxTable())
|
|
+ wxT("\n USING ") + GetIndexType()
|
|
+ wxT("\n (");
|
|
if (GetProcName().IsNull())
|
|
str += GetQuotedColumns();
|
|
else
|
|
{
|
|
str += GetQuotedSchemaPrefix(GetProcNamespace()) + qtIdent(GetProcName()) + wxT("(") + GetQuotedColumns() + wxT(")");
|
|
if (!this->GetOperatorClasses().IsNull())
|
|
str += wxT(" ") + GetOperatorClasses();
|
|
}
|
|
|
|
str += wxT(")");
|
|
|
|
// if (GetConnection()->BackendMinimumVersion(8, 2) && GetFillFactor().Length() > 0)
|
|
// str += wxT("\n WITH (FILLFACTOR=") + GetFillFactor() + wxT(")");
|
|
if (GetConnection()->BackendMinimumVersion(8, 2) && GetRelOptions().Length() > 0)
|
|
str += wxT("\n WITH (") + GetRelOptions() + wxT(")");
|
|
|
|
|
|
if (GetConnection()->BackendMinimumVersion(8, 0) && tablespace != GetDatabase()->GetDefaultTablespace())
|
|
str += wxT("\nTABLESPACE ") + qtIdent(tablespace);
|
|
|
|
AppendIfFilled(str, wxT("\n WHERE "), GetConstraint());
|
|
|
|
str += wxT(";\n");
|
|
|
|
if (GetConnection()->BackendMinimumVersion(7, 5))
|
|
if (GetIsClustered())
|
|
str += wxT("ALTER TABLE ") + GetQuotedSchemaPrefix(GetIdxSchema()) + qtIdent(GetIdxTable())
|
|
+ wxT(" CLUSTER ON ") + qtIdent(GetName())
|
|
+ wxT(";\n");
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
wxString pgIndexBase::GetSql(ctlTree *browser)
|
|
{
|
|
if (sql.IsNull())
|
|
{
|
|
sql = wxT("-- Index: ") + GetQuotedFullIdentifier() + wxT("\n\n")
|
|
+ wxT("-- DROP INDEX ") + GetQuotedFullIdentifier() + wxT(";\n\n")
|
|
+ GetCreate()
|
|
+ GetCommentSql();
|
|
}
|
|
return sql;
|
|
}
|
|
|
|
|
|
|
|
void pgIndexBase::ReadColumnDetails()
|
|
{
|
|
if (!expandedKids)
|
|
{
|
|
expandedKids = true;
|
|
bool indexconstraint = GetMetaType() == PGM_PRIMARYKEY || GetMetaType() == PGM_UNIQUE || GetMetaType() == PGM_EXCLUDE;
|
|
|
|
// Allocate memory to store column def
|
|
if (columnCount > 0) columnList.Alloc(columnCount);
|
|
|
|
if (GetConnection()->BackendMinimumVersion(7, 4))
|
|
{
|
|
long i;
|
|
|
|
for (i = 1 ; i <= columnCount ; i++)
|
|
{
|
|
if (i > 1)
|
|
{
|
|
columns += wxT(", ");
|
|
quotedColumns += wxT(", ");
|
|
}
|
|
|
|
wxString options, coldef, opcname;
|
|
if (GetConnection()->BackendMinimumVersion(8, 3))
|
|
options = wxT(" i.indoption[") + NumToStr((long)(i - 1)) + wxT("] AS options,\n");
|
|
|
|
pgSet *res;
|
|
wxString query;
|
|
|
|
if (GetConnection()->BackendMinimumVersion(9, 0))
|
|
{
|
|
query = wxT("SELECT\n") + options +
|
|
wxT(" pg_get_indexdef(i.indexrelid, ") + NumToStr(i) + GetDatabase()->GetPrettyOption() + wxT(") AS coldef,\n") +
|
|
wxT(" op.oprname,\n") +
|
|
wxT(" CASE WHEN (o.opcdefault = FALSE) THEN o.opcname ELSE null END AS opcname\n");
|
|
if (GetConnection()->BackendMinimumVersion(9, 1))
|
|
query += wxT(",\n coll.collname, nspc.nspname as collnspname\n");
|
|
query += wxT("FROM pg_index i\n")
|
|
wxT("JOIN pg_attribute a ON (a.attrelid = i.indexrelid AND attnum = ") + NumToStr(i) + wxT(")\n") +
|
|
wxT("LEFT OUTER JOIN pg_opclass o ON (o.oid = i.indclass[") + NumToStr((long)(i - 1)) + wxT("])\n") +
|
|
wxT("LEFT OUTER JOIN pg_constraint c ON (c.conindid = i.indexrelid) ")
|
|
wxT("LEFT OUTER JOIN pg_operator op ON (op.oid = c.conexclop[") + NumToStr(i) + wxT("])\n");
|
|
if (GetConnection()->BackendMinimumVersion(9, 1))
|
|
query += wxT("LEFT OUTER JOIN pg_collation coll ON a.attcollation=coll.oid\n")
|
|
wxT("LEFT OUTER JOIN pg_namespace nspc ON coll.collnamespace=nspc.oid\n");
|
|
query += wxT("WHERE i.indexrelid = ") + GetOidStr();
|
|
}
|
|
else
|
|
{
|
|
query = wxT("SELECT\n") + options +
|
|
wxT(" pg_get_indexdef(i.indexrelid, ") + NumToStr(i) + GetDatabase()->GetPrettyOption() + wxT(") AS coldef,\n") +
|
|
wxT(" CASE WHEN (o.opcdefault = FALSE) THEN o.opcname ELSE null END AS opcname\n") +
|
|
wxT("FROM pg_index i\n") +
|
|
wxT("JOIN pg_attribute a ON (a.attrelid = i.indexrelid AND attnum = ") + NumToStr(i) + wxT(")\n") +
|
|
wxT("LEFT OUTER JOIN pg_opclass o ON (o.oid = i.indclass[") + NumToStr((long)(i - 1)) + wxT("])\n") +
|
|
wxT("WHERE i.indexrelid = ") + GetOidStr();
|
|
}
|
|
|
|
res = ExecuteSet(query);
|
|
|
|
if (res->NumRows() > 0)
|
|
{
|
|
coldef = res->GetVal(wxT("coldef"));
|
|
|
|
if (GetConnection()->BackendMinimumVersion(9, 1) && !indexconstraint)
|
|
{
|
|
wxString collation = wxEmptyString;
|
|
if (!res->GetVal(wxT("collname")).IsEmpty())
|
|
{
|
|
collation = qtIdent(res->GetVal(wxT("collnspname"))) + wxT(".") + qtIdent(res->GetVal(wxT("collname")));
|
|
coldef += wxT(" COLLATE ") + collation;
|
|
}
|
|
collationsArray.Add(collation);
|
|
}
|
|
else
|
|
{
|
|
collationsArray.Add(wxEmptyString);
|
|
}
|
|
|
|
opcname = res->GetVal(wxT("opcname"));
|
|
opclassesArray.Add(opcname);
|
|
if (!opcname.IsEmpty())
|
|
coldef += wxT(" ") + opcname;
|
|
|
|
// Get the column options
|
|
if (GetConnection()->BackendMinimumVersion(8, 3))
|
|
{
|
|
long opt = res->GetLong(wxT("options"));
|
|
|
|
if (opt && (opt & 0x0001)) // Descending...
|
|
{
|
|
ordersArray.Add(wxT("DESC"));
|
|
coldef += wxT(" DESC");
|
|
// NULLS FIRST is the default for descending
|
|
if (!(opt && (opt & 0x0002)))
|
|
{
|
|
nullsArray.Add(wxT("NULLS LAST"));
|
|
coldef += wxT(" NULLS LAST");
|
|
}
|
|
else
|
|
{
|
|
nullsArray.Add(wxEmptyString);
|
|
}
|
|
}
|
|
else // Ascending...
|
|
{
|
|
ordersArray.Add(wxT("ASC"));
|
|
if ((opt && (opt & 0x0002)))
|
|
{
|
|
nullsArray.Add(wxT("NULLS FIRST"));
|
|
coldef += wxT(" NULLS FIRST");
|
|
}
|
|
else
|
|
{
|
|
nullsArray.Add(wxEmptyString);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ordersArray.Add(wxEmptyString);
|
|
nullsArray.Add(wxEmptyString);
|
|
}
|
|
}
|
|
|
|
if (isExclude)
|
|
{
|
|
coldef += wxT(" WITH ") + res->GetVal(wxT("oprname"));
|
|
}
|
|
columns += coldef;
|
|
quotedColumns += coldef;
|
|
columnList.Add(coldef);
|
|
|
|
//resolve memory leak occurred while expanding the index node in object browser
|
|
delete res;
|
|
res = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// its a 7.3 db
|
|
|
|
// We cannot use SELECT IN (colNumbers) here because we couldn't be sure
|
|
// about the read order
|
|
wxStringTokenizer collist(GetColumnNumbers());
|
|
wxStringTokenizer args(procArgTypeList);
|
|
wxString cn, ct;
|
|
columnCount = 0;
|
|
|
|
while (collist.HasMoreTokens())
|
|
{
|
|
cn = collist.GetNextToken();
|
|
ct = args.GetNextToken();
|
|
|
|
pgSet *colSet = ExecuteSet(
|
|
wxT("SELECT attname as conattname\n")
|
|
wxT(" FROM pg_attribute\n")
|
|
wxT(" WHERE attrelid=") + GetOidStr() + wxT(" AND attnum=") + cn);
|
|
if (colSet)
|
|
{
|
|
if (columnCount)
|
|
{
|
|
columns += wxT(", ");
|
|
quotedColumns += wxT(", ");
|
|
}
|
|
wxString colName = colSet->GetVal(0);
|
|
columns += colName;
|
|
columnList.Add(colName);
|
|
ordersArray.Add(wxEmptyString);
|
|
nullsArray.Add(wxEmptyString);
|
|
opclassesArray.Add(wxEmptyString);
|
|
collationsArray.Add(wxEmptyString);
|
|
quotedColumns += qtIdent(colName);
|
|
|
|
if (!ct.IsNull())
|
|
{
|
|
pgSet *typeSet = ExecuteSet(wxT(
|
|
"SELECT typname FROM pg_type where oid=") + ct);
|
|
if (typeSet)
|
|
{
|
|
if (columnCount)
|
|
{
|
|
procArgs += wxT(", ");
|
|
typedColumns += wxT(", ");
|
|
quotedTypedColumns += wxT(", ");
|
|
}
|
|
wxString colType = typeSet->GetVal(0);
|
|
procArgs += colType;
|
|
typedColumns += colName + wxT("::") + colType;
|
|
quotedTypedColumns += qtIdent(colName) + wxT("::") + colType;
|
|
delete typeSet;
|
|
}
|
|
}
|
|
delete colSet;
|
|
}
|
|
columnCount++;
|
|
}
|
|
}
|
|
wxStringTokenizer ops(operatorClassList);
|
|
wxString op;
|
|
while (ops.HasMoreTokens())
|
|
{
|
|
op = ops.GetNextToken();
|
|
pgSet *set = ExecuteSet(wxT(
|
|
"SELECT opcname FROM pg_opclass WHERE oid=") + op);
|
|
if (set)
|
|
{
|
|
if (!operatorClasses.IsNull())
|
|
operatorClasses += wxT(", ");
|
|
operatorClasses += set->GetVal(0);
|
|
delete set;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void pgIndexBase::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
|
|
{
|
|
ReadColumnDetails();
|
|
if (properties)
|
|
{
|
|
CreateListColumns(properties);
|
|
|
|
properties->AppendItem(_("Name"), GetName());
|
|
properties->AppendItem(_("OID"), GetOid());
|
|
if (GetConnection()->BackendMinimumVersion(8, 0))
|
|
properties->AppendItem(_("Tablespace"), tablespace);
|
|
if (!GetProcName().IsNull())
|
|
properties->AppendItem(_("Procedure "), GetSchemaPrefix(GetProcNamespace()) + GetProcName() + wxT("(") + GetTypedColumns() + wxT(")"));
|
|
else
|
|
properties->AppendItem(_("Columns"), GetColumns());
|
|
|
|
properties->AppendItem(_("Operator classes"), GetOperatorClasses());
|
|
properties->AppendYesNoItem(_("Unique?"), GetIsUnique());
|
|
properties->AppendYesNoItem(_("Primary?"), GetIsPrimary());
|
|
properties->AppendYesNoItem(_("Clustered?"), GetIsClustered());
|
|
properties->AppendYesNoItem(_("Valid?"), GetIsValid());
|
|
properties->AppendItem(_("Access method"), GetIndexType());
|
|
properties->AppendItem(_("Constraint"), GetConstraint());
|
|
properties->AppendYesNoItem(_("System index?"), GetSystemObject());
|
|
if (GetConnection()->BackendMinimumVersion(8, 2))
|
|
properties->AppendItem(_("Fill factor"), GetFillFactor());
|
|
properties->AppendItem(_("Comment"), firstLineOnly(GetComment()));
|
|
}
|
|
}
|
|
|
|
|
|
void pgIndexBase::ShowStatistics(frmMain *form, ctlListView *statistics)
|
|
{
|
|
wxString sql =
|
|
wxT("SELECT idx_scan AS ") + qtIdent(_("Index Scans")) +
|
|
wxT(", idx_tup_read AS ") + qtIdent(_("Index Tuples Read")) +
|
|
wxT(", idx_tup_fetch AS ") + qtIdent(_("Index Tuples Fetched")) +
|
|
wxT(", idx_blks_read AS ") + qtIdent(_("Index Blocks Read")) +
|
|
wxT(", idx_blks_hit AS ") + qtIdent(_("Index Blocks Hit"));
|
|
|
|
if (GetConnection()->HasFeature(FEATURE_SIZE))
|
|
sql += wxT(", pg_size_pretty(pg_relation_size(") + GetOidStr() + wxT(")) AS ") + qtIdent(_("Index Size"));
|
|
|
|
if (showExtendedStatistics)
|
|
{
|
|
sql += wxT(", version AS ") + qtIdent(_("Version")) + wxT(",\n")
|
|
wxT(" tree_level AS ") + qtIdent(_("Tree Level")) + wxT(",\n")
|
|
wxT(" pg_size_pretty(index_size) AS ") + qtIdent(_("Index Size")) + wxT(",\n")
|
|
wxT(" root_block_no AS ") + qtIdent(_("Root Block No")) + wxT(",\n")
|
|
wxT(" internal_pages AS ") + qtIdent(_("Internal Pages")) + wxT(",\n")
|
|
wxT(" leaf_pages AS ") + qtIdent(_("Leaf Pages")) + wxT(",\n")
|
|
wxT(" empty_pages AS ") + qtIdent(_("Empty Pages")) + wxT(",\n")
|
|
wxT(" deleted_pages AS ") + qtIdent(_("Deleted Pages")) + wxT(",\n")
|
|
wxT(" avg_leaf_density AS ") + qtIdent(_("Average Leaf Density")) + wxT(",\n")
|
|
wxT(" leaf_fragmentation AS ") + qtIdent(_("Leaf Fragmentation")) + wxT("\n")
|
|
wxT(" FROM pgstatindex('") + GetQuotedFullIdentifier() + wxT("'), pg_stat_all_indexes stat");
|
|
}
|
|
else
|
|
{
|
|
sql += wxT("\n")
|
|
wxT(" FROM pg_stat_all_indexes stat");
|
|
}
|
|
sql += wxT("\n")
|
|
wxT(" JOIN pg_statio_all_indexes statio ON stat.indexrelid = statio.indexrelid\n")
|
|
wxT(" JOIN pg_class cl ON cl.oid=stat.indexrelid\n")
|
|
wxT(" WHERE stat.indexrelid = ") + GetOidStr();
|
|
|
|
DisplayStatistics(statistics, sql);
|
|
}
|
|
|
|
|
|
pgObject *pgIndexBase::Refresh(ctlTree *browser, const wxTreeItemId item)
|
|
{
|
|
pgObject *index = 0;
|
|
pgCollection *coll = browser->GetParentCollection(item);
|
|
if (coll)
|
|
index = indexFactory.CreateObjects(coll, 0, wxT("\n AND cls.oid=") + GetOidStr());
|
|
|
|
return index;
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
mnu->Append(id, _("&Extended index statistics"), _("Get extended statistics via pgstatindex for the selected object."), wxITEM_CHECK);
|
|
}
|
|
|
|
|
|
wxWindow *executePgstatindexFactory::StartDialog(frmMain *form, pgObject *obj)
|
|
{
|
|
if (!((pgIndexBase *)obj)->GetShowExtendedStatistics())
|
|
{
|
|
((pgIndexBase *)obj)->iSetShowExtendedStatistics(true);
|
|
wxTreeItemId item = form->GetBrowser()->GetSelection();
|
|
if (obj == form->GetBrowser()->GetObject(item))
|
|
form->SelectStatisticsTab();
|
|
}
|
|
else
|
|
((pgIndexBase *)obj)->iSetShowExtendedStatistics(false);
|
|
|
|
form->GetMenuFactories()->CheckMenu(obj, form->GetMenuBar(), (ctlMenuToolbar *)form->GetToolBar());
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool executePgstatindexFactory::CheckEnable(pgObject *obj)
|
|
{
|
|
return obj &&
|
|
(obj->IsCreatedBy(indexFactory) || obj->IsCreatedBy(primaryKeyFactory)
|
|
|| obj->IsCreatedBy(uniqueFactory) || obj->IsCreatedBy(excludeFactory)) &&
|
|
((pgIndexBase *)obj)->HasPgstatindex();
|
|
}
|
|
|
|
bool executePgstatindexFactory::CheckChecked(pgObject *obj)
|
|
{
|
|
if (!obj)
|
|
return false;
|
|
|
|
if (obj->GetMetaType() == PGM_INDEX || obj->GetMetaType() == PGM_PRIMARYKEY
|
|
|| obj->GetMetaType() == PGM_UNIQUE || obj->GetMetaType() == PGM_EXCLUDE)
|
|
return ((pgIndexBase *)obj)->GetShowExtendedStatistics();
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
pgIndex::pgIndex(pgSchema *newSchema, const wxString &newName)
|
|
: pgIndexBase(newSchema, indexFactory, newName)
|
|
{
|
|
}
|
|
|
|
|
|
pgObject *pgIndexBaseFactory::CreateObjects(pgCollection *coll, ctlTree *browser, const wxString &restriction)
|
|
{
|
|
pgSchemaObjCollection *collection = (pgSchemaObjCollection *)coll;
|
|
pgIndexBase *index = 0;
|
|
wxString query;
|
|
|
|
wxString proname, projoin;
|
|
if (collection->GetConnection()->BackendMinimumVersion(7, 4))
|
|
{
|
|
proname = wxT("indnatts, ");
|
|
if (collection->GetConnection()->BackendMinimumVersion(7, 5))
|
|
{
|
|
proname += wxT("cls.reltablespace AS spcoid, spcname, ");
|
|
projoin = wxT(" LEFT OUTER JOIN pg_tablespace ta on ta.oid=cls.reltablespace\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
proname = wxT("proname, pn.nspname as pronspname, proargtypes, ");
|
|
projoin = wxT(" LEFT OUTER JOIN pg_proc pr ON pr.oid=indproc\n")
|
|
wxT(" LEFT OUTER JOIN pg_namespace pn ON pn.oid=pr.pronamespace\n");
|
|
}
|
|
query = wxT("SELECT DISTINCT ON(cls.relname) cls.oid, cls.relname as idxname, indrelid, indkey, indisclustered, indisvalid, indisunique, indisprimary, n.nspname,\n")
|
|
wxT(" ") + proname + wxT("tab.relname as tabname, indclass, con.oid AS conoid, CASE contype WHEN 'p' THEN desp.description WHEN 'u' THEN desp.description WHEN 'x' THEN desp.description ELSE des.description END AS description,\n")
|
|
wxT(" pg_get_expr(indpred, indrelid") + collection->GetDatabase()->GetPrettyOption() + wxT(") as indconstraint, contype, condeferrable, condeferred, amname, array_to_string(cls.reloptions, ',') reloptions\n");
|
|
if (collection->GetConnection()->BackendMinimumVersion(8, 2))
|
|
query += wxT(", substring(array_to_string(cls.reloptions, ',') from 'fillfactor=([0-9]*)') AS fillfactor \n");
|
|
query += wxT(" FROM pg_index idx\n")
|
|
wxT(" JOIN pg_class cls ON cls.oid=indexrelid\n")
|
|
wxT(" JOIN pg_class tab ON tab.oid=indrelid\n")
|
|
+ projoin +
|
|
wxT(" JOIN pg_namespace n ON n.oid=tab.relnamespace\n")
|
|
wxT(" JOIN pg_am am ON am.oid=cls.relam\n")
|
|
wxT(" LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i')\n")
|
|
wxT(" LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)\n")
|
|
wxT(" LEFT OUTER JOIN pg_description des ON (des.objoid=cls.oid AND des.classoid='pg_class'::regclass)\n")
|
|
wxT(" LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0 AND desp.classoid='pg_constraint'::regclass)\n")
|
|
wxT(" WHERE indrelid = ") + collection->GetOidStr()
|
|
+ restriction + wxT("\n")
|
|
wxT(" ORDER BY cls.relname");
|
|
pgSet *indexes = collection->GetDatabase()->ExecuteSet(query);
|
|
|
|
if (indexes)
|
|
{
|
|
while (!indexes->Eof())
|
|
{
|
|
switch (*(indexes->GetCharPtr(wxT("contype"))))
|
|
{
|
|
case 0:
|
|
index = new pgIndex(collection->GetSchema()->GetSchema(), indexes->GetVal(wxT("idxname")));
|
|
break;
|
|
case 'p':
|
|
index = new pgPrimaryKey(collection->GetSchema()->GetSchema(), indexes->GetVal(wxT("idxname")));
|
|
((pgPrimaryKey *)index)->iSetConstraintOid(indexes->GetOid(wxT("conoid")));
|
|
break;
|
|
case 'u':
|
|
index = new pgUnique(collection->GetSchema()->GetSchema(), indexes->GetVal(wxT("idxname")));
|
|
((pgUnique *)index)->iSetConstraintOid(indexes->GetOid(wxT("conoid")));
|
|
break;
|
|
case 'x':
|
|
index = new pgExclude(collection->GetSchema()->GetSchema(), indexes->GetVal(wxT("idxname")));
|
|
((pgExclude *)index)->iSetConstraintOid(indexes->GetOid(wxT("conoid")));
|
|
break;
|
|
default:
|
|
index = 0;
|
|
break;
|
|
}
|
|
|
|
index->iSetOid(indexes->GetOid(wxT("oid")));
|
|
index->iSetIsClustered(indexes->GetBool(wxT("indisclustered")));
|
|
index->iSetIsValid(indexes->GetBool(wxT("indisvalid")));
|
|
index->iSetIsUnique(indexes->GetBool(wxT("indisunique")));
|
|
index->iSetIsPrimary(indexes->GetBool(wxT("indisprimary")));
|
|
index->iSetIsExclude(*(indexes->GetCharPtr(wxT("contype"))) == 'x');
|
|
index->iSetColumnNumbers(indexes->GetVal(wxT("indkey")));
|
|
index->iSetIdxSchema(indexes->GetVal(wxT("nspname")));
|
|
index->iSetComment(indexes->GetVal(wxT("description")));
|
|
index->iSetRelOptions(indexes->GetVal(wxT("reloptions")));
|
|
index->iSetIdxTable(indexes->GetVal(wxT("tabname")));
|
|
index->iSetRelTableOid(indexes->GetOid(wxT("indrelid")));
|
|
if (collection->GetConnection()->BackendMinimumVersion(7, 4))
|
|
{
|
|
index->iSetColumnCount(indexes->GetLong(wxT("indnatts")));
|
|
if (collection->GetConnection()->BackendMinimumVersion(8, 0))
|
|
{
|
|
if (indexes->GetOid(wxT("spcoid")) == 0)
|
|
index->iSetTablespaceOid(collection->GetDatabase()->GetTablespaceOid());
|
|
else
|
|
index->iSetTablespaceOid(indexes->GetOid(wxT("spcoid")));
|
|
|
|
if (indexes->GetVal(wxT("spcname")) == wxEmptyString)
|
|
index->iSetTablespace(collection->GetDatabase()->GetTablespace());
|
|
else
|
|
index->iSetTablespace(indexes->GetVal(wxT("spcname")));
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
index->iSetColumnCount(0L);
|
|
index->iSetProcNamespace(indexes->GetVal(wxT("pronspname")));
|
|
index->iSetProcName(indexes->GetVal(wxT("proname")));
|
|
index->iSetProcArgTypeList(indexes->GetVal(wxT("proargtypes")));
|
|
}
|
|
index->iSetOperatorClassList(indexes->GetVal(wxT("indclass")));
|
|
index->iSetDeferrable(indexes->GetBool(wxT("condeferrable")));
|
|
index->iSetDeferred(indexes->GetBool(wxT("condeferred")));
|
|
index->iSetConstraint(indexes->GetVal(wxT("indconstraint")));
|
|
index->iSetIndexType(indexes->GetVal(wxT("amname")));
|
|
if (collection->GetConnection()->BackendMinimumVersion(8, 2))
|
|
index->iSetFillFactor(indexes->GetVal(wxT("fillfactor")));
|
|
|
|
if (browser)
|
|
{
|
|
browser->AppendObject(collection, index);
|
|
indexes->MoveNext();
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
delete indexes;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
|
|
pgCollection *pgIndexBaseFactory::CreateCollection(pgObject *obj)
|
|
{
|
|
return new pgIndexBaseCollection(GetCollectionFactory(), (pgSchema *)obj);
|
|
}
|
|
|
|
|
|
|
|
pgObject *pgIndexFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
|
|
{
|
|
return pgIndexBaseFactory::CreateObjects(collection, browser, restriction + wxT("\n AND conname IS NULL"));
|
|
}
|
|
|
|
|
|
wxString pgIndexBaseCollection::GetTranslatedMessage(int kindOfMessage) const
|
|
{
|
|
wxString message = wxEmptyString;
|
|
|
|
switch (kindOfMessage)
|
|
{
|
|
case RETRIEVINGDETAILS:
|
|
message = _("Retrieving details on indexes");
|
|
break;
|
|
case REFRESHINGDETAILS:
|
|
message = _("Refreshing indexes");
|
|
break;
|
|
case OBJECTSLISTREPORT:
|
|
message = _("Indexes list report");
|
|
break;
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
/////////////////////////////
|
|
|
|
#include "images/index.pngc"
|
|
#include "images/indexes.pngc"
|
|
|
|
pgIndexFactory::pgIndexFactory()
|
|
: pgIndexBaseFactory(__("Index"), __("New Index..."), __("Create a new Index."), index_png_img)
|
|
{
|
|
metaType = PGM_INDEX;
|
|
}
|
|
|
|
|
|
pgIndexFactory indexFactory;
|
|
static pgaCollectionFactory cf(&indexFactory, __("Indexes"), indexes_png_img);
|
|
|
|
|
|
pgIndexBaseCollection::pgIndexBaseCollection(pgaFactory *factory, pgSchema *sch)
|
|
: pgSchemaObjCollection(factory, sch)
|
|
{
|
|
}
|
|
|
|
|
|
void pgIndexBaseCollection::ShowStatistics(frmMain *form, ctlListView *statistics)
|
|
{
|
|
wxLogInfo(wxT("Displaying statistics for indexes on ") + GetSchema()->GetName());
|
|
|
|
bool hasSize = GetConnection()->HasFeature(FEATURE_SIZE);
|
|
|
|
// Add the statistics view columns
|
|
statistics->ClearAll();
|
|
statistics->AddColumn(_("Index Name"));
|
|
statistics->AddColumn(_("Index Scans"));
|
|
statistics->AddColumn(_("Index Tuples Read"));
|
|
statistics->AddColumn(_("Index Tuples Fetched"));
|
|
if (hasSize)
|
|
statistics->AddColumn(_("Size"));
|
|
|
|
wxString sql = wxT("SELECT indexrelname, ")
|
|
wxT("idx_scan, idx_tup_read, idx_tup_fetch");
|
|
|
|
if (hasSize)
|
|
sql += wxT(", pg_size_pretty(pg_relation_size(indexrelid)) AS ") + qtIdent(wxT("size"));
|
|
|
|
sql += wxT("\n")
|
|
wxT(" FROM pg_stat_all_indexes stat\n")
|
|
wxT(" JOIN pg_class cls ON cls.oid=indexrelid\n")
|
|
wxT(" LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint'))\n")
|
|
wxT(" LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)\n")
|
|
wxT(" WHERE schemaname = ") + qtDbString(GetSchema()->GetSchema()->GetName())
|
|
+ wxT(" AND stat.relname = ") + qtDbString(GetSchema()->GetName())
|
|
+ wxT(" AND con.contype IS NULL")
|
|
+ wxT("\n ORDER BY indexrelname");
|
|
|
|
pgSet *stats = GetDatabase()->ExecuteSet(sql);
|
|
|
|
if (stats)
|
|
{
|
|
long pos = 0;
|
|
while (!stats->Eof())
|
|
{
|
|
statistics->InsertItem(pos, stats->GetVal(wxT("indexrelname")), PGICON_STATISTICS);
|
|
statistics->SetItem(pos, 1, stats->GetVal(wxT("idx_scan")));
|
|
statistics->SetItem(pos, 2, stats->GetVal(wxT("idx_tup_read")));
|
|
statistics->SetItem(pos, 3, stats->GetVal(wxT("idx_tup_fetch")));
|
|
if (hasSize)
|
|
statistics->SetItem(pos, 4, stats->GetVal(wxT("size")));
|
|
stats->MoveNext();
|
|
pos++;
|
|
}
|
|
|
|
delete stats;
|
|
}
|
|
statistics->SetColumnWidth(0, wxLIST_AUTOSIZE);
|
|
}
|