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