mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 06:05:49 -06:00
2047 lines
71 KiB
C++
2047 lines
71 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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 <wx/regex.h>
|
|
|
|
// 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 <condition>;\n");
|
|
return sql;
|
|
}
|
|
|
|
wxString pgTable::GetDeleteSql(ctlTree *browser)
|
|
{
|
|
wxString sql =
|
|
wxT("DELETE FROM ") + GetQuotedFullIdentifier() + wxT("\n")
|
|
wxT(" WHERE <condition>;\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<gpPartition *>(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"), _("<no 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);
|
|
}
|