////////////////////////////////////////////////////////////////////////// // // pgAdmin III - PostgreSQL Tools // // Copyright (C) 2002 - 2016, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // pgTable.cpp - Table class // ////////////////////////////////////////////////////////////////////////// #include "pgAdmin3.h" #include "utils/misc.h" #include "frm/frmHint.h" #include "frm/frmMain.h" #include "frm/frmMaintenance.h" #include "schema/pgTable.h" #include "schema/pgColumn.h" #include "schema/pgIndexConstraint.h" #include "schema/pgForeignKey.h" #include "schema/pgCheck.h" #include "utils/sysSettings.h" #include "utils/pgfeatures.h" #include "schema/pgRule.h" #include "schema/pgTrigger.h" #include "schema/pgConstraints.h" #include "schema/gpPartition.h" #include "schema/pgPartition.h" #include // App headers pgTable::pgTable(pgSchema *newSchema, const wxString &newName) : pgSchemaObject(newSchema, tableFactory, newName) { Init(); } pgTable::pgTable(pgSchema *newSchema, pgaFactory &newFactory, const wxString &newName) : pgSchemaObject(newSchema, newFactory, newName) { Init(); } pgTable::~pgTable() { } void pgTable::Init() { rows = 0; estimatedRows = 0.0; hasToastTable = false; autovacuum_enabled = 0; toast_autovacuum_enabled = 0; isPartitioned = false; hasOids = false; unlogged = false; hasSubclass = false; rowsCounted = false; isReplicated = false; showExtendedStatistics = false; distributionIsRandom = false; inheritedTableCount = 0; triggerCount = 0; tablespaceOid = 0; ofTypeOid = 0; } wxString pgTable::GetTranslatedMessage(int kindOfMessage) const { wxString message = wxEmptyString; switch (kindOfMessage) { case RETRIEVINGDETAILS: message = _("Retrieving details on table"); message += wxT(" ") + GetName(); break; case REFRESHINGDETAILS: message = _("Refreshing table"); message += wxT(" ") + GetName(); break; case DROPINCLUDINGDEPS: message = wxString::Format(_("Are you sure you wish to drop table \"%s\" including all objects that depend on it?"), GetFullIdentifier().c_str()); break; case DROPEXCLUDINGDEPS: message = wxString::Format(_("Are you sure you wish to drop table \"%s\"?"), GetFullIdentifier().c_str()); break; case DROPCASCADETITLE: message = _("Drop table cascaded?"); break; case DROPTITLE: message = _("Drop table?"); break; case PROPERTIESREPORT: message = _("Table properties report"); message += wxT(" - ") + GetName(); break; case PROPERTIES: message = _("Table properties"); break; case DDLREPORT: message = _("Table DDL report"); message += wxT(" - ") + GetName(); break; case DDL: message = _("Table DDL"); break; case DATADICTIONNARYREPORT: message = _("Table Data dictionary report"); message += wxT(" - ") + GetName(); break; case STATISTICSREPORT: message = _("Table statistics report"); message += wxT(" - ") + GetName(); break; case OBJSTATISTICS: message = _("Table statistics"); break; case DEPENDENCIESREPORT: message = _("Table dependencies report"); message += wxT(" - ") + GetName(); break; case DEPENDENCIES: message = _("Table dependencies"); break; case DEPENDENTSREPORT: message = _("Table dependents report"); message += wxT(" - ") + GetName(); break; case DEPENDENTS: message = _("Table dependents"); break; case BACKUPTITLE: message = wxString::Format(_("Backup table \"%s\""), GetFullIdentifier().c_str()); break; case RESTORETITLE: message = wxString::Format(_("Restore table \"%s\""), GetFullIdentifier().c_str()); break; } return message; } int pgTable::GetIconId() { if (isReplicated) return tableFactory.GetReplicatedIconId(); else if (!GetPartKeyDef().IsEmpty()||GetIsPartitioned()) return tableFactory.GetPartitionsIconId(); else return tableFactory.GetIconId(); } wxMenu *pgTable::GetNewMenu() { wxMenu *menu = pgObject::GetNewMenu(); if (schema->GetCreatePrivilege()) { columnFactory.AppendMenu(menu); if (GetPrimaryKey().IsEmpty()) // Will not notice if pk has been added after last refresh primaryKeyFactory.AppendMenu(menu); foreignKeyFactory.AppendMenu(menu); excludeFactory.AppendMenu(menu); uniqueFactory.AppendMenu(menu); checkFactory.AppendMenu(menu); indexFactory.AppendMenu(menu); ruleFactory.AppendMenu(menu); triggerFactory.AppendMenu(menu); /* * TEMPORARY: Disable adding new partitions until that code is working right. * if (GetConnection() != 0 && GetConnection()->GetIsGreenplum() && GetIsPartitioned()) partitionFactory.AppendMenu(menu); */ } return menu; } int pgTable::GetReplicationStatus(ctlTree *browser, wxString *clusterName, long *setId) { wxArrayString clusters = GetDatabase()->GetSlonyClusters(browser); bool isSubscribed = false; size_t i; for (i = 0 ; i < clusters.GetCount() ; i++) { wxString nsp = qtIdent(wxT("_") + clusters.Item(i)); pgSetIterator sets(GetConnection(), wxT("SELECT tab_set, sub_provider, ") + nsp + wxT(".getlocalnodeid(") + qtDbString(wxT("_") + clusters.Item(i)) + wxT(") AS localnode\n") wxT(" FROM ") + nsp + wxT(".sl_table\n") wxT(" LEFT JOIN ") + nsp + wxT(".sl_subscribe ON sub_set=tab_set\n") wxT(" WHERE tab_reloid = ") + GetOidStr()); if (sets.RowsLeft()) { if (clusterName) *clusterName = clusters.Item(i); if (setId) *setId = sets.GetLong(wxT("tab_set")); if (isSubscribed) return REPLICATIONSTATUS_MULTIPLY_PUBLISHED; long provider = sets.GetLong(wxT("sub_provider")); if (provider) { if (provider != sets.GetLong(wxT("localnode"))) return REPLICATIONSTATUS_REPLICATED; isSubscribed = true; } } } if (isSubscribed) return REPLICATIONSTATUS_SUBSCRIBED; return REPLICATIONSTATUS_NONE; } wxString pgTable::GetHelpPage(bool forCreate) const { if (forCreate) return wxT("pg/sql-createtable"); else return wxT("pg/sql-altertable"); } bool pgTable::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded) { wxString sql = wxT("DROP TABLE ") + this->GetSchema()->GetQuotedIdentifier() + wxT(".") + this->GetQuotedIdentifier(); if (cascaded) sql += wxT(" CASCADE"); return GetDatabase()->ExecuteVoid(sql); } bool pgTable::Truncate(bool cascaded) { wxString sql = wxT("TRUNCATE TABLE ") + this->GetSchema()->GetQuotedIdentifier() + wxT(".") + this->GetQuotedIdentifier(); if (cascaded) sql += wxT(" CASCADE"); return GetDatabase()->ExecuteVoid(sql); } bool pgTable::ResetStats() { wxString sql = wxT("SELECT pg_stat_reset_single_table_counters(") + NumToStr(this->GetOid()) + wxT(")"); return GetDatabase()->ExecuteVoid(sql); } void pgTable::AppendStuff(wxString &sql, ctlTree *browser, pgaFactory &factory) { wxString tmp; pgCollection *collection = browser->FindCollection(factory, GetId()); if (collection) { tmp += wxT("\n"); collection->ShowTreeDetail(browser); treeObjectIterator idxIt(browser, collection); pgObject *obj; while ((obj = idxIt.GetNextObject()) != 0) { obj->ShowTreeDetail(browser); tmp += obj->GetSql(browser) + wxT("\n"); } } if (!tmp.IsEmpty() && tmp != wxT("\n")) sql += tmp; } void pgTable::AppendStuffNoSql(wxString &sql, ctlTree *browser, pgaFactory &factory) { pgCollection *collection = browser->FindCollection(factory, GetId()); if (collection) { collection->ShowTreeDetail(browser); treeObjectIterator idxIt(browser, collection); pgObject *obj; while ((obj = idxIt.GetNextObject()) != 0) { obj->ShowTreeDetail(browser); } } } wxString pgTable::GetSql(ctlTree *browser) { wxString colDetails, conDetails; wxString prevComment; wxString cols_sql = wxEmptyString; wxString cols_type = wxEmptyString; wxString columnPrivileges; if (sql.IsNull()) { // make sure all kids are appended ShowTreeDetail(browser); sql = wxT("-- Table: ") + GetQuotedFullIdentifier() + wxT("\n\n") + wxT("-- DROP TABLE ") + GetQuotedFullIdentifier() + wxT(";") + wxT("\n\nCREATE "); if (GetUnlogged()) sql += wxT("UNLOGGED "); sql += wxT("TABLE ") + GetQuotedFullIdentifier(); // of type (9.0 material) if (ofTypeOid > 0) sql += wxT("\nOF ") + qtIdent(ofType); // Get a count of the constraints. int consCount = 0; pgCollection *constraints = browser->FindCollection(primaryKeyFactory, GetId()); if (constraints) consCount = browser->GetChildrenCount(constraints->GetId()); // Get the columns pgCollection *columns = browser->FindCollection(columnFactory, GetId()); if (columns) { columns->ShowTreeDetail(browser); treeObjectIterator colIt1(browser, columns); treeObjectIterator colIt2(browser, columns); int lastRealCol = 0; int currentCol = 0; pgColumn *column; // Iterate the columns to find the last 'real' one while ((column = (pgColumn *)colIt1.GetNextObject()) != 0) { if (currentCol==0) {cols_type+=column->GetDisplayName()+wxT(" ")+column->GetQuotedTypename(); } else cols_type+=wxT(", ")+column->GetDisplayName()+wxT(" ")+column->GetQuotedTypename(); currentCol++; //AppendIfFilled(cols_type,wxT(","),cols_type); if (column->GetInheritedCount() == 0) lastRealCol = currentCol; } cols_type=wxT("-- (")+cols_type+wxT(")"); // Now build the actual column list int colCount = 0; while ((column = (pgColumn *)colIt2.GetNextObject()) != 0) { column->ShowTreeDetail(browser); if (column->GetColNumber() > 0) { if (colCount) { // Only add a comma if this isn't the last 'real' column, or if there are constraints if (colCount != lastRealCol || consCount) cols_sql += wxT(","); if (!prevComment.IsEmpty()) cols_sql += wxT(" -- ") + firstLineOnly(prevComment); cols_sql += wxT("\n"); } if (column->GetInheritedCount() > 0) { if (!column->GetIsLocal()) { cols_sql += wxString::Format(wxT("-- %s "), _("Inherited")) + wxT("from table ") + column->GetInheritedTableName() + wxT(":"); } } if (ofTypeOid > 0) { if (column->GetDefinition().Length() == 0) { cols_sql += wxString::Format(wxT("-- %s "), _("Inherited")) + wxT("from type ") + ofType + wxT(": ") + column->GetQuotedIdentifier(); } else { cols_sql += wxT(" ") + column->GetQuotedIdentifier() + wxT(" WITH OPTIONS ") + column->GetDefinition(); } } else { cols_sql += wxT(" ") + column->GetQuotedIdentifier() + wxT(" ") + column->GetDefinition(); } prevComment = column->GetComment(); // Whilst we are looping round the columns, grab their comments as well. colDetails += column->GetCommentSql(); if (colDetails.Length() > 0) if (colDetails.Last() != '\n') colDetails += wxT("\n"); if (!GetDatabase()->BackendMinimumVersion(16, 0)) colDetails += column->GetStorageSql(); if (colDetails.Length() > 0) if (colDetails.Last() != '\n') colDetails += wxT("\n"); colDetails += column->GetAttstattargetSql(); if (colDetails.Length() > 0) if (colDetails.Last() != '\n') colDetails += wxT("\n"); colDetails += column->GetVariablesSql(); if (colDetails.Length() > 0) if (colDetails.Last() != '\n') colDetails += wxT("\n"); colCount++; columnPrivileges += column->GetPrivileges(); } } } // Now iterate the constraints if (constraints) { constraints->ShowTreeDetail(browser); treeObjectIterator consIt(browser, constraints); pgObject *data; while ((data = consIt.GetNextObject()) != 0) { data->ShowTreeDetail(browser); cols_sql += wxT(","); if (!prevComment.IsEmpty()) cols_sql += wxT(" -- ") + firstLineOnly(prevComment); cols_sql += wxT("\n CONSTRAINT ") + data->GetQuotedIdentifier() + wxT(" ") + data->GetTypeName().Upper() + wxT(" ") ; prevComment = data->GetComment(); if (!data->GetComment().IsEmpty()) conDetails += wxT("COMMENT ON CONSTRAINT ") + data->GetQuotedIdentifier() + wxT(" ON ") + GetQuotedFullIdentifier() + wxT(" IS ") + qtDbString(data->GetComment()) + wxT(";\n"); switch (data->GetMetaType()) { case PGM_PRIMARYKEY: case PGM_UNIQUE: case PGM_EXCLUDE: cols_sql += ((pgIndexConstraint *)data)->GetDefinition(); break; case PGM_FOREIGNKEY: cols_sql += ((pgForeignKey *)data)->GetDefinition(); break; case PGM_CHECK: cols_sql += wxT("(") + ((pgCheck *)data)->GetDefinition() + wxT(")"); if (GetDatabase()->BackendMinimumVersion(9, 2) && ((pgCheck *)data)->GetNoInherit()) cols_sql += wxT(" NO INHERIT"); if (GetDatabase()->BackendMinimumVersion(9, 2) && !((pgCheck *)data)->GetValid()) cols_sql += wxT(" NOT VALID"); break; } } } if (!prevComment.IsEmpty()) cols_sql += wxT(" -- ") + firstLineOnly(prevComment); if (GetConnection()->BackendMinimumVersion(10, 0)&&!GetPartExp().IsEmpty()) { sql += wxT("\n PARTITION OF ") + GetQuotedInheritedTables() + wxT("(\n") + cols_sql + wxT("\n)"); sql += wxT(" ") + GetPartExp() + wxT(" "); } else { sql += wxT("\n(\n") + cols_sql + wxT("\n)"); } if (GetInheritedTableCount()) { if (GetConnection()->BackendMinimumVersion(10, 0)&&!GetPartExp().IsEmpty()) { } else { sql += wxT("\nINHERITS (") + GetQuotedInheritedTables() + wxT(")"); } } if (GetConnection()->BackendMinimumVersion(10, 0)&&!GetPartKeyDef().IsEmpty()) { sql += wxT("\nPARTITION BY ") + GetPartKeyDef() + wxT(" "); } if (GetConnection()->BackendMinimumVersion(8, 2)) { wxString sqlopt=wxEmptyString; if (GetFillFactor().Length() > 0) sqlopt += wxT("\n FILLFACTOR=") + GetFillFactor() + wxT(", "); if (GetAppendOnly().Length() > 0) sqlopt += wxT("APPENDONLY=") + GetAppendOnly() + wxT(", "); if (GetCompressLevel().Length() > 0) sqlopt += wxT("COMPRESSLEVEL=") + GetCompressLevel() + wxT(", "); if (GetOrientation().Length() > 0) sqlopt += wxT("ORIENTATION=") + GetOrientation() + wxT(", "); if (GetCompressType().Length() > 0) sqlopt += wxT("COMPRESSTYPE=") + GetCompressType() + wxT(", "); if (GetBlocksize().Length() > 0) sqlopt += wxT("BLOCKSIZE=") + GetBlocksize() + wxT(", "); if (GetChecksum().Length() > 0) sqlopt += wxT("CHECKSUM=") + GetChecksum() + wxT(", "); if (GetConnection()->BackendMinimumVersion(12, 0)) { if (!sqlopt.IsEmpty()) sqlopt=sqlopt.BeforeLast(','); } else { if (GetHasOids()) sqlopt += wxT("\n OIDS=TRUE"); else sqlopt += wxT("\n OIDS=FALSE"); } if(GetConnection()->BackendMinimumVersion(8, 4)) { if (GetCustomAutoVacuumEnabled()) { if (GetAutoVacuumEnabled() == 1) sqlopt += wxT(",\n autovacuum_enabled=true"); else if (GetCustomAutoVacuumEnabled() == 0) sqlopt += wxT(",\n autovacuum_enabled=false"); if (!GetAutoVacuumVacuumThreshold().IsEmpty()) { sqlopt += wxT(",\n autovacuum_vacuum_threshold=") + GetAutoVacuumVacuumThreshold(); } if (!GetAutoVacuumVacuumScaleFactor().IsEmpty()) { sqlopt += wxT(",\n autovacuum_vacuum_scale_factor=") + GetAutoVacuumVacuumScaleFactor(); } if (!GetAutoVacuumAnalyzeThreshold().IsEmpty()) { sqlopt += wxT(",\n autovacuum_analyze_threshold=") + GetAutoVacuumAnalyzeThreshold(); } if (!GetAutoVacuumAnalyzeScaleFactor().IsEmpty()) { sqlopt += wxT(",\n autovacuum_analyze_scale_factor=") + GetAutoVacuumAnalyzeScaleFactor(); } if (!GetAutoVacuumVacuumCostDelay().IsEmpty()) { sqlopt += wxT(",\n autovacuum_vacuum_cost_delay=") + GetAutoVacuumVacuumCostDelay(); } if (!GetAutoVacuumVacuumCostLimit().IsEmpty()) { sqlopt += wxT(",\n autovacuum_vacuum_cost_limit=") + GetAutoVacuumVacuumCostLimit(); } if (!GetAutoVacuumFreezeMinAge().IsEmpty()) { sqlopt += wxT(",\n autovacuum_freeze_min_age=") + GetAutoVacuumFreezeMinAge(); } if (!GetAutoVacuumFreezeMaxAge().IsEmpty()) { sqlopt += wxT(",\n autovacuum_freeze_max_age=") + GetAutoVacuumFreezeMaxAge(); } if (!GetAutoVacuumFreezeTableAge().IsEmpty()) { sqlopt += wxT(",\n autovacuum_freeze_table_age=") + GetAutoVacuumFreezeTableAge(); } } if (GetHasToastTable() && GetToastCustomAutoVacuumEnabled()) { if (GetToastAutoVacuumEnabled() == 1) sqlopt += wxT(",\n toast.autovacuum_enabled=true"); else if (GetToastAutoVacuumEnabled() == 0) sqlopt += wxT(",\n toast.autovacuum_enabled=false"); if (!GetToastAutoVacuumVacuumThreshold().IsEmpty()) { sqlopt += wxT(",\n toast.autovacuum_vacuum_threshold=") + GetToastAutoVacuumVacuumThreshold(); } if (!GetToastAutoVacuumVacuumScaleFactor().IsEmpty()) { sqlopt += wxT(",\n toast.autovacuum_vacuum_scale_factor=") + GetToastAutoVacuumVacuumScaleFactor(); } if (!GetToastAutoVacuumVacuumCostDelay().IsEmpty()) { sqlopt += wxT(",\n toast.autovacuum_vacuum_cost_delay=") + GetToastAutoVacuumVacuumCostDelay(); } if (!GetToastAutoVacuumVacuumCostLimit().IsEmpty()) { sqlopt += wxT(",\n toast.autovacuum_vacuum_cost_limit=") + GetToastAutoVacuumVacuumCostLimit(); } if (!GetToastAutoVacuumFreezeMinAge().IsEmpty()) { sqlopt += wxT(",\n toast.autovacuum_freeze_min_age=") + GetToastAutoVacuumFreezeMinAge(); } if (!GetToastAutoVacuumFreezeMaxAge().IsEmpty()) { sqlopt += wxT(",\n toast.autovacuum_freeze_max_age=") + GetToastAutoVacuumFreezeMaxAge(); } if (!GetToastAutoVacuumFreezeTableAge().IsEmpty()) { sqlopt += wxT(",\n toast.autovacuum_freeze_table_age=") + GetToastAutoVacuumFreezeTableAge(); } } } 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; while ( reg.Matches(relopt) ) { reg.GetMatch(&start, &len, 0); o=reg.GetMatch(relopt, 0); AppendIfFilled(sqlopt, ",\n ", o); relopt = relopt.Mid (start + len); } relopt=GetToastRelOptions(); while ( reg.Matches(relopt) ) { reg.GetMatch(&start, &len, 0); o="toast." + reg.GetMatch(relopt, 0); AppendIfFilled(sqlopt, ",\n ", o); relopt = relopt.Mid (start + len); } if (!sqlopt.IsEmpty()) { sql += wxT("\nWITH ("); if (sqlopt.Index(',')==0) sqlopt=sqlopt.Mid(1); sql += sqlopt; sql += wxT("\n)"); } } else { if (GetHasOids()) sql += wxT("\nWITH OIDS"); else sql += wxT("\nWITHOUT OIDS"); } if (GetConnection()->BackendMinimumVersion(8, 0) && tablespace != GetDatabase()->GetDefaultTablespace()) sql += wxT("\nTABLESPACE ") + qtIdent(tablespace); if (GetConnection()->GetIsGreenplum()) { // Add Greenplum DISTRIBUTED BY if (distributionIsRandom) { sql += wxT("\nDISTRIBUTED RANDOMLY"); } else if (GetDistributionColNumbers().Length() == 0) { // catalog table or other non-distributed table } else { // convert list of columns numbers to column names wxStringTokenizer collist(GetDistributionColNumbers(), wxT(",")); wxString cn; wxString distributionColumns; while (collist.HasMoreTokens()) { cn = collist.GetNextToken(); pgSet *set = ExecuteSet( wxT("SELECT attname\n") wxT(" FROM pg_attribute\n") wxT(" WHERE attrelid=") + GetOidStr() + wxT(" AND attnum IN (") + cn + wxT(")")); if (set) { if (!distributionColumns.IsNull()) { distributionColumns += wxT(", "); } distributionColumns += qtIdent(set->GetVal(0)); delete set; } } sql += wxT("\nDISTRIBUTED BY ("); sql += distributionColumns; sql += wxT(")"); } if (GetIsPartitioned()) if (GetConnection()->BackendMinimumVersion(8, 2, 9) && GetConnection()->GetIsGreenplum()) if (GetPartitionDef().Length() == 0) { wxString query = wxT("SELECT pg_get_partition_def("); query += GetOidStr(); query += wxT(", true) "); wxString partition_def = GetDatabase()->ExecuteScalar(query); iSetPartitionDef(partition_def); // pg_get_partition_def() doesn't work on partitions if (GetPartitionDef().Length() == 0) iSetPartitionDef(wxT("-- This partition has subpartitions")); } if (partitionDef.Length() > 0) sql += wxT("\n") + partitionDef + wxT("\n"); } sql += wxT(";\n") + GetOwnerSql(7, 3); if (GetConnection()->BackendMinimumVersion(17, 0)) sql += GetGrant(wxT("arwdDxtm")); else if (GetConnection()->BackendMinimumVersion(8, 4)) sql += GetGrant(wxT("arwdDxt")); else if (GetConnection()->BackendMinimumVersion(8, 2)) sql += GetGrant(wxT("arwdxt")); else sql += GetGrant(wxT("arwdRxt")); wxString st=GetStatExt(); if (!st.IsEmpty()) sql += st + wxT("\n"); sql += GetCommentSql(); if (GetConnection()->BackendMinimumVersion(9, 1)) sql += GetSeqLabelsSql(); // Column/constraint comments if (!colDetails.IsEmpty()) sql += colDetails + wxT("\n"); if (!conDetails.IsEmpty()) sql += conDetails + wxT("\n"); if (!columnPrivileges.IsEmpty()) { sql += columnPrivileges + wxT("\n"); } AppendStuff(sql, browser, indexFactory); AppendStuff(sql, browser, ruleFactory); AppendStuff(sql, browser, triggerFactory); /* * Disable adding partitions until that code works. * if (partitionDef.Length() > 0) { AppendStuffNoSql(sql, browser, partitionFactory); } */ sql+=cols_type; } return sql; } wxString pgTable::GetCoveringIndex(ctlTree *browser, const wxString &collist) { // delivers the name of the index which covers the named columns wxCookieType cookie; wxTreeItemId collItem = browser->GetFirstChild(GetId(), cookie); while (collItem) { pgObject *data = browser->GetObject(collItem); if (data && data->IsCollection() && (data->GetMetaType() == PGM_CONSTRAINT || data->GetMetaType() == PGM_INDEX)) { wxCookieType cookie2; wxTreeItemId item = browser->GetFirstChild(collItem, cookie2); while (item) { pgIndex *index = (pgIndex *)browser->GetObject(item); if (index && (index->GetMetaType() == PGM_INDEX || index->GetMetaType() == PGM_PRIMARYKEY || index->GetMetaType() == PGM_UNIQUE || index->GetMetaType() == PGM_EXCLUDE)) { index->ShowTreeDetail(browser); if (collist == index->GetColumns() || collist + wxT(",") == index->GetColumns().Left(collist.Length() + 1)) return index->GetName(); } item = browser->GetNextChild(collItem, cookie2); } } collItem = browser->GetNextChild(GetId(), cookie); } return wxEmptyString; } wxString pgTable::GetCols(ctlTree *browser, size_t indent, wxString &QMs, bool withQM) { wxString sql; wxString line; int colcount = 0; pgCollection *columns = browser->FindCollection(columnFactory, GetId()); if (columns) { columns->ShowTreeDetail(browser); treeObjectIterator colIt(browser, columns); pgColumn *column; while ((column = (pgColumn *)colIt.GetNextObject()) != 0) { column->ShowTreeDetail(browser); if (column->GetColNumber() > 0) { if (colcount++) { line += wxT(", "); QMs += wxT(", "); } if (line.Length() > 60) { if (!sql.IsEmpty()) { sql += wxT("\n") + wxString(' ', indent); } sql += line; line = wxEmptyString; QMs += wxT("\n") + wxString(' ', indent); } line += column->GetQuotedIdentifier(); if (withQM) line += wxT("=?"); QMs += wxT("?"); } } } if (!line.IsEmpty()) { if (!sql.IsEmpty()) sql += wxT("\n") + wxString(' ', indent); sql += line; } return sql; } pgCollection *pgTable::GetColumnCollection(ctlTree *browser) { pgCollection *columns = browser->FindCollection(columnFactory, GetId()); return columns; } pgCollection *pgTable::GetConstraintCollection(ctlTree *browser) { pgCollection *constraints = browser->FindCollection(constraintFactory, GetId()); return constraints; } wxString pgTable::GetSelectSql(ctlTree *browser) { wxString qms; wxString sql = wxT("SELECT ") + GetCols(browser, 7, qms, false) + wxT("\n") wxT(" FROM ") + GetQuotedFullIdentifier() + wxT(";\n"); return sql; } wxString pgTable::GetInsertSql(ctlTree *browser) { wxString qms; wxString sql = wxT("INSERT INTO ") + GetQuotedFullIdentifier() + wxT("(\n") wxT(" ") + GetCols(browser, 12, qms, false) + wxT(")\n") wxT(" VALUES (") + qms + wxT(");\n"); return sql; } wxString pgTable::GetUpdateSql(ctlTree *browser) { wxString qms; wxString sql = wxT("UPDATE ") + GetQuotedFullIdentifier() + wxT("\n") wxT(" SET ") + GetCols(browser, 7, qms, true) + wxT("\n") wxT(" WHERE ;\n"); return sql; } wxString pgTable::GetDeleteSql(ctlTree *browser) { wxString sql = wxT("DELETE FROM ") + GetQuotedFullIdentifier() + wxT("\n") wxT(" WHERE ;\n"); return sql; } bool pgTable::EnableTriggers(const bool b) { wxString sql = wxT("ALTER TABLE ") + GetQuotedFullIdentifier() + wxT(" "); if (!b) sql += wxT("DISABLE"); else sql += wxT("ENABLE"); sql += wxT(" TRIGGER ALL"); return GetDatabase()->ExecuteVoid(sql); } void pgTable::UpdateRows() { pgSet *props = ExecuteSet(wxT("SELECT count(*) AS rows FROM ONLY ") + GetQuotedFullIdentifier()); if (props) { rows = props->GetLongLong(0); delete props; rowsCounted = true; } } void pgTable::UpdateInheritance() { // not checked so far pgSet *props = ExecuteSet( wxT("SELECT c.oid, c.relname , nspname\n") wxT(" FROM pg_inherits i\n") wxT(" JOIN pg_class c ON c.oid = i.inhparent\n") wxT(" JOIN pg_namespace n ON n.oid=c.relnamespace\n") wxT(" WHERE i.inhrelid = ") + GetOidStr() + wxT("\n") wxT(" ORDER BY inhseqno")); if (props) { inheritedTableCount = 0; inheritedTables = wxT(""); while (!props->Eof()) { if (inheritedTableCount) { inheritedTables += wxT(", "); quotedInheritedTables += wxT(", "); } inheritedTables += props->GetVal(wxT("relname")); quotedInheritedTables += GetQuotedSchemaPrefix(props->GetVal(wxT("nspname"))) + qtIdent(props->GetVal(wxT("relname"))); quotedInheritedTablesList.Add(GetQuotedSchemaPrefix(props->GetVal(wxT("nspname"))) + qtIdent(props->GetVal(wxT("relname")))); inheritedTablesOidList.Add(props->GetVal(wxT("oid"))); props->MoveNext(); inheritedTableCount++; } delete props; } } void pgTable::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane) { if (!expandedKids) { expandedKids = true; browser->RemoveDummyChild(this); // Log wxLogInfo(wxT("Adding child object to table %s"), GetIdentifier().c_str()); browser->AppendCollection(this, columnFactory); browser->AppendCollection(this, constraintFactory); browser->AppendCollection(this, indexFactory); browser->AppendCollection(this, ruleFactory); browser->AppendCollection(this, triggerFactory); if (GetConnection() != 0 && GetConnection()->GetIsGreenplum() && GetIsPartitioned()) browser->AppendCollection(this, partitionFactory); if (GetConnection() != 0 && (!GetPartKeyDef().IsEmpty()||GetIsPartitioned())) browser->AppendCollection(this, pg_partitionFactory); // convert list of columns numbers to column names wxStringTokenizer collist(GetPrimaryKeyColNumbers(), wxT(",")); wxString cn; while (collist.HasMoreTokens()) { cn = collist.GetNextToken(); pgSet *set = ExecuteSet( wxT("SELECT attname\n") wxT(" FROM pg_attribute\n") wxT(" WHERE attrelid=") + GetOidStr() + wxT(" AND attnum IN (") + cn + wxT(")")); if (set) { if (!primaryKey.IsNull()) { quotedPrimaryKey += wxT(", "); primaryKey += wxT(", "); } primaryKey += set->GetVal(0); quotedPrimaryKey += qtIdent(set->GetVal(0)); delete set; } } if (settings->GetAutoRowCountThreshold() >= GetEstimatedRows()) UpdateRows(); UpdateInheritance(); } if (properties) { CreateListColumns(properties); properties->AppendItem(_("Name"), GetName()); if (GetConnection() != 0 && GetConnection()->GetIsGreenplum()) { gpPartition *p = dynamic_cast(this); if (p != 0) properties->AppendItem(_("Partition Name"), p->GetPartitionName()); } properties->AppendItem(_("OID"), GetOid()); properties->AppendItem(_("Owner"), GetOwner()); if (GetConnection()->BackendMinimumVersion(8, 0)) properties->AppendItem(_("Tablespace"), tablespace); properties->AppendItem(_("ACL"), GetAcl()); if (GetConnection()->BackendMinimumVersion(9, 0)) properties->AppendItem(_("Of type"), ofType); if (GetPrimaryKey().IsNull()) properties->AppendItem(_("Primary key"), _("")); else properties->AppendItem(_("Primary key"), GetPrimaryKey()); properties->AppendItem(_("Rows (estimated)"), GetEstimatedRows()); if (GetConnection()->BackendMinimumVersion(8, 2)) properties->AppendItem(_("Fill factor"), GetFillFactor()); if (rowsCounted) properties->AppendItem(_("Rows (counted)"), rows); else properties->AppendItem(_("Rows (counted)"), _("not counted")); long setId; wxString clusterName; long repStat = GetReplicationStatus(browser, &clusterName, &setId); wxString clusterInfo; clusterInfo.Printf(_("Cluster \"%s\", set %ld"), clusterName.c_str(), setId); wxString repString; switch (repStat) { case REPLICATIONSTATUS_SUBSCRIBED: repString = _("Published"); break; case REPLICATIONSTATUS_REPLICATED: repString = _("Replicated"); break; case REPLICATIONSTATUS_MULTIPLY_PUBLISHED: repString = _("Replicated"); clusterName = _("Multiple clusters"); break; default: break; } if (!repString.IsEmpty()) properties->AppendItem(repString, clusterInfo); properties->AppendYesNoItem(_("Inherits tables"), GetHasSubclass()); properties->AppendItem(_("Inherited tables count"), GetInheritedTableCount()); if (GetInheritedTableCount()) properties->AppendItem(_("Inherited tables"), GetInheritedTables()); if (GetConnection()->BackendMinimumVersion(9, 1)) properties->AppendYesNoItem(_("Unlogged?"), GetUnlogged()); if (!GetConnection()->BackendMinimumVersion(12, 0)) properties->AppendYesNoItem(_("Has OIDs?"), GetHasOids()); if (!GetConnection()->BackendMinimumVersion(12, 0)) properties->AppendYesNoItem(_("System table?"), GetSystemObject()); if (GetConnection()->GetIsPgProEnt() && GetRatio().Len()>0) properties->AppendItem(_("CFS fragmentation"), GetRatio()); if (GetConnection()->HasFeature(FEATURE_TRACK_COMMIT_TS) && GetCreateTableTS().Len()>0) properties->AppendItem(_("Create table timestamp"), GetCreateTableTS()); /* Custom AutoVacuum Settings */ if (GetConnection()->BackendMinimumVersion(8, 4) && GetCustomAutoVacuumEnabled()) { if (GetAutoVacuumEnabled() != 2) { properties->AppendItem(_("Table auto-vacuum enabled?"), GetAutoVacuumEnabled() == 1 ? _("Yes") : _("No")); } if (!GetAutoVacuumVacuumThreshold().IsEmpty()) properties->AppendItem(_("Table auto-vacuum VACUUM base threshold"), GetAutoVacuumVacuumThreshold()); if (!GetAutoVacuumVacuumScaleFactor().IsEmpty()) properties->AppendItem(_("Table auto-vacuum VACUUM scale factor"), GetAutoVacuumVacuumScaleFactor()); if (!GetAutoVacuumAnalyzeThreshold().IsEmpty()) properties->AppendItem(_("Table auto-vacuum ANALYZE base threshold"), GetAutoVacuumAnalyzeThreshold()); if (!GetAutoVacuumAnalyzeScaleFactor().IsEmpty()) properties->AppendItem(_("Table auto-vacuum ANALYZE scale factor"), GetAutoVacuumAnalyzeScaleFactor()); if (!GetAutoVacuumVacuumCostDelay().IsEmpty()) properties->AppendItem(_("Table auto-vacuum VACUUM cost delay"), GetAutoVacuumVacuumCostDelay()); if (!GetAutoVacuumVacuumCostLimit().IsEmpty()) properties->AppendItem(_("Table auto-vacuum VACUUM cost limit"), GetAutoVacuumVacuumCostLimit()); if (!GetAutoVacuumFreezeMinAge().IsEmpty()) properties->AppendItem(_("Table auto-vacuum FREEZE minimum age"), GetAutoVacuumFreezeMinAge()); if (!GetAutoVacuumFreezeMaxAge().IsEmpty()) properties->AppendItem(_("Table auto-vacuum FREEZE maximum age"), GetAutoVacuumFreezeMaxAge()); if (!GetAutoVacuumFreezeTableAge().IsEmpty()) properties->AppendItem(_("Table auto-vacuum FREEZE table age"), GetAutoVacuumFreezeTableAge()); } /* Custom TOAST-TABLE AutoVacuum Settings */ if (GetConnection()->BackendMinimumVersion(8, 4) && GetHasToastTable() && GetToastCustomAutoVacuumEnabled()) { if (GetToastAutoVacuumEnabled() != 2) { properties->AppendItem(_("Toast auto-vacuum enabled?"), GetToastAutoVacuumEnabled() == 1 ? _("Yes") : _("No")); } if (!GetToastAutoVacuumVacuumThreshold().IsEmpty()) properties->AppendItem(_("Toast auto-vacuum VACUUM base threshold"), GetToastAutoVacuumVacuumThreshold()); if (!GetToastAutoVacuumVacuumScaleFactor().IsEmpty()) properties->AppendItem(_("Toast auto-vacuum VACUUM scale factor"), GetToastAutoVacuumVacuumScaleFactor()); if (!GetToastAutoVacuumVacuumCostDelay().IsEmpty()) properties->AppendItem(_("Toast auto-vacuum VACUUM cost delay"), GetToastAutoVacuumVacuumCostDelay()); if (!GetToastAutoVacuumVacuumCostLimit().IsEmpty()) properties->AppendItem(_("Toast auto-vacuum VACUUM cost limit"), GetToastAutoVacuumVacuumCostLimit()); if (!GetToastAutoVacuumFreezeMinAge().IsEmpty()) properties->AppendItem(_("Toast auto-vacuum FREEZE minimum age"), GetToastAutoVacuumFreezeMinAge()); if (!GetToastAutoVacuumFreezeMaxAge().IsEmpty()) properties->AppendItem(_("Toast auto-vacuum FREEZE maximum age"), GetToastAutoVacuumFreezeMaxAge()); if (!GetToastAutoVacuumFreezeTableAge().IsEmpty()) properties->AppendItem(_("Toast auto-vacuum FREEZE table age"), GetToastAutoVacuumFreezeTableAge()); } properties->AppendItem(_("Comment"), firstLineOnly(GetComment())); if (!GetLabels().IsEmpty()) { wxArrayString seclabels = GetProviderLabelArray(); if (seclabels.GetCount() > 0) { for (unsigned int index = 0 ; index < seclabels.GetCount() - 1 ; index += 2) { properties->AppendItem(seclabels.Item(index), seclabels.Item(index + 1)); } } } } if (form && GetVacuumHint() && !hintShown) { ShowHint(form, false); } } bool pgTable::GetCanHint() { return (GetVacuumHint() || primaryKey.IsEmpty()); } bool pgTable::GetVacuumHint() { bool canHint = false; if (rowsCounted) { if (!estimatedRows || (estimatedRows == 1000 && rows.GetValue() != 1000)) canHint = (rows >= 20); else { double rowsDbl = (wxLongLong_t)rows.GetValue(); double quot = rowsDbl * 10. / estimatedRows; canHint = ((quot > 12 || quot < 8) && (rowsDbl < estimatedRows - 20. || rowsDbl > estimatedRows + 20.)); } } else if (estimatedRows == 1000) { canHint = true; } return canHint; } void pgTable::ShowHint(frmMain *form, bool force) { hintShown = true; int rc; if (force) { wxArrayString hints; if (GetVacuumHint()) hints.Add(HINT_VACUUM); if (primaryKey.IsEmpty()) hints.Add(HINT_PRIMARYKEY); rc = frmHint::ShowHint(form, hints, GetFullIdentifier(), force); } else rc = frmHint::ShowHint(form, HINT_VACUUM, GetFullIdentifier(), force); if (rc == HINT_RC_FIX) { frmMaintenance *frm = new frmMaintenance(form, this); frm->Go(); } } pgObject *pgTable::Refresh(ctlTree *browser, const wxTreeItemId item) { pgTable *table = 0; pgCollection *coll = browser->GetParentCollection(item); if (coll) table = (pgTable *)tableFactory.CreateObjects(coll, 0, wxT("\n AND rel.oid=") + GetOidStr()); return table; } bool pgTable::HasPgstattuple() { return GetConnection()->HasFeature(FEATURE_PGSTATTUPLE); } void pgTable::iSetTriggersEnabled(ctlTree *browser, bool enable) { pgCollection *triggers = browser->FindCollection(triggerFactory, GetId()); if (triggers) { triggers->ShowTreeDetail(browser); treeObjectIterator trgIt(browser, triggers); pgTrigger *trigger; while ((trigger = (pgTrigger *)trgIt.GetNextObject()) != 0) { trigger->SetEnabled(browser, enable); } } } /////////////////////////////////////////////////////////// pgTableCollection::pgTableCollection(pgaFactory *factory, pgSchema *sch) : pgSchemaObjCollection(factory, sch) { } wxString pgTableCollection::GetTranslatedMessage(int kindOfMessage) const { wxString message = wxEmptyString; switch (kindOfMessage) { case RETRIEVINGDETAILS: message = _("Retrieving details on tables"); break; case REFRESHINGDETAILS: message = _("Refreshing tables"); break; case GRANTWIZARDTITLE: message = _("Privileges for tables"); break; case STATISTICSREPORT: message = _("Tables statistics report"); break; case OBJSTATISTICS: message = _("Tables statistics"); break; case OBJECTSLISTREPORT: message = _("Tables list report"); break; } return message; } 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); // Add the statistics view columns statistics->ClearAll(); statistics->AddColumn(_("Table Name")); if (hasSize) statistics->AddColumn(_("Size"), 50); if (GetConnection()->GetIsPgProEnt()) statistics->AddColumn(_("CFS %")); statistics->AddColumn(_("Live tuples")); statistics->AddColumn(_("Tuples inserted")); statistics->AddColumn(_("Tuples updated")); statistics->AddColumn(_("Tuples deleted")); if (GetConnection()->BackendMinimumVersion(8, 3)) { statistics->AddColumn(_("Tuples HOT updated")); statistics->AddColumn(_("Dead tuples")); } if (GetConnection()->BackendMinimumVersion(8, 2)) { statistics->AddColumn(_("Last vacuum")); statistics->AddColumn(_("Last autovacuum")); statistics->AddColumn(_("Last analyze")); statistics->AddColumn(_("Last autoanalyze")); } if (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 (GetConnection()->BackendMinimumVersion(8, 3)) sql += wxT(", n_tup_hot_upd, n_live_tup, n_dead_tup"); if (GetConnection()->BackendMinimumVersion(8, 2)) sql += wxT(", last_vacuum, last_autovacuum, last_analyze, last_autoanalyze"); if (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 (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(" WHERE schemaname = ") + qtDbString(GetSchema()->GetName()) + wxT("\n ORDER BY relname"); pgSet *stats = 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 (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"))); statistics->SetItem(pos, i++, stats->GetVal(wxT("n_live_tup"))); statistics->SetItem(pos, i++, stats->GetVal(wxT("n_dead_tup"))); } if (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 (stats->GetVal(wxT("last_analyze")).IsEmpty()&& stats->GetVal(wxT("last_autoanalyze")).IsEmpty()) statistics->SetItemBackgroundColour(pos, wxColour(wxT("#FF8028"))); } if (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); } /////////////////////////////////////////////////////////// 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")) + wxT(", idx_scan AS ") + qtIdent(_("Index Scans")) + wxT(", idx_tup_fetch AS ") + qtIdent(_("Index Tuples Fetched")) + wxT(", n_tup_ins AS ") + qtIdent(_("Tuples Inserted")) + wxT(", n_tup_upd AS ") + qtIdent(_("Tuples Updated")) + wxT(", n_tup_del AS ") + qtIdent(_("Tuples Deleted")); if (GetConnection()->BackendMinimumVersion(8, 3)) { sql += wxT(", n_tup_hot_upd AS ") + qtIdent(_("Tuples HOT Updated")) + wxT(", n_live_tup AS ") + qtIdent(_("Live Tuples")) + wxT(", n_dead_tup AS ") + qtIdent(_("Dead Tuples")); } sql += wxT(", heap_blks_read AS ") + qtIdent(_("Heap Blocks Read")) + wxT(", heap_blks_hit AS ") + qtIdent(_("Heap Blocks Hit")) + wxT(", idx_blks_read AS ") + qtIdent(_("Index Blocks Read")) + wxT(", idx_blks_hit AS ") + qtIdent(_("Index Blocks Hit")) + wxT(", toast_blks_read AS ") + qtIdent(_("Toast Blocks Read")) + wxT(", toast_blks_hit AS ") + qtIdent(_("Toast Blocks Hit")) + wxT(", tidx_blks_read AS ") + qtIdent(_("Toast Index Blocks Read")) + wxT(", tidx_blks_hit AS ") + qtIdent(_("Toast Index Blocks Hit")); if (GetConnection()->BackendMinimumVersion(8, 2)) { sql += wxT(", last_vacuum AS ") + qtIdent(_("Last Vacuum")) + wxT(", last_autovacuum AS ") + qtIdent(_("Last Autovacuum")) + wxT(", last_analyze AS ") + qtIdent(_("Last Analyze")) + wxT(", last_autoanalyze AS ") + qtIdent(_("Last Autoanalyze")); } if (GetConnection()->BackendMinimumVersion(9, 1)) { sql += wxT(", vacuum_count AS ") + qtIdent(_("Vacuum counter")) + wxT(", autovacuum_count AS ") + qtIdent(_("Autovacuum counter")) + wxT(", analyze_count AS ") + qtIdent(_("Analyze counter")) + wxT(", autoanalyze_count AS ") + qtIdent(_("Autoanalyze counter")); } if (GetConnection()->HasFeature(FEATURE_SIZE)) { sql += wxT(", pg_size_pretty(pg_relation_size(stat.relid)) AS ") + qtIdent(_("Table Size")) + wxT(", CASE WHEN cl.reltoastrelid = 0 THEN ") + qtDbString(_("none")) + wxT(" ELSE pg_size_pretty(pg_relation_size(cl.reltoastrelid)+ COALESCE((SELECT SUM(pg_relation_size(indexrelid)) FROM pg_index WHERE indrelid=cl.reltoastrelid)::int8, 0)) END AS ") + qtIdent(_("Toast Table Size")) + wxT(", pg_size_pretty(COALESCE((SELECT SUM(pg_relation_size(indexrelid)) FROM pg_index WHERE indrelid=stat.relid)::int8, 0)) AS ") + qtIdent(_("Indexes Size")); } if (showExtendedStatistics) { sql += wxT("\n") wxT(", tuple_count AS ") + qtIdent(_("Tuple Count")) + wxT(",\n") wxT(" pg_size_pretty(tuple_len) AS ") + qtIdent(_("Tuple Length")) + wxT(",\n") wxT(" tuple_percent AS ") + qtIdent(_("Tuple Percent")) + wxT(",\n") wxT(" dead_tuple_count AS ") + qtIdent(_("Dead Tuple Count")) + wxT(",\n") wxT(" pg_size_pretty(dead_tuple_len) AS ") + qtIdent(_("Dead Tuple Length")) + wxT(",\n") wxT(" dead_tuple_percent AS ") + qtIdent(_("Dead Tuple Percent")) + wxT(",\n") wxT(" pg_size_pretty(free_space) AS ") + qtIdent(_("Free Space")) + wxT(",\n") wxT(" free_percent AS ") + qtIdent(_("Free Percent")) + wxT("\n") wxT(" FROM pgstattuple('") + GetQuotedFullIdentifier() + wxT("'), pg_stat_all_tables stat"); } else { sql += wxT("\n") wxT(" FROM pg_stat_all_tables stat"); } sql += wxT("\n") wxT(" JOIN pg_statio_all_tables statio ON stat.relid = statio.relid\n") wxT(" JOIN pg_class cl ON cl.oid=stat.relid\n") wxT(" WHERE stat.relid = ") + GetOidStr(); DisplayStatistics(statistics, sql); } pgObject *pgTableFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction) { wxString query; pgTable *table = 0; // Greenplum returns reltuples and relpages as tuples per segmentDB and pages per segmentDB, // so we need to multiply them by the number of segmentDBs to get reasonable values. long gp_segments = 1; if (collection->GetConnection()->GetIsGreenplum()) { query = wxT("SELECT count(*) AS gp_segments from pg_catalog.gp_configuration where definedprimary = 't' and content >= 0"); gp_segments = StrToLong(collection->GetDatabase()->ExecuteScalar(query)); if (gp_segments <= 1) gp_segments = 1; } pgSet *tables; if (collection->GetConnection()->BackendMinimumVersion(8, 0)) { query = wxT("SELECT rel.oid, rel.relname, rel.reltablespace AS spcoid, spc.spcname, pg_get_userbyid(rel.relowner) AS relowner, rel.relacl, ") wxT("rel.relhassubclass, rel.reltuples, des.description, con.conname, con.conkey,\n") wxT(" EXISTS(select 1 FROM pg_trigger\n") wxT(" JOIN pg_proc pt ON pt.oid=tgfoid AND pt.proname='logtrigger'\n") wxT(" JOIN pg_proc pc ON pc.pronamespace=pt.pronamespace AND pc.proname='slonyversion'\n") wxT(" WHERE tgrelid=rel.oid) AS isrepl,\n"); if (collection->GetConnection()->BackendMinimumVersion(9, 0)) { query += wxT(" (select count(*) FROM pg_trigger\n") wxT(" WHERE tgrelid=rel.oid AND tgisinternal = FALSE) AS triggercount\n"); } else { query += wxT(" (select count(*) FROM pg_trigger\n") wxT(" WHERE tgrelid=rel.oid AND tgisconstraint = FALSE) AS triggercount\n"); } if (collection->GetConnection()->BackendMinimumVersion(9, 1)) query += wxT(", rel.relpersistence \n"); if (collection->GetConnection()->BackendMinimumVersion(8, 2)) query += wxT(", substring(array_to_string(rel.reloptions, ',') FROM 'fillfactor=([0-9]*)') AS fillfactor \n"); if (collection->GetConnection()->GetIsGreenplum()) { query += wxT(", gpd.localoid, gpd.attrnums \n"); query += wxT(", substring(array_to_string(rel.reloptions, ',') from 'appendonly=([a-z]*)') AS appendonly \n"); query += wxT(", substring(array_to_string(rel.reloptions, ',') from 'compresslevel=([0-9]*)') AS compresslevel \n"); query += wxT(", substring(array_to_string(rel.reloptions, ',') from 'orientation=([a-z]*)') AS orientation \n"); query += wxT(", substring(array_to_string(rel.reloptions, ',') from 'compresstype=([a-z0-9]*)') AS compresstype \n"); query += wxT(", substring(array_to_string(rel.reloptions, ',') from 'blocksize=([0-9]*)') AS blocksize \n"); query += wxT(", substring(array_to_string(rel.reloptions, ',') from 'checksum=([a-z]*)') AS checksum \n"); if (collection->GetConnection()->GetIsGreenplum() && collection->GetConnection()->BackendMinimumVersion(8, 2, 9)) query += wxT(", rel.oid in (select parrelid from pg_partition) as ispartitioned\n"); } else if (collection->GetConnection()->BackendMinimumVersion(8, 4)) { query += wxT(", substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') AS autovacuum_enabled \n") wxT(", substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS autovacuum_vacuum_threshold \n") wxT(", substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_vacuum_scale_factor \n") wxT(", substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS autovacuum_analyze_threshold \n") wxT(", substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_analyze_scale_factor \n") wxT(", substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS autovacuum_vacuum_cost_delay \n") wxT(", substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS autovacuum_vacuum_cost_limit \n") wxT(", substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS autovacuum_freeze_min_age \n") wxT(", substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS autovacuum_freeze_max_age \n") wxT(", substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS autovacuum_freeze_table_age \n") wxT(", substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') AS toast_autovacuum_enabled \n") wxT(", substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS toast_autovacuum_vacuum_threshold \n") wxT(", substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_vacuum_scale_factor \n") wxT(", substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS toast_autovacuum_analyze_threshold \n") wxT(", substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_analyze_scale_factor \n") wxT(", substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS toast_autovacuum_vacuum_cost_delay \n") wxT(", substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS toast_autovacuum_vacuum_cost_limit \n") wxT(", substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age \n") wxT(", substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age \n") wxT(", substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age \n") wxT(", rel.reloptions AS reloptions, tst.reloptions AS toast_reloptions \n") wxT(", (CASE WHEN rel.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable\n"); } if (collection->GetConnection()->BackendMinimumVersion(9, 0)) query += wxT(", rel.reloftype, typ.typname\n"); if (collection->GetConnection()->HasFeature(FEATURE_TRACK_COMMIT_TS)) query += wxT(", pg_xact_commit_timestamp(typ2.xmin) create_ts\n"); if (collection->GetDatabase()->BackendMinimumVersion(9, 1)) { query += wxT(",\n(SELECT array_agg(label) FROM pg_seclabels sl1 WHERE sl1.objoid=rel.oid AND sl1.objsubid=0) AS labels"); query += wxT(",\n(SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=rel.oid AND sl2.objsubid=0) AS providers"); } wxString pg10=wxEmptyString; if (collection->GetDatabase()->BackendMinimumVersion(10, 0)) { query += wxT(",case when lk.relation=rel.oid then null else pg_get_partkeydef(rel.oid) end \n AS partkeydef"); query += wxT(",case when lk.relation=rel.oid then null 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"); //pg10=wxT("pg_get_expr(rel.relpartbound, rel.oid) is null and"); pg10=wxT("(rel.relpartbound is null and i.inhrelid is null) and"); //query += wxT(",\n(SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=rel.oid AND sl2.objsubid=0) AS providers"); } // 'ALTER STATISTICS '||substring('CREATE STATISTICS public.tab_a (dependencies) ON c1, c3 FROM a' from 'ICS (.+?)\s\(')||' OWNER TO '|| //select relation from pg_locks where locktype='relation' and granted=true and mode='AccessExclusiveLock' if (collection->GetDatabase()->connection()->GetIsPgProEnt()) query += wxT(",left((cfs_fragmentation(rel.oid)*100)::text,5)::text AS cfs_ratio"); else query += wxT(",null::text AS cfs_ratio"); 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(" LEFT JOIN pg_locks lk ON locktype='relation' and granted=true and mode='AccessExclusiveLock' and relation=rel.oid\n") wxT(" LEFT OUTER JOIN pg_tablespace spc on spc.oid=rel.reltablespace\n") wxT(" LEFT OUTER JOIN pg_description des ON (des.objoid=rel.oid AND des.objsubid=0 AND des.classoid='pg_class'::regclass)\n") wxT(" LEFT OUTER JOIN pg_inherits i on i.inhrelid=rel.oid\n") wxT(" LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'\n"); // Add the toast table for vacuum parameters. if (collection->GetConnection()->BackendMinimumVersion(8, 4)) query += wxT(" LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid\n"); if (collection->GetConnection()->GetIsGreenplum()) query += wxT(" LEFT OUTER JOIN gp_distribution_policy gpd ON gpd.localoid=rel.oid\n"); if (collection->GetConnection()->BackendMinimumVersion(9, 0)) query += wxT("LEFT JOIN pg_type typ ON rel.reloftype=typ.oid\n"); if (collection->GetConnection()->HasFeature(FEATURE_TRACK_COMMIT_TS)) query += wxT("LEFT JOIN pg_type typ2 ON rel.oid=typ2.typrelid\n"); query += wxT(" WHERE ")+pg10+wxT(" rel.relkind IN ('r','s','t','p') AND rel.relnamespace = ") + collection->GetSchema()->GetOidStr() + wxT("\n"); // Greenplum: Eliminate (sub)partitions from the display, only show the parent partitioned table // and eliminate external tables if (collection->GetConnection()->GetIsGreenplum() && collection->GetConnection()->BackendMinimumVersion(8, 2, 9)) query += wxT("AND rel.relstorage <> 'x' AND rel.oid NOT IN (SELECT parchildrelid from pg_partition_rule)"); // only show the parent partitioned table if (collection->GetConnection()->BackendMinimumVersion(10, 0)){ //addIcon(tables_png_img); query += wxT("AND (not (rel.relkind='r' and rel.relpartbound IS NOT NULL)) "); } query += restriction + wxT(" ORDER BY rel.relname"); } else { query = wxT("SELECT rel.oid, rel.relname, pg_get_userbyid(rel.relowner) AS relowner, rel.relacl, ") wxT("rel.relhassubclass, rel.reltuples, des.description, con.conname, con.conkey,\n") wxT(" (select count(*) FROM pg_trigger\n") wxT(" WHERE tgrelid=rel.oid AND tgisconstraint = FALSE) AS triggercount,\n") wxT(" EXISTS(select 1 FROM pg_trigger\n") wxT(" JOIN pg_proc pt ON pt.oid=tgfoid AND pt.proname='logtrigger'\n") wxT(" JOIN pg_proc pc ON pc.pronamespace=pt.pronamespace AND pc.proname='slonyversion'\n") wxT(" WHERE tgrelid=rel.oid) AS isrepl\n") wxT(" FROM pg_class rel\n") wxT(" LEFT OUTER JOIN pg_description des ON (des.objoid=rel.oid AND des.objsubid=0 AND des.classoid='pg_class'::regclass)\n") wxT(" LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'\n") wxT(" WHERE rel.relkind IN ('r','s','t') AND rel.relnamespace = ") + collection->GetSchema()->GetOidStr() + wxT("\n") + restriction + wxT(" ORDER BY rel.relname"); } tables = collection->GetDatabase()->ExecuteSet(query); if (tables) { while (!tables->Eof()) { table = new pgTable(collection->GetSchema(), tables->GetVal(wxT("relname"))); table->iSetOid(tables->GetOid(wxT("oid"))); table->iSetOwner(tables->GetVal(wxT("relowner"))); table->iSetAcl(tables->GetVal(wxT("relacl"))); table->iSetRatio(tables->GetVal(wxT("cfs_ratio"))); if (collection->GetConnection()->HasFeature(FEATURE_TRACK_COMMIT_TS)) { table->iSetCreateTableTS(tables->GetVal(wxT("create_ts"))); } if (collection->GetConnection()->BackendMinimumVersion(8, 0)) { if (tables->GetOid(wxT("spcoid")) == 0) table->iSetTablespaceOid(collection->GetDatabase()->GetTablespaceOid()); else table->iSetTablespaceOid(tables->GetOid(wxT("spcoid"))); if (tables->GetVal(wxT("spcname")) == wxEmptyString) table->iSetTablespace(collection->GetDatabase()->GetTablespace()); else table->iSetTablespace(tables->GetVal(wxT("spcname"))); } if (collection->GetConnection()->BackendMinimumVersion(9, 0)) { table->iSetOfTypeOid(tables->GetOid(wxT("reloftype"))); table->iSetOfType(tables->GetVal(wxT("typname"))); } else { table->iSetOfTypeOid(0); table->iSetOfType(wxT("")); } table->iSetComment(tables->GetVal(wxT("description"))); if (collection->GetConnection()->BackendMinimumVersion(9, 1)) table->iSetUnlogged(tables->GetVal(wxT("relpersistence")) == wxT("u")); else table->iSetUnlogged(false); table->iSetHasOids(false); table->iSetEstimatedRows(tables->GetDouble(wxT("reltuples")) * gp_segments); if (collection->GetConnection()->BackendMinimumVersion(8, 2)) { table->iSetFillFactor(tables->GetVal(wxT("fillfactor"))); } if (collection->GetConnection()->BackendMinimumVersion(8, 4)) { table->iSetRelOptions(tables->GetVal(wxT("reloptions"))); if (table->GetCustomAutoVacuumEnabled()) { if (tables->GetVal(wxT("autovacuum_enabled")).IsEmpty()) table->iSetAutoVacuumEnabled(2); else if (tables->GetBool(wxT("autovacuum_enabled"))) table->iSetAutoVacuumEnabled(1); else table->iSetAutoVacuumEnabled(0); table->iSetAutoVacuumVacuumThreshold(tables->GetVal(wxT("autovacuum_vacuum_threshold"))); table->iSetAutoVacuumVacuumScaleFactor(tables->GetVal(wxT("autovacuum_vacuum_scale_factor"))); table->iSetAutoVacuumAnalyzeThreshold(tables->GetVal(wxT("autovacuum_analyze_threshold"))); table->iSetAutoVacuumAnalyzeScaleFactor(tables->GetVal(wxT("autovacuum_analyze_scale_factor"))); table->iSetAutoVacuumVacuumCostDelay(tables->GetVal(wxT("autovacuum_vacuum_cost_delay"))); table->iSetAutoVacuumVacuumCostLimit(tables->GetVal(wxT("autovacuum_vacuum_cost_limit"))); table->iSetAutoVacuumFreezeMinAge(tables->GetVal(wxT("autovacuum_freeze_min_age"))); table->iSetAutoVacuumFreezeMaxAge(tables->GetVal(wxT("autovacuum_freeze_max_age"))); table->iSetAutoVacuumFreezeTableAge(tables->GetVal(wxT("autovacuum_freeze_table_age"))); } table->iSetHasToastTable(tables->GetBool(wxT("hastoasttable"))); if (table->GetHasToastTable()) { table->iSetToastRelOptions(tables->GetVal(wxT("toast_reloptions"))); if (table->GetToastCustomAutoVacuumEnabled()) { if (tables->GetVal(wxT("toast_autovacuum_enabled")).IsEmpty()) table->iSetToastAutoVacuumEnabled(2); else if (tables->GetBool(wxT("toast_autovacuum_enabled"))) table->iSetToastAutoVacuumEnabled(1); else table->iSetToastAutoVacuumEnabled(0); table->iSetToastAutoVacuumVacuumThreshold(tables->GetVal(wxT("toast_autovacuum_vacuum_threshold"))); table->iSetToastAutoVacuumVacuumScaleFactor(tables->GetVal(wxT("toast_autovacuum_vacuum_scale_factor"))); table->iSetToastAutoVacuumVacuumCostDelay(tables->GetVal(wxT("toast_autovacuum_vacuum_cost_delay"))); table->iSetToastAutoVacuumVacuumCostLimit(tables->GetVal(wxT("toast_autovacuum_vacuum_cost_limit"))); table->iSetToastAutoVacuumFreezeMinAge(tables->GetVal(wxT("toast_autovacuum_freeze_min_age"))); table->iSetToastAutoVacuumFreezeMaxAge(tables->GetVal(wxT("toast_autovacuum_freeze_max_age"))); table->iSetToastAutoVacuumFreezeTableAge(tables->GetVal(wxT("toast_autovacuum_freeze_table_age"))); } } } table->iSetHasSubclass(tables->GetBool(wxT("relhassubclass"))); table->iSetPrimaryKeyName(tables->GetVal(wxT("conname"))); table->iSetIsReplicated(tables->GetBool(wxT("isrepl"))); table->iSetTriggerCount(tables->GetLong(wxT("triggercount"))); wxString cn = tables->GetVal(wxT("conkey")); cn = cn.Mid(1, cn.Length() - 2); table->iSetPrimaryKeyColNumbers(cn); table->iSetPartitionDef(wxT("")); table->iSetIsPartitioned(false); if (collection->GetConnection()->BackendMinimumVersion(10, 0)) { 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()) { Oid lo = tables->GetOid(wxT("localoid")); wxString db = tables->GetVal(wxT("attrnums")); db = db.Mid(1, db.Length() - 2); table->iSetDistributionColNumbers(db); if (lo > 0 && db.Length() == 0) table->iSetDistributionIsRandom(); table->iSetAppendOnly(tables->GetVal(wxT("appendonly"))); table->iSetCompressLevel(tables->GetVal(wxT("compresslevel"))); table->iSetOrientation(tables->GetVal(wxT("orientation"))); table->iSetCompressType(tables->GetVal(wxT("compresstype"))); table->iSetBlocksize(tables->GetVal(wxT("blocksize"))); table->iSetChecksum(tables->GetVal(wxT("checksum"))); if (collection->GetConnection()->BackendMinimumVersion(8, 2, 9)) { table->iSetIsPartitioned(tables->GetBool(wxT("ispartitioned"))); } } if (collection->GetConnection()->BackendMinimumVersion(9, 1)) { table->iSetProviders(tables->GetVal(wxT("providers"))); table->iSetLabels(tables->GetVal(wxT("labels"))); } if (browser) { browser->AppendObject(collection, table); tables->MoveNext(); } else break; } delete tables; } return table; } bool pgTableObjCollection::CanCreate() { // We don't create sub-objects of Views or External tables if (GetTable()->GetMetaType() == PGM_VIEW || GetTable()->GetMetaType() == GP_EXTTABLE) return false; return GetSchema()->GetCreatePrivilege(); } #include "images/table.pngc" #include "images/table-repl.pngc" #include "images/table-repl-sm.pngc" #include "images/table-sm.pngc" #include "images/tables.pngc" pgTableFactory::pgTableFactory() : pgSchemaObjFactory(__("Table"), __("New Table..."), __("Create a new Table."), table_png_img, table_sm_png_img) { metaType = PGM_TABLE; if (WantSmallIcon()) replicatedIconId = addIcon(table_repl_sm_png_img); else replicatedIconId = addIcon(table_repl_png_img); partitionsIconId=addIcon(tables_png_img); } pgCollection *pgTableFactory::CreateCollection(pgObject *obj) { return new pgTableCollection(GetCollectionFactory(), (pgSchema *)obj); } pgTableFactory tableFactory; static pgaCollectionFactory cf(&tableFactory, __("Tables"), tables_png_img); pgCollection *pgTableObjFactory::CreateCollection(pgObject *obj) { return new pgTableObjCollection(GetCollectionFactory(), (pgTable *)obj); } countRowsFactory::countRowsFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list) { mnu->Append(id, _("&Count"), _("Count rows in the selected object.")); } wxWindow *countRowsFactory::StartDialog(frmMain *form, pgObject *obj) { form->StartMsg(_("Counting rows")); ((pgTable *)obj)->UpdateRows(); wxTreeItemId item = form->GetBrowser()->GetSelection(); if (obj == form->GetBrowser()->GetObject(item)) obj->ShowTreeDetail(form->GetBrowser(), 0, form->GetProperties()); form->EndMsg(); return 0; } bool countRowsFactory::CheckEnable(pgObject *obj) { return obj && ( obj->IsCreatedBy(pg_partitionFactory) ||obj->IsCreatedBy(tableFactory)); } executePgstattupleFactory::executePgstattupleFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list) { mnu->Append(id, _("&Extended table statistics"), _("Get extended statistics via pgstattuple for the selected object."), wxITEM_CHECK); } wxWindow *executePgstattupleFactory::StartDialog(frmMain *form, pgObject *obj) { if (!((pgTable *)obj)->GetShowExtendedStatistics()) { ((pgTable *)obj)->iSetShowExtendedStatistics(true); wxTreeItemId item = form->GetBrowser()->GetSelection(); if (obj == form->GetBrowser()->GetObject(item)) form->SelectStatisticsTab(); } else ((pgTable *)obj)->iSetShowExtendedStatistics(false); form->GetMenuFactories()->CheckMenu(obj, form->GetMenuBar(), (ctlMenuToolbar *)form->GetToolBar()); return 0; } bool executePgstattupleFactory::CheckEnable(pgObject *obj) { return obj && ( obj->IsCreatedBy(pg_partitionFactory) ||obj->IsCreatedBy(tableFactory)) && ((pgTable *)obj)->HasPgstattuple(); } bool executePgstattupleFactory::CheckChecked(pgObject *obj) { return obj && ( obj->IsCreatedBy(pg_partitionFactory) ||obj->IsCreatedBy(tableFactory)) && ((pgTable *)obj)->GetShowExtendedStatistics(); } disableAllTriggersFactory::disableAllTriggersFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list) { mnu->Append(id, _("Disable triggers"), _("Disable all triggers on the selected table.")); } wxWindow *disableAllTriggersFactory::StartDialog(frmMain *form, pgObject *obj) { if (wxMessageBox(_("Are you sure you wish to disable all triggers on this table?"), _("Disable triggers"), wxYES_NO) != wxYES) return 0; if (!((pgTable *)obj)->EnableTriggers(false)) return 0; ((pgTable *)obj)->iSetTriggersEnabled(form->GetBrowser(), false); return 0; } bool disableAllTriggersFactory::CheckEnable(pgObject *obj) { return obj && ( obj->IsCreatedBy(pg_partitionFactory) ||obj->IsCreatedBy(tableFactory)) && obj->CanEdit() && (obj->GetOwner() == obj->GetConnection()->GetUser() || obj->GetServer()->GetSuperUser()) && ((pgTable *)obj)->GetConnection()->BackendMinimumVersion(8, 1) && ((pgTable *)obj)->GetTriggerCount() > 0; } enableAllTriggersFactory::enableAllTriggersFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list) { mnu->Append(id, _("Enable triggers"), _("Enable all triggers on the selected table.")); } wxWindow *enableAllTriggersFactory::StartDialog(frmMain *form, pgObject *obj) { if (wxMessageBox(_("Are you sure you wish to enable all triggers on this table?"), _("Enable triggers"), wxYES_NO) != wxYES) return 0; if (!((pgTable *)obj)->EnableTriggers(true)) return 0; ((pgTable *)obj)->iSetTriggersEnabled(form->GetBrowser(), true); return 0; } bool enableAllTriggersFactory::CheckEnable(pgObject *obj) { return obj && ( obj->IsCreatedBy(pg_partitionFactory) ||obj->IsCreatedBy(tableFactory)) && obj->CanEdit() && (obj->GetOwner() == obj->GetConnection()->GetUser() || obj->GetServer()->GetSuperUser()) && ((pgTable *)obj)->GetConnection()->BackendMinimumVersion(8, 1) && ((pgTable *)obj)->GetTriggerCount() > 0; } truncateFactory::truncateFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list) { mnu->Append(id, _("&Truncate"), _("Truncate the selected table.")); } wxWindow *truncateFactory::StartDialog(frmMain *form, pgObject *obj) { if (wxMessageBox(_("Are you sure you wish to truncate this table?\n\nWARNING: This action will delete ALL data in the table!"), _("Truncate table"), wxYES_NO | wxICON_QUESTION | wxNO_DEFAULT) != wxYES) return 0; ((pgTable *)obj)->Truncate(false); ((pgTable *)obj)->UpdateRows(); wxTreeItemId item = form->GetBrowser()->GetSelection(); if (obj == form->GetBrowser()->GetObject(item)) obj->ShowTreeDetail(form->GetBrowser(), 0, form->GetProperties()); return 0; } bool truncateFactory::CheckEnable(pgObject *obj) { return obj && obj->IsCreatedBy(tableFactory); } truncateCascadedFactory::truncateCascadedFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list) { mnu->Append(id, _("Truncate Cascaded"), _("Truncate the selected table and all referencing tables.")); } wxWindow *truncateCascadedFactory::StartDialog(frmMain *form, pgObject *obj) { if (wxMessageBox(_("Are you sure you wish to truncate this table and all tables that have foreign key references to this table?\n\nWARNING: This action will delete ALL data in the tables!"), _("Truncate table cascaded"), wxYES_NO | wxICON_QUESTION | wxNO_DEFAULT) != wxYES) return 0; ((pgTable *)obj)->Truncate(true); ((pgTable *)obj)->UpdateRows(); wxTreeItemId item = form->GetBrowser()->GetSelection(); if (obj == form->GetBrowser()->GetObject(item)) obj->ShowTreeDetail(form->GetBrowser(), 0, form->GetProperties()); return 0; } bool truncateCascadedFactory::CheckEnable(pgObject *obj) { return obj && obj->IsCreatedBy(tableFactory) && ((pgTable *)obj)->GetConnection()->BackendMinimumVersion(8, 2); } resetTableStatsFactory::resetTableStatsFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list) { mnu->Append(id, _("&Reset table statistics"), _("Reset statistics of the selected table.")); } wxWindow *resetTableStatsFactory::StartDialog(frmMain *form, pgObject *obj) { if (wxMessageBox(_("Are you sure you wish to reset statistics of this table?"), _("Reset statistics"), wxYES_NO) != wxYES) return 0; ((pgTable *)obj)->ResetStats(); ((pgTable *)obj)->ShowStatistics(form, form->GetStatistics()); return 0; } bool resetTableStatsFactory::CheckEnable(pgObject *obj) { return obj && ( obj->IsCreatedBy(pg_partitionFactory) ||obj->IsCreatedBy(tableFactory)) && ((pgTable *)obj)->GetConnection()->BackendMinimumVersion(9, 0); }