pgadmin3/schema/pgIndex.cpp
lsv f64972182a Add column sort ctlListView. Add Partition statistics.
Добавлена сортировка при щелчке по заголовку таблице.
Дабавлена заполнение статистики при выборе секчионированной таблицы.
Статистика берётся по всем уровням иерархии.
2020-12-31 15:40:54 +05:00

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);
}