mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 06:05:49 -06:00
1372 lines
42 KiB
C++
1372 lines
42 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// pgAdmin III - PostgreSQL Tools
|
|
//
|
|
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
|
// This software is released under the PostgreSQL Licence
|
|
//
|
|
// pgDatabase.cpp - PostgreSQL Database
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// wxWindows headers
|
|
#include <wx/wx.h>
|
|
|
|
// App headers
|
|
#include "pgAdmin3.h"
|
|
#include "utils/misc.h"
|
|
#include "utils/pgfeatures.h"
|
|
#include "frm/frmMain.h"
|
|
#include "schema/edbSynonym.h"
|
|
#include "schema/pgCast.h"
|
|
#include "schema/pgExtension.h"
|
|
#include "schema/pgPublication.h"
|
|
#include "schema/pgSubscription.h"
|
|
#include "schema/pgForeignDataWrapper.h"
|
|
#include "schema/pgLanguage.h"
|
|
#include "schema/pgSchema.h"
|
|
#include "schema/pgEventTrigger.h"
|
|
#include "slony/slCluster.h"
|
|
#include "frm/frmHint.h"
|
|
#include "frm/frmReport.h"
|
|
#include "pro_scheduler/pgproJob.h"
|
|
|
|
|
|
pgDatabase::pgDatabase(const wxString &newName)
|
|
: pgServerObject(databaseFactory, newName)
|
|
{
|
|
useServerConnection = true;
|
|
allowConnections = true;
|
|
connected = false;
|
|
conn = NULL;
|
|
missingFKs = 0;
|
|
canDebugPlpgsql = 0;
|
|
canDebugEdbspl = 0;
|
|
}
|
|
|
|
|
|
pgDatabase::~pgDatabase()
|
|
{
|
|
Disconnect();
|
|
}
|
|
|
|
|
|
wxString pgDatabase::GetTranslatedMessage(int kindOfMessage) const
|
|
{
|
|
wxString message = wxEmptyString;
|
|
|
|
switch (kindOfMessage)
|
|
{
|
|
case RETRIEVINGDETAILS:
|
|
message = _("Retrieving details on database");
|
|
message += wxT(" ") + GetName();
|
|
break;
|
|
case REFRESHINGDETAILS:
|
|
message = _("Refreshing database");
|
|
message += wxT(" ") + GetName();
|
|
break;
|
|
case DROPINCLUDINGDEPS:
|
|
message = wxString::Format(_("Are you sure you wish to drop database \"%s\" including all objects that depend on it?"),
|
|
GetFullIdentifier().c_str());
|
|
break;
|
|
case DROPEXCLUDINGDEPS:
|
|
message = wxString::Format(_("Are you sure you wish to drop database \"%s\"?"),
|
|
GetFullIdentifier().c_str());
|
|
break;
|
|
case DROPCASCADETITLE:
|
|
message = _("Drop database cascaded?");
|
|
break;
|
|
case DROPTITLE:
|
|
message = _("Drop database?");
|
|
break;
|
|
case PROPERTIESREPORT:
|
|
message = _("Database properties report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case PROPERTIES:
|
|
message = _("Database properties");
|
|
break;
|
|
case DDLREPORT:
|
|
message = _("Database DDL report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case DDL:
|
|
message = _("Database DDL");
|
|
break;
|
|
case STATISTICSREPORT:
|
|
message = _("Database statistics report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case OBJSTATISTICS:
|
|
message = _("Database statistics");
|
|
break;
|
|
case DEPENDENCIESREPORT:
|
|
message = _("Database dependencies report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case DEPENDENCIES:
|
|
message = _("Database dependencies");
|
|
break;
|
|
case DEPENDENTSREPORT:
|
|
message = _("Database dependents report");
|
|
message += wxT(" - ") + GetName();
|
|
break;
|
|
case DEPENDENTS:
|
|
message = _("Database dependents");
|
|
break;
|
|
case BACKUPTITLE:
|
|
message = wxString::Format(_("Backup database \"%s\""),
|
|
GetFullIdentifier().c_str());
|
|
break;
|
|
case RESTORETITLE:
|
|
message = wxString::Format(_("Restore database \"%s\""),
|
|
GetFullIdentifier().c_str());
|
|
break;
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
|
|
int pgDatabase::GetIconId()
|
|
{
|
|
if (GetConnected())
|
|
return databaseFactory.GetIconId();
|
|
else
|
|
return databaseFactory.GetClosedIconId();
|
|
}
|
|
|
|
|
|
wxMenu *pgDatabase::GetNewMenu()
|
|
{
|
|
wxMenu *menu = pgObject::GetNewMenu();
|
|
|
|
if (GetConnection() && GetCreatePrivilege())
|
|
{
|
|
if (settings->GetDisplayOption(_("Casts")))
|
|
castFactory.AppendMenu(menu);
|
|
if (settings->GetDisplayOption(_("Extensions")) && GetConnection()->BackendMinimumVersion(9, 1))
|
|
extensionFactory.AppendMenu(menu);
|
|
if (settings->GetDisplayOption(_("Publications")) && GetConnection()->BackendMinimumVersion(10, 0))
|
|
publicationFactory.AppendMenu(menu);
|
|
if (settings->GetDisplayOption(_("Subscriptions")) && GetConnection()->BackendMinimumVersion(10, 0))
|
|
subscriptionFactory.AppendMenu(menu);
|
|
if (settings->GetDisplayOption(_("Foreign Data Wrappers")) && GetConnection()->BackendMinimumVersion(8, 4))
|
|
foreignDataWrapperFactory.AppendMenu(menu);
|
|
if (settings->GetDisplayOption(_("Languages")))
|
|
languageFactory.AppendMenu(menu);
|
|
if (settings->GetDisplayOption(_("Synonyms")) && GetConnection()->EdbMinimumVersion(8, 0))
|
|
if (!GetConnection()->BackendMinimumVersion(8, 4))
|
|
synonymFactory.AppendMenu(menu);
|
|
if (settings->GetDisplayOption(_("Schemas")))
|
|
schemaFactory.AppendMenu(menu);
|
|
if (settings->GetDisplayOption(_("Slony")))
|
|
slClusterFactory.AppendMenu(menu);
|
|
}
|
|
return menu;
|
|
}
|
|
|
|
int pgDatabase::Connect()
|
|
{
|
|
if (!allowConnections)
|
|
return PGCONN_REFUSED;
|
|
|
|
if (!connected)
|
|
{
|
|
if (GetName() == server->GetDatabaseName() && server->connection()->GetStatus() == PGCONN_OK)
|
|
{
|
|
useServerConnection = true;
|
|
conn = 0;
|
|
}
|
|
else
|
|
{
|
|
useServerConnection = false;
|
|
wxString applicationname = appearanceFactory->GetLongAppName() + wxT(" - Browser");
|
|
conn = CreateConn(applicationname);
|
|
|
|
if (!conn)
|
|
{
|
|
connected = false;
|
|
return PGCONN_BAD;
|
|
}
|
|
}
|
|
|
|
// Now we're connected.
|
|
|
|
// check for extended ruleutils with pretty-print option
|
|
wxString exprname = connection()->ExecuteScalar(wxT("SELECT proname FROM pg_proc WHERE proname='pg_get_viewdef' AND proargtypes[1]=16"));
|
|
if (!exprname.IsEmpty())
|
|
prettyOption = wxT(", true");
|
|
|
|
UpdateDefaultSchema();
|
|
|
|
if (connection()->BackendMinimumVersion(9, 0))
|
|
{
|
|
m_defPrivsOnTables = connection()->ExecuteScalar(wxT("SELECT array_agg(array_to_string(defaclacl,',')) FROM pg_catalog.pg_default_acl dacl WHERE dacl.defaclnamespace = 0::OID AND defaclobjtype='r'"));
|
|
m_defPrivsOnSeqs = connection()->ExecuteScalar(wxT("SELECT array_agg(array_to_string(defaclacl,',')) FROM pg_catalog.pg_default_acl dacl WHERE dacl.defaclnamespace = 0::OID AND defaclobjtype='S'"));
|
|
m_defPrivsOnFuncs = connection()->ExecuteScalar(wxT("SELECT array_agg(array_to_string(defaclacl,',')) FROM pg_catalog.pg_default_acl dacl WHERE dacl.defaclnamespace = 0::OID AND defaclobjtype='f'"));
|
|
}
|
|
if (connection()->BackendMinimumVersion(9, 2))
|
|
{
|
|
m_defPrivsOnTypes = connection()->ExecuteScalar(wxT("SELECT array_agg(array_to_string(defaclacl,',')) FROM pg_catalog.pg_default_acl dacl WHERE dacl.defaclnamespace = 0::OID AND defaclobjtype='T'"));
|
|
}
|
|
|
|
connected = true;
|
|
}
|
|
|
|
return connection()->GetStatus();
|
|
}
|
|
|
|
|
|
pgConn *pgDatabase::connection()
|
|
{
|
|
if (useServerConnection)
|
|
return server->connection();
|
|
return conn;
|
|
|
|
}
|
|
|
|
void pgDatabase::CheckAlive()
|
|
{
|
|
if (connected)
|
|
connected = connection()->IsAlive();
|
|
}
|
|
|
|
void pgDatabase::Disconnect()
|
|
{
|
|
connected = false;
|
|
if (conn)
|
|
delete conn;
|
|
conn = 0;
|
|
}
|
|
|
|
|
|
bool pgDatabase::GetCanHint()
|
|
{
|
|
if (encoding == wxT("SQL_ASCII"))
|
|
return true;
|
|
|
|
if (encoding == wxT("UNICODE"))
|
|
{
|
|
wxString ver = GetServer()->GetVersionString();
|
|
if (ver.Find(wxT("mingw32")) > 0 && ver.Find(wxT("SQL 8.0.")) > 0)
|
|
return true;
|
|
}
|
|
|
|
if (GetServer()->GetConnection() == GetConnection() &&
|
|
GetConnection()->BackendMinimumVersion(8, 0) &&
|
|
!GetConnection()->HasFeature(FEATURE_FILEREAD))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void pgDatabase::ShowHint(frmMain *form, bool force)
|
|
{
|
|
wxArrayString hints;
|
|
int rc = -1;
|
|
|
|
if (encoding == wxT("SQL_ASCII"))
|
|
hints.Add(HINT_ENCODING_ASCII);
|
|
else if (encoding == wxT("UNICODE"))
|
|
{
|
|
wxString ver = GetServer()->GetVersionString();
|
|
if (ver.Find(wxT("mingw32")) > 0 && ver.Find(wxT("SQL 8.0.")) > 0)
|
|
hints.Add(HINT_ENCODING_UNICODE);
|
|
}
|
|
|
|
if (GetServer()->GetConnection() == GetConnection() &&
|
|
GetConnection()->BackendMinimumVersion(8, 0) &&
|
|
!GetConnection()->HasFeature(FEATURE_FILEREAD))
|
|
{
|
|
// if the server release is 9.1 or more and the server has no adminpack
|
|
if (GetConnection()->BackendMinimumVersion(9, 1))
|
|
{
|
|
// Search the adminpack extension
|
|
pgSet *set = GetConnection()->ExecuteSet(wxT("SELECT 1 FROM pg_available_extensions WHERE name='adminpack'"));
|
|
if (set->NumRows() == 1)
|
|
hints.Add(HINT_INSTRUMENTATION_91_WITH);
|
|
else
|
|
hints.Add(HINT_INSTRUMENTATION_91_WITHOUT);
|
|
delete set;
|
|
}
|
|
else
|
|
hints.Add(HINT_INSTRUMENTATION);
|
|
}
|
|
|
|
if (force || !hintShown)
|
|
{
|
|
rc = frmHint::ShowHint(form, hints, GetFullIdentifier(), force);
|
|
if (rc == HINT_RC_FIX)
|
|
GetConnection()->ExecuteVoid(wxT("CREATE EXTENSION adminpack"), true);
|
|
}
|
|
hintShown = true;
|
|
}
|
|
|
|
|
|
void pgDatabase::ShowStatistics(frmMain *form, ctlListView *statistics)
|
|
{
|
|
if (GetConnection())
|
|
{
|
|
bool hasSize = connection()->HasFeature(FEATURE_SIZE);
|
|
|
|
wxString sql = wxT("SELECT numbackends AS ") + qtIdent(_("Backends")) +
|
|
wxT(", xact_commit AS ") + qtIdent(_("Xact Committed")) +
|
|
wxT(", xact_rollback AS ") + qtIdent(_("Xact Rolled Back")) +
|
|
wxT(", blks_read AS ") + qtIdent(_("Blocks Read")) +
|
|
wxT(", blks_hit AS ") + qtIdent(_("Blocks Hit"));
|
|
|
|
if (connection()->BackendMinimumVersion(8, 3))
|
|
sql += wxT(", tup_returned AS ") + qtIdent(_("Tuples Returned")) +
|
|
wxT(", tup_fetched AS ") + qtIdent(_("Tuples Fetched")) +
|
|
wxT(", tup_inserted AS ") + qtIdent(_("Tuples Inserted")) +
|
|
wxT(", tup_updated AS ") + qtIdent(_("Tuples Updated")) +
|
|
wxT(", tup_deleted AS ") + qtIdent(_("Tuples Deleted"));
|
|
|
|
if (connection()->BackendMinimumVersion(9, 1))
|
|
sql += wxT(", stats_reset AS ") + qtIdent(_("Last statistics reset")) +
|
|
wxT(", slave.confl_tablespace AS ") + qtIdent(_("Tablespace conflicts")) +
|
|
wxT(", slave.confl_lock AS ") + qtIdent(_("Lock conflicts")) +
|
|
wxT(", slave.confl_snapshot AS ") + qtIdent(_("Snapshot conflicts")) +
|
|
wxT(", slave.confl_bufferpin AS ") + qtIdent(_("Bufferpin conflicts")) +
|
|
wxT(", slave.confl_deadlock AS ") + qtIdent(_("Deadlock conflicts"));
|
|
|
|
if (connection()->BackendMinimumVersion(9, 2))
|
|
sql += wxT(", temp_files AS ") + qtIdent(_("Temporary files")) +
|
|
wxT(", pg_size_pretty(temp_bytes) AS ") + qtIdent(_("Size of temporary files")) +
|
|
wxT(", deadlocks AS ") + qtIdent(_("Deadlocks")) +
|
|
wxT(", blk_read_time AS ") + qtIdent(_("Block read time")) +
|
|
wxT(", blk_write_time AS ") + qtIdent(_("Block write time"));
|
|
|
|
if (hasSize)
|
|
sql += wxT(", pg_size_pretty(pg_database_size(db.datid)) AS ") + qtIdent(_("Size"));
|
|
|
|
sql += wxT("\n FROM pg_stat_database db");
|
|
if (connection()->BackendMinimumVersion(9, 1))
|
|
sql += wxT(" JOIN pg_stat_database_conflicts slave ON db.datid=slave.datid");
|
|
sql += wxT(" WHERE db.datname=") + qtDbString(GetName());
|
|
|
|
// DisplayStatistics is not available for this object
|
|
|
|
CreateListColumns(statistics, _("Statistic"), _("Value"));
|
|
|
|
pgSet *stats = connection()->ExecuteSet(sql);
|
|
|
|
if (stats)
|
|
{
|
|
int col;
|
|
for (col = 0 ; col < stats->NumCols() ; col++)
|
|
{
|
|
if (!stats->ColName(col).IsEmpty())
|
|
statistics->AppendItem(stats->ColName(col), stats->GetVal(col));
|
|
}
|
|
delete stats;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
pgSet *pgDatabase::ExecuteSet(const wxString &sql)
|
|
{
|
|
pgSet *set = 0;
|
|
if (connection())
|
|
{
|
|
set = connection()->ExecuteSet(sql);
|
|
if (!set)
|
|
CheckAlive();
|
|
}
|
|
return set;
|
|
}
|
|
|
|
|
|
wxString pgDatabase::ExecuteScalar(const wxString &sql)
|
|
{
|
|
wxString str;
|
|
if (connection())
|
|
{
|
|
str = connection()->ExecuteScalar(sql);
|
|
if (str.IsEmpty() && connection()->GetLastResultStatus() != PGRES_TUPLES_OK)
|
|
CheckAlive();
|
|
}
|
|
return str;
|
|
}
|
|
|
|
|
|
bool pgDatabase::ExecuteVoid(const wxString &sql, bool reportError)
|
|
{
|
|
bool rc = 0;
|
|
if (connection())
|
|
{
|
|
rc = connection()->ExecuteVoid(sql, reportError);
|
|
if (!rc)
|
|
CheckAlive();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
void pgDatabase::UpdateDefaultSchema()
|
|
{
|
|
searchPath = connection()->ExecuteScalar(wxT("SHOW search_path"));
|
|
|
|
if (!searchPath.IsEmpty())
|
|
{
|
|
wxStringTokenizer tk(searchPath, wxT(","));
|
|
pgSet *set = ExecuteSet(wxT("SELECT nspname, session_user=nspname AS isuser FROM pg_namespace"));
|
|
if (set)
|
|
{
|
|
while (tk.HasMoreTokens())
|
|
{
|
|
wxString str = tk.GetNextToken();
|
|
str.Strip(wxString::both);
|
|
|
|
if (str.IsEmpty())
|
|
continue;
|
|
long row;
|
|
for (row = 1 ; row <= set->NumRows() ; row++)
|
|
{
|
|
set->Locate(row);
|
|
defaultSchema = set->GetVal(wxT("nspname"));
|
|
if (str == defaultSchema ||
|
|
((str == wxT("$user") || str == wxT("\"$user\"")) && set->GetBool(wxT("isuser"))))
|
|
{
|
|
delete set;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
delete set;
|
|
}
|
|
}
|
|
defaultSchema = wxEmptyString;
|
|
}
|
|
|
|
|
|
wxString pgDatabase::GetSchemaPrefix(const wxString &name) const
|
|
{
|
|
if (name.IsEmpty())
|
|
return name;
|
|
|
|
if (name == wxT("pg_catalog") || name == defaultSchema)
|
|
return wxEmptyString;
|
|
|
|
return name + wxT(".");
|
|
}
|
|
|
|
|
|
wxString pgDatabase::GetQuotedSchemaPrefix(const wxString &name) const
|
|
{
|
|
wxString str = GetSchemaPrefix(name);
|
|
if (!str.IsEmpty())
|
|
return qtIdent(str.Left(str.Length() - 1)) + wxT(".");
|
|
return str;
|
|
}
|
|
|
|
|
|
bool pgDatabase::GetSystemObject() const
|
|
{
|
|
if (server)
|
|
{
|
|
if (this->GetName() == wxT("template0")) return true;
|
|
return (this->GetOid() <= server->GetLastSystemOID());
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
wxArrayString pgDatabase::GetSlonyClusters(ctlTree *browser)
|
|
{
|
|
wxArrayString clusters;
|
|
|
|
pgCollection *collection = browser->FindCollection(slClusterFactory, GetId());
|
|
if (collection)
|
|
{
|
|
treeObjectIterator clusterIterator(browser, collection);
|
|
|
|
slCluster *cluster;
|
|
while ((cluster = (slCluster *)clusterIterator.GetNextObject()) != 0)
|
|
clusters.Add(cluster->GetName());
|
|
}
|
|
return clusters;
|
|
}
|
|
|
|
|
|
void pgDatabase::AppendSchemaChange(const wxString &sql)
|
|
{
|
|
wxDateTime dt;
|
|
dt.Now();
|
|
schemaChanges.Append(wxT("-- ") + DateToStr(dt) + wxT("\n"));
|
|
schemaChanges.Append(sql);
|
|
schemaChanges.Append(wxT("\n\n"));
|
|
}
|
|
|
|
bool pgDatabase::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
|
|
{
|
|
if (useServerConnection)
|
|
{
|
|
wxMessageDialog(frame, _("Maintenance database can't be dropped."),
|
|
_("Dropping database not allowed"), wxICON_EXCLAMATION | wxOK).ShowModal();
|
|
|
|
return false;
|
|
}
|
|
Disconnect();
|
|
|
|
bool done = server->ExecuteVoid(wxT("DROP DATABASE ") + GetQuotedIdentifier() + wxT(";"));
|
|
if (!done)
|
|
Connect();
|
|
|
|
return done;
|
|
}
|
|
|
|
|
|
|
|
wxString pgDatabase::GetSql(ctlTree *browser)
|
|
{
|
|
if (sql.IsEmpty())
|
|
{
|
|
// If we can't connect to this database, use the maintenance DB
|
|
pgConn *myConn = GetConnection();
|
|
if (!myConn)
|
|
myConn = GetServer()->GetConnection();
|
|
|
|
sql = wxT("-- Database: ") + GetQuotedFullIdentifier() + wxT("\n\n")
|
|
+ wxT("-- DROP DATABASE ") + GetQuotedIdentifier() + wxT(";")
|
|
+ wxT("\n\nCREATE DATABASE ") + GetQuotedIdentifier()
|
|
+ wxT("\n WITH OWNER = ") + qtIdent(GetOwner())
|
|
+ wxT("\n ENCODING = ") + qtDbString(GetEncoding());
|
|
if (!GetTablespace().IsEmpty())
|
|
sql += wxT("\n TABLESPACE = ") + qtIdent(GetTablespace());
|
|
if (myConn && myConn->BackendMinimumVersion(8, 4))
|
|
{
|
|
sql += wxT("\n LC_COLLATE = ") + qtDbString(GetCollate());
|
|
sql += wxT("\n LC_CTYPE = ") + qtDbString(GetCType());
|
|
}
|
|
if (myConn && myConn->BackendMinimumVersion(8, 1))
|
|
{
|
|
sql += wxT("\n CONNECTION LIMIT = ");
|
|
sql << GetConnectionLimit();
|
|
}
|
|
sql += wxT(";\n");
|
|
|
|
if (variables.GetCount() > 0)
|
|
{
|
|
size_t i;
|
|
wxString username = wxEmptyString;
|
|
wxString parameter = wxEmptyString;
|
|
wxString value = wxEmptyString;
|
|
|
|
sql += wxT("\n");
|
|
for (i = 0 ; i < variables.GetCount() ; i += 3)
|
|
{
|
|
username = variables.Item(i);
|
|
parameter = variables.Item(i + 1);
|
|
value = variables.Item(i + 2);
|
|
|
|
if (username.Length() == 0)
|
|
{
|
|
sql += wxT("ALTER DATABASE ") + GetQuotedFullIdentifier();
|
|
}
|
|
else
|
|
{
|
|
sql += wxT("ALTER ROLE ") + username + wxT(" IN DATABASE ") + GetQuotedFullIdentifier();
|
|
}
|
|
|
|
if (parameter != wxT("search_path") && parameter != wxT("temp_tablespaces"))
|
|
{
|
|
sql += wxT("\n SET ") + parameter + wxT(" = '") + value + wxT("';\n");
|
|
}
|
|
else
|
|
{
|
|
sql += wxT("\n SET ") + parameter + wxT(" = ") + value + wxT(";\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (myConn)
|
|
{
|
|
if (!myConn->BackendMinimumVersion(8, 2))
|
|
sql += GetGrant(wxT("CT"));
|
|
else
|
|
sql += GetGrant(wxT("CTc"));
|
|
}
|
|
|
|
sql += wxT("\n") + pgDatabase::GetDefaultPrivileges('r', m_defPrivsOnTables, wxT(""));
|
|
sql += pgDatabase::GetDefaultPrivileges('S', m_defPrivsOnSeqs, wxT(""));
|
|
sql += pgDatabase::GetDefaultPrivileges('f', m_defPrivsOnFuncs, wxT(""));
|
|
sql += pgDatabase::GetDefaultPrivileges('T', m_defPrivsOnTypes, wxT(""));
|
|
|
|
sql += GetCommentSql();
|
|
|
|
if (myConn->BackendMinimumVersion(9, 2))
|
|
sql += GetSeqLabelsSql();
|
|
}
|
|
return sql;
|
|
}
|
|
|
|
|
|
|
|
void pgDatabase::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
|
|
{
|
|
if (Connect() == PGCONN_OK)
|
|
{
|
|
// Set the icon if required
|
|
UpdateIcon(browser);
|
|
|
|
// Add child nodes if necessary
|
|
if (browser->GetChildrenCount(GetId(), false) == 0)
|
|
{
|
|
wxLogInfo(wxT("Adding child object to database %s"), GetIdentifier().c_str());
|
|
|
|
if (settings->GetDisplayOption(_("Catalogs")))
|
|
browser->AppendCollection(this, catalogFactory);
|
|
if (settings->GetDisplayOption(_("Casts")))
|
|
browser->AppendCollection(this, castFactory);
|
|
if (connection()->BackendMinimumVersion(9, 3) && settings->GetDisplayOption(_("Event Triggers")))
|
|
browser->AppendCollection(this, eventTriggerFactory);
|
|
if (settings->GetDisplayOption(_("Extensions")) && GetConnection()->BackendMinimumVersion(9, 1))
|
|
browser->AppendCollection(this, extensionFactory);
|
|
if (settings->GetDisplayOption(_("Publications")) && GetConnection()->BackendMinimumVersion(10, 0))
|
|
browser->AppendCollection(this, publicationFactory);
|
|
if (settings->GetDisplayOption(_("Subscriptions")) && GetConnection()->BackendMinimumVersion(10, 0))
|
|
browser->AppendCollection(this, subscriptionFactory);
|
|
if (settings->GetDisplayOption(_("Foreign Data Wrappers")) && GetConnection()->BackendMinimumVersion(8, 4))
|
|
browser->AppendCollection(this, foreignDataWrapperFactory);
|
|
if (settings->GetDisplayOption(_("Languages")))
|
|
browser->AppendCollection(this, languageFactory);
|
|
if (settings->GetDisplayOption(_("Synonyms")) && connection()->EdbMinimumVersion(8, 0))
|
|
if (!GetConnection()->BackendMinimumVersion(8, 4))
|
|
browser->AppendCollection(this, synonymFactory);
|
|
if (settings->GetDisplayOption(_("Schemas")))
|
|
browser->AppendCollection(this, schemaFactory);
|
|
if (settings->GetDisplayOption(_("pgpro_scheduler")))
|
|
{
|
|
wxString exists = connection()->ExecuteScalar(
|
|
wxT("select true ok from pg_extension where extname='pgpro_scheduler'\n"));
|
|
|
|
if (!exists.IsNull())
|
|
{
|
|
browser->AppendCollection(this, projobFactory);
|
|
}
|
|
}
|
|
|
|
if (settings->GetDisplayOption(_("Slony-I Clusters")))
|
|
browser->AppendCollection(this, slClusterFactory);
|
|
|
|
wxString missingFKsql = wxT("SELECT COUNT(*) FROM\n")
|
|
wxT(" (SELECT tgargs from pg_trigger tr\n")
|
|
wxT(" LEFT JOIN pg_depend dep ON dep.objid=tr.oid AND deptype = 'i'\n")
|
|
wxT(" LEFT JOIN pg_constraint co ON refobjid = co.oid AND contype = 'f'\n")
|
|
wxT(" WHERE \n");
|
|
if (connection()->BackendMinimumVersion(9, 0))
|
|
missingFKsql += wxT("tgisinternal\n");
|
|
else
|
|
missingFKsql += wxT("tgisconstraint\n");
|
|
|
|
missingFKsql += wxT(" AND co.oid IS NULL\n")
|
|
wxT(" GROUP BY tgargs\n")
|
|
wxT(" HAVING count(1) = 3) AS foo");
|
|
missingFKs = StrToLong(connection()->ExecuteScalar(missingFKsql));
|
|
|
|
// Get configuration
|
|
wxString query;
|
|
if (connection()->BackendMinimumVersion(9, 0))
|
|
{
|
|
query = wxT("WITH configs AS ")
|
|
wxT("(SELECT rolname, unnest(setconfig) AS config")
|
|
wxT(" FROM pg_db_role_setting s")
|
|
wxT(" LEFT JOIN pg_roles r ON r.oid=s.setrole")
|
|
wxT(" WHERE s.setdatabase=") + NumToStr(GetOid()) + wxT(")\n")
|
|
wxT("SELECT rolname, split_part(config, '=', 1) AS variable, ")
|
|
wxT(" replace(config, split_part(config, '=', 1) || '=', '') AS value\n")
|
|
wxT("FROM configs");
|
|
}
|
|
else
|
|
{
|
|
wxString query_mydb = wxT("SELECT datconfig FROM pg_database WHERE oid=") + NumToStr(GetOid());
|
|
query = wxT("SELECT '' AS rolname, split_part(config, '=', 1) AS variable,\n")
|
|
wxT(" replace(config,split_part(config, '=', 1) || '=', '') AS value\n")
|
|
wxT("FROM (\n")
|
|
wxT(" SELECT\n")
|
|
wxT(" (\n")
|
|
wxT(" SELECT datconfig[i]\n")
|
|
wxT(" FROM pg_database\n")
|
|
wxT(" WHERE oid=") + NumToStr(GetOid()) + wxT("\n")
|
|
wxT(" ) AS config\n")
|
|
wxT(" FROM generate_series(array_lower((") + query_mydb + wxT("),1), array_upper((") + query_mydb + wxT("),1)) AS i\n")
|
|
wxT(" ) configs");
|
|
}
|
|
pgSet *configs = connection()->ExecuteSet(query);
|
|
if (configs)
|
|
{
|
|
while (!configs->Eof())
|
|
{
|
|
variables.Add(configs->GetVal(wxT("rolname")));
|
|
variables.Add(configs->GetVal(wxT("variable")));
|
|
variables.Add(configs->GetVal(wxT("value")));
|
|
configs->MoveNext();
|
|
}
|
|
delete configs;
|
|
}
|
|
}
|
|
}
|
|
|
|
GetServer()->iSetLastDatabase(GetName());
|
|
|
|
if (properties)
|
|
{
|
|
// Setup listview
|
|
CreateListColumns(properties);
|
|
|
|
properties->AppendItem(_("Name"), GetName());
|
|
properties->AppendItem(_("OID"), NumToStr(GetOid()));
|
|
properties->AppendItem(_("Owner"), GetOwner());
|
|
properties->AppendItem(_("ACL"), GetAcl());
|
|
if (!GetPath().IsEmpty())
|
|
properties->AppendItem(_("Path"), GetPath());
|
|
|
|
// We may not actually be connected...
|
|
if (GetConnection() && GetConnection()->BackendMinimumVersion(8, 0))
|
|
{
|
|
properties->AppendItem(_("Tablespace"), GetTablespace());
|
|
properties->AppendItem(_("Default tablespace"), GetDefaultTablespace());
|
|
}
|
|
properties->AppendItem(_("Encoding"), GetEncoding());
|
|
|
|
if (GetConnection() && GetConnection()->BackendMinimumVersion(8, 4))
|
|
{
|
|
properties->AppendItem(_("Collation"), GetCollate());
|
|
properties->AppendItem(_("Character type"), GetCType());
|
|
}
|
|
|
|
properties->AppendItem(_("Default schema"), defaultSchema);
|
|
|
|
if (GetConnection() && GetConnection()->BackendMinimumVersion(9, 0))
|
|
{
|
|
properties->AppendItem(_("Default table ACL"), m_defPrivsOnTables);
|
|
properties->AppendItem(_("Default sequence ACL"), m_defPrivsOnSeqs);
|
|
properties->AppendItem(_("Default function ACL"), m_defPrivsOnFuncs);
|
|
}
|
|
if (GetConnection() && GetConnection()->BackendMinimumVersion(9, 2))
|
|
properties->AppendItem(_("Default type ACL"), m_defPrivsOnTypes);
|
|
|
|
size_t i;
|
|
wxString username;
|
|
wxString parameter;
|
|
wxString value;
|
|
for (i = 0 ; i < variables.GetCount() ; i += 3)
|
|
{
|
|
username = variables.Item(i);
|
|
parameter = variables.Item(i + 1);
|
|
value = variables.Item(i + 2);
|
|
|
|
if (username.Length() == 0)
|
|
{
|
|
properties->AppendItem(parameter, value);
|
|
}
|
|
else
|
|
{
|
|
properties->AppendItem(parameter + wxT(" (role ") + username + wxT(")"), value);
|
|
}
|
|
}
|
|
properties->AppendYesNoItem(_("Allow connections?"), GetAllowConnections());
|
|
properties->AppendYesNoItem(_("Connected?"), GetConnected());
|
|
if (GetConnection() && GetConnection()->BackendMinimumVersion(8, 1))
|
|
{
|
|
wxString strConnLimit;
|
|
strConnLimit.Printf(wxT("%ld"), GetConnectionLimit());
|
|
properties->AppendItem(_("Connection limit"), strConnLimit);
|
|
}
|
|
properties->AppendYesNoItem(_("System database?"), GetSystemObject());
|
|
if (GetMissingFKs())
|
|
properties->AppendItem(_("Old style FKs"), GetMissingFKs());
|
|
if (!GetSchemaRestriction().IsEmpty())
|
|
properties->AppendItem(_("Schema restriction"), GetSchemaRestriction());
|
|
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 && GetCanHint() && !hintShown)
|
|
{
|
|
ShowHint(form, false);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pgObject *pgDatabase::Refresh(ctlTree *browser, const wxTreeItemId item)
|
|
{
|
|
pgDatabase *database = 0;
|
|
pgCollection *coll = browser->GetParentCollection(item);
|
|
if (coll)
|
|
database = (pgDatabase *)databaseFactory.CreateObjects(coll, 0, wxT(" WHERE db.oid=") + GetOidStr() + wxT("\n"));
|
|
|
|
return database;
|
|
}
|
|
|
|
|
|
pgObject *pgDatabaseFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
|
|
{
|
|
pgDatabase *database = 0;
|
|
|
|
pgSet *databases;
|
|
|
|
wxString datcollate, datctype, datconnlimit, seclabelsql;
|
|
|
|
if (collection->GetConnection()->BackendMinimumVersion(8, 1))
|
|
{
|
|
datconnlimit = wxT(", db.datconnlimit as connectionlimit");
|
|
}
|
|
if (collection->GetConnection()->BackendMinimumVersion(8, 4))
|
|
{
|
|
datctype = wxT(", db.datctype as ctype");
|
|
datcollate = wxT(", db.datcollate as collate");
|
|
}
|
|
if (collection->GetConnection()->BackendMinimumVersion(9, 2))
|
|
{
|
|
seclabelsql = wxT(",\n(SELECT array_agg(label) FROM pg_shseclabel sl1 WHERE sl1.objoid=db.oid) AS labels")
|
|
wxT(",\n(SELECT array_agg(provider) FROM pg_shseclabel sl2 WHERE sl2.objoid=db.oid) AS providers");
|
|
}
|
|
|
|
wxString restr = restriction;
|
|
if (!collection->GetServer()->GetDbRestriction().IsEmpty())
|
|
{
|
|
if (restr.IsEmpty())
|
|
restr = wxT(" WHERE datname IN (");
|
|
else
|
|
restr += wxT(" AND datname IN (");
|
|
|
|
restr += collection->GetServer()->GetDbRestriction() + wxT(")\n");
|
|
}
|
|
|
|
// In 9.0+, database config options are in pg_db_role_setting
|
|
if (collection->GetConnection()->BackendMinimumVersion(9, 0))
|
|
{
|
|
databases = collection->GetServer()->ExecuteSet(
|
|
wxT("SELECT db.oid, datname, db.dattablespace AS spcoid, spcname, datallowconn, datacl, ")
|
|
wxT("pg_encoding_to_char(encoding) AS serverencoding, pg_get_userbyid(datdba) AS datowner,")
|
|
wxT("has_database_privilege(db.oid, 'CREATE') as cancreate, \n")
|
|
wxT("current_setting('default_tablespace') AS default_tablespace, \n")
|
|
wxT("descr.description\n") +
|
|
datconnlimit + datcollate + datctype + seclabelsql +
|
|
wxT(" FROM pg_database db\n")
|
|
wxT(" LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace=ta.OID\n")
|
|
wxT(" LEFT OUTER JOIN pg_shdescription descr ON (db.oid=descr.objoid AND descr.classoid='pg_database'::regclass)\n")
|
|
+ restr +
|
|
wxT(" ORDER BY datname"));
|
|
}
|
|
else if (collection->GetConnection()->BackendMinimumVersion(8, 0))
|
|
databases = collection->GetServer()->ExecuteSet(
|
|
wxT("SELECT db.oid, datname, db.dattablespace AS spcoid, spcname, datallowconn, datacl, ")
|
|
wxT("pg_encoding_to_char(encoding) AS serverencoding, pg_get_userbyid(datdba) AS datowner,")
|
|
wxT("has_database_privilege(db.oid, 'CREATE') as cancreate, \n")
|
|
wxT("current_setting('default_tablespace') AS default_tablespace, \n")
|
|
wxT("descr.description\n") +
|
|
datconnlimit + datcollate + datctype +
|
|
wxT(" FROM pg_database db\n")
|
|
wxT(" LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace=ta.OID\n")
|
|
wxT(" LEFT OUTER JOIN ")
|
|
+ wxString(collection->GetConnection()->BackendMinimumVersion(8, 2) ? wxT("pg_shdescription") : wxT("pg_description")) +
|
|
wxT(" descr ON (db.oid=descr.objoid AND descr.classoid='pg_database'::regclass)\n")
|
|
+ restr +
|
|
wxT(" ORDER BY datname"));
|
|
else
|
|
databases = collection->GetServer()->ExecuteSet(
|
|
wxT("SELECT db.oid, datname, datpath, datallowconn, datacl, ")
|
|
wxT("pg_encoding_to_char(encoding) AS serverencoding, pg_get_userbyid(datdba) AS datowner,")
|
|
wxT("has_database_privilege(db.oid, 'CREATE') as cancreate,\n")
|
|
wxT("descr.description\n")
|
|
wxT(" FROM pg_database db\n")
|
|
wxT(" LEFT OUTER JOIN pg_description descr ON (db.oid=descr.objoid AND descr.classoid='pg_database'::regclass)\n")
|
|
+ restr +
|
|
wxT(" ORDER BY datname"));
|
|
|
|
if (databases)
|
|
{
|
|
while (!databases->Eof())
|
|
{
|
|
wxString name = databases->GetVal(wxT("datname"));
|
|
database = new pgDatabase(name);
|
|
database->iSetServer(collection->GetServer());
|
|
database->iSetOid(databases->GetOid(wxT("oid")));
|
|
database->iSetOwner(databases->GetVal(wxT("datowner")));
|
|
database->iSetAcl(databases->GetVal(wxT("datacl")));
|
|
database->iSetEncoding(databases->GetVal(wxT("serverencoding")));
|
|
database->iSetCreatePrivilege(databases->GetBool(wxT("cancreate")));
|
|
database->iSetComment(databases->GetVal(wxT("description")));
|
|
database->iSetAllowConnections(databases->GetBool(wxT("datallowconn")));
|
|
|
|
if (collection->GetConnection()->BackendMinimumVersion(8, 0))
|
|
{
|
|
database->iSetTablespace(databases->GetVal(wxT("spcname")));
|
|
database->iSetTablespaceOid(databases->GetOid(wxT("spcoid")));
|
|
if (databases->GetVal(wxT("default_tablespace")) == wxEmptyString || databases->GetVal(wxT("default_tablespace")) == wxT("unset"))
|
|
database->iSetDefaultTablespace(databases->GetVal(wxT("spcname")));
|
|
else
|
|
database->iSetDefaultTablespace(databases->GetVal(wxT("default_tablespace")));
|
|
}
|
|
else
|
|
database->iSetPath(databases->GetVal(wxT("datpath")));
|
|
|
|
if (collection->GetConnection()->BackendMinimumVersion(8, 1))
|
|
{
|
|
database->iSetConnectionLimit(databases->GetLong(wxT("connectionlimit")));
|
|
}
|
|
if (collection->GetConnection()->BackendMinimumVersion(8, 4))
|
|
{
|
|
database->iSetCollate(databases->GetVal(wxT("collate")));
|
|
database->iSetCType(databases->GetVal(wxT("ctype")));
|
|
}
|
|
|
|
if (collection->GetServer()->GetServerIndex())
|
|
{
|
|
wxString value;
|
|
settings->Read(wxT("Servers/") + NumToStr(collection->GetServer()->GetServerIndex())
|
|
+ wxT("/Databases/") + name + wxT("/SchemaRestriction"), &value, wxEmptyString);
|
|
|
|
database->iSetSchemaRestriction(value);
|
|
}
|
|
|
|
if (collection->GetConnection()->BackendMinimumVersion(9, 2))
|
|
{
|
|
database->iSetProviders(databases->GetVal(wxT("providers")));
|
|
database->iSetLabels(databases->GetVal(wxT("labels")));
|
|
}
|
|
|
|
// Add the treeview node if required
|
|
if (settings->GetShowSystemObjects() || !database->GetSystemObject())
|
|
{
|
|
if (browser)
|
|
{
|
|
int icon;
|
|
if (database->GetName() == database->GetServer()->GetDatabaseName())
|
|
icon = databaseFactory.GetIconId();
|
|
else
|
|
icon = databaseFactory.GetClosedIconId();
|
|
|
|
browser->AppendItem(collection->GetId(), database->GetIdentifier(), icon, -1, database);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
delete database;
|
|
|
|
databases->MoveNext();
|
|
}
|
|
delete databases;
|
|
}
|
|
return database;
|
|
}
|
|
|
|
wxString pgDatabase::GetDefaultPrivileges(const wxChar &cType, wxString strDefPrivs, const wxString &strSchema)
|
|
{
|
|
wxString strDefPrivsSql;
|
|
|
|
if (!strDefPrivs.IsEmpty())
|
|
{
|
|
wxString strRole, strPriv, strSupportedPrivs, strType;
|
|
strDefPrivs.Replace(wxT("\\\""), wxT("\""), true);
|
|
strDefPrivs.Replace(wxT("\\\\"), wxT("\\"), true);
|
|
|
|
switch(cType)
|
|
{
|
|
case 'r':
|
|
strType = wxT("TABLES");
|
|
strSupportedPrivs = wxT("arwdDxtm");
|
|
break;
|
|
case 'S':
|
|
strType = wxT("SEQUENCES");
|
|
strSupportedPrivs = wxT("rwU");
|
|
break;
|
|
case 'f':
|
|
strType = wxT("FUNCTIONS");
|
|
strSupportedPrivs = wxT("X");
|
|
break;
|
|
case 'T':
|
|
strType = wxT("TYPES");
|
|
strSupportedPrivs = wxT("U");
|
|
break;
|
|
default:
|
|
return wxT("");
|
|
}
|
|
|
|
// Removing starting brace '{' and ending brace '}'
|
|
strDefPrivs = strDefPrivs.SubString(1, strDefPrivs.Length() - 1);
|
|
|
|
while (pgObject::findUserPrivs(strDefPrivs, strRole, strPriv))
|
|
{
|
|
strDefPrivsSql += pgObject::GetDefaultPrivileges(strType, strSupportedPrivs, strSchema, wxT(""), strPriv, qtIdent(strRole));
|
|
|
|
strRole = wxT("");
|
|
strPriv = wxT("");
|
|
}
|
|
}
|
|
return strDefPrivsSql;
|
|
}
|
|
|
|
bool pgDatabase::CanCreate()
|
|
{
|
|
return GetDatabase()->GetCreatePrivilege();
|
|
}
|
|
|
|
bool pgDatabase::CanDebugPlpgsql()
|
|
{
|
|
wxString preload_option;
|
|
|
|
// Result cache - 0 = not tested, 1 = false, 2 = true.
|
|
if (canDebugPlpgsql == 1)
|
|
return false;
|
|
else if (canDebugPlpgsql == 2)
|
|
return true;
|
|
|
|
// "show shared_preload_libraries" does not work for other than
|
|
// the super users.
|
|
if (GetServer()->GetSuperUser())
|
|
{
|
|
// Parameter's name depends of the backend's version
|
|
if (server->GetConnection()->BackendMinimumVersion(8, 2))
|
|
{
|
|
preload_option = wxT("shared_preload_libraries");
|
|
}
|
|
else
|
|
{
|
|
preload_option = wxT("preload_libraries");
|
|
}
|
|
|
|
// Check the appropriate plugin is loaded
|
|
if (!ExecuteScalar(wxT("SHOW ") + preload_option).Contains(wxT("plugin_debugger")))
|
|
{
|
|
canDebugPlpgsql = 1;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (ExecuteScalar(wxT("SELECT count(*) FROM pg_proc WHERE proname = 'pldbg_get_target_info';")) == wxT("0"))
|
|
{
|
|
canDebugPlpgsql = 1;
|
|
return false;
|
|
}
|
|
|
|
// If this is EDBAS81, the debuggers will be built into the PLs
|
|
// so we don't need to check any further.
|
|
if (server->GetConnection()->EdbMinimumVersion(8, 1))
|
|
{
|
|
canDebugPlpgsql = 2;
|
|
return true;
|
|
}
|
|
|
|
// On EDBAS82 and PostgreSQL, we need to check to make sure that
|
|
// the debugger library is also available.
|
|
if (ExecuteScalar(wxT("SELECT count(*) FROM pg_proc WHERE proname = 'plpgsql_oid_debug';")) == wxT("0"))
|
|
{
|
|
canDebugPlpgsql = 1;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
canDebugPlpgsql = 2;
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool pgDatabase::CanDebugEdbspl()
|
|
{
|
|
// Result cache - 0 = not tested, 1 = false, 2 = true.
|
|
if (canDebugEdbspl == 1)
|
|
return false;
|
|
else if (canDebugEdbspl == 2)
|
|
return true;
|
|
|
|
// "show shared_preload_libraries" does not work for other than
|
|
// the super users.
|
|
if (GetServer()->GetSuperUser())
|
|
{
|
|
// Check the appropriate plugin is loaded
|
|
|
|
// Before EDBAS92, there was a separate library for SPL and PL/pgSQL.
|
|
// Starting with 9.2, EDB uses the community version of pldebugger,
|
|
// and support for both languages is built into plugin_debugger.so
|
|
wxString library_name;
|
|
|
|
if (server->GetConnection()->EdbMinimumVersion(9, 2))
|
|
library_name = wxT("plugin_debugger");
|
|
else
|
|
library_name = wxT("plugin_spl_debugger");
|
|
|
|
if (!ExecuteScalar(wxT("SHOW shared_preload_libraries;")).Contains(library_name))
|
|
{
|
|
canDebugEdbspl = 1;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (ExecuteScalar(wxT("SELECT count(*) FROM pg_proc WHERE proname = 'pldbg_get_target_info';")) == wxT("0"))
|
|
{
|
|
canDebugEdbspl = 1;
|
|
return false;
|
|
}
|
|
|
|
// If this is EDBAS81, the debuggers will be built into the PLs
|
|
// so we don't need to check any further.
|
|
if (server->GetConnection()->EdbMinimumVersion(8, 1))
|
|
{
|
|
canDebugEdbspl = 2;
|
|
return true;
|
|
}
|
|
|
|
// On EDBAS82 and PostgreSQL, we need to check to make sure that
|
|
// the debugger library is also available.
|
|
if (ExecuteScalar(wxT("SELECT count(*) FROM pg_proc WHERE proname = 'edb_oid_debug';")) == wxT("0"))
|
|
{
|
|
canDebugEdbspl = 1;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
canDebugEdbspl = 2;
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
pgDatabaseCollection::pgDatabaseCollection(pgaFactory *factory, pgServer *sv)
|
|
: pgServerObjCollection(factory, sv)
|
|
{
|
|
}
|
|
|
|
|
|
wxString pgDatabaseCollection::GetTranslatedMessage(int kindOfMessage) const
|
|
{
|
|
wxString message = wxEmptyString;
|
|
|
|
switch (kindOfMessage)
|
|
{
|
|
case RETRIEVINGDETAILS:
|
|
message = _("Retrieving details on databases");
|
|
break;
|
|
case REFRESHINGDETAILS:
|
|
message = _("Refreshing databases");
|
|
break;
|
|
case STATISTICSREPORT:
|
|
message = _("Databases statistics report");
|
|
break;
|
|
case OBJSTATISTICS:
|
|
message = _("Databases statistics");
|
|
break;
|
|
case OBJECTSLISTREPORT:
|
|
message = _("Databases list report");
|
|
break;
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
|
|
void pgDatabaseCollection::ShowStatistics(frmMain *form, ctlListView *statistics)
|
|
{
|
|
wxLogInfo(wxT("Displaying statistics for databases on ") + GetServer()->GetIdentifier());
|
|
|
|
bool hasSize = GetConnection()->HasFeature(FEATURE_SIZE);
|
|
|
|
wxString restr;
|
|
if (!GetServer()->GetDbRestriction().IsEmpty())
|
|
{
|
|
if (restr.IsEmpty())
|
|
restr = wxT(" WHERE db.datname IN (");
|
|
else
|
|
restr = wxT(" AND db.datname IN (");
|
|
|
|
restr += GetServer()->GetDbRestriction() + wxT(")\n");
|
|
}
|
|
|
|
wxString sql = wxT("SELECT db.datid, db.datname, numbackends, xact_commit, xact_rollback, blks_read, blks_hit");
|
|
|
|
if (GetConnection()->BackendMinimumVersion(8, 3))
|
|
sql += wxT(", tup_returned, tup_fetched, tup_inserted, tup_updated, tup_deleted");
|
|
if (GetConnection()->BackendMinimumVersion(9, 1))
|
|
sql += wxT(", stats_reset, slave.confl_tablespace, slave.confl_lock, slave.confl_snapshot, slave.confl_bufferpin, slave.confl_deadlock");
|
|
if (hasSize)
|
|
sql += wxT(", pg_size_pretty(pg_database_size(db.datid)) as size");
|
|
|
|
sql += wxT("\n FROM pg_stat_database db\n");
|
|
if (GetConnection()->BackendMinimumVersion(9, 1))
|
|
sql += wxT(" JOIN pg_stat_database_conflicts slave ON db.datid=slave.datid\n");
|
|
sql += restr + wxT(" ORDER BY db.datname");
|
|
|
|
// Add the statistics view columns
|
|
statistics->ClearAll();
|
|
statistics->AddColumn(_("Database"), 60);
|
|
statistics->AddColumn(_("Backends"), 50);
|
|
if (hasSize)
|
|
statistics->AddColumn(_("Size"), 60);
|
|
statistics->AddColumn(_("Xact Committed"), 60);
|
|
statistics->AddColumn(_("Xact Rolled Back"), 60);
|
|
statistics->AddColumn(_("Blocks Read"), 60);
|
|
statistics->AddColumn(_("Blocks Hit"), 60);
|
|
if (GetConnection()->BackendMinimumVersion(8, 3))
|
|
{
|
|
statistics->AddColumn(_("Tuples Returned"), 60);
|
|
statistics->AddColumn(_("Tuples Fetched"), 60);
|
|
statistics->AddColumn(_("Tuples Inserted"), 60);
|
|
statistics->AddColumn(_("Tuples Updated"), 60);
|
|
statistics->AddColumn(_("Tuples Deleted"), 60);
|
|
}
|
|
if (GetConnection()->BackendMinimumVersion(9, 1))
|
|
{
|
|
statistics->AddColumn(_("Last statistics reset"), 60);
|
|
statistics->AddColumn(_("Tablespace conflicts"), 60);
|
|
statistics->AddColumn(_("Lock conflicts"), 60);
|
|
statistics->AddColumn(_("Snapshot conflicts"), 60);
|
|
statistics->AddColumn(_("Bufferpin conflicts"), 60);
|
|
statistics->AddColumn(_("Deadlock conflicts"), 60);
|
|
}
|
|
|
|
bool sysobj;
|
|
pgSet *stats = GetServer()->ExecuteSet(sql);
|
|
if (stats)
|
|
{
|
|
while (!stats->Eof())
|
|
{
|
|
if (stats->GetVal(wxT("datname")) == wxT("template0"))
|
|
sysobj = true;
|
|
else
|
|
sysobj = stats->GetOid(wxT("datid")) <= GetServer()->GetLastSystemOID();
|
|
|
|
if (settings->GetShowSystemObjects() || !sysobj)
|
|
{
|
|
statistics->InsertItem(statistics->GetItemCount(), stats->GetVal(wxT("datname")), PGICON_STATISTICS);
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 1, stats->GetVal(wxT("numbackends")));
|
|
if (hasSize)
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 2, stats->GetVal(wxT("size")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 2 + (hasSize ? 1 : 0), stats->GetVal(wxT("xact_commit")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 3 + (hasSize ? 1 : 0), stats->GetVal(wxT("xact_rollback")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 4 + (hasSize ? 1 : 0), stats->GetVal(wxT("blks_read")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 5 + (hasSize ? 1 : 0), stats->GetVal(wxT("blks_hit")));
|
|
if (GetConnection()->BackendMinimumVersion(8, 3))
|
|
{
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 6 + (hasSize ? 1 : 0), stats->GetVal(wxT("tup_returned")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 7 + (hasSize ? 1 : 0), stats->GetVal(wxT("tup_fetched")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 8 + (hasSize ? 1 : 0), stats->GetVal(wxT("tup_inserted")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 9 + (hasSize ? 1 : 0), stats->GetVal(wxT("tup_updated")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 10 + (hasSize ? 1 : 0), stats->GetVal(wxT("tup_deleted")));
|
|
}
|
|
if (GetConnection()->BackendMinimumVersion(9, 1))
|
|
{
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 11 + (hasSize ? 1 : 0), stats->GetVal(wxT("stats_reset")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 12 + (hasSize ? 1 : 0), stats->GetVal(wxT("confl_tablespace")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 13 + (hasSize ? 1 : 0), stats->GetVal(wxT("confl_lock")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 14 + (hasSize ? 1 : 0), stats->GetVal(wxT("confl_snapshot")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 15 + (hasSize ? 1 : 0), stats->GetVal(wxT("confl_bufferpin")));
|
|
statistics->SetItem(statistics->GetItemCount() - 1, 16 + (hasSize ? 1 : 0), stats->GetVal(wxT("confl_deadlock")));
|
|
}
|
|
}
|
|
stats->MoveNext();
|
|
}
|
|
|
|
delete stats;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////
|
|
|
|
pgDatabaseObjCollection::pgDatabaseObjCollection(pgaFactory *factory, pgDatabase *db)
|
|
: pgCollection(factory)
|
|
{
|
|
database = db;
|
|
server = database->GetServer();
|
|
}
|
|
|
|
|
|
bool pgDatabaseObjCollection::CanCreate()
|
|
{
|
|
if (IsCollectionForType(PGM_CATALOG))
|
|
return false;
|
|
|
|
return GetDatabase()->GetCreatePrivilege();
|
|
}
|
|
|
|
|
|
#include "images/database.pngc"
|
|
#include "images/database-sm.pngc"
|
|
#include "images/databases.pngc"
|
|
#include "images/closeddatabase.pngc"
|
|
#include "images/closeddatabase-sm.pngc"
|
|
|
|
pgDatabaseFactory::pgDatabaseFactory()
|
|
: pgServerObjFactory(__("Database"), __("New Database..."), __("Create a new Database."), database_png_img, database_sm_png_img)
|
|
{
|
|
metaType = PGM_DATABASE;
|
|
closedId = addIcon(closeddatabase_png_img);
|
|
smallClosedId = addIcon(closeddatabase_sm_png_img);
|
|
}
|
|
|
|
pgCollection *pgDatabaseFactory::CreateCollection(pgObject *obj)
|
|
{
|
|
return new pgDatabaseCollection(GetCollectionFactory(), (pgServer *)obj);
|
|
}
|
|
|
|
pgCollection *pgDatabaseObjFactory::CreateCollection(pgObject *obj)
|
|
{
|
|
return new pgDatabaseObjCollection(GetCollectionFactory(), (pgDatabase *)obj);
|
|
}
|
|
|
|
|
|
pgDatabaseFactory databaseFactory;
|
|
static pgaCollectionFactory cf(&databaseFactory, __("Databases"), databases_png_img);
|
|
|
|
|
|
disconnectDatabaseFactory::disconnectDatabaseFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
|
|
{
|
|
mnu->Append(id, _("Disconnec&t database"), _("Disconnect from the selected database."));
|
|
}
|
|
|
|
|
|
wxWindow *disconnectDatabaseFactory::StartDialog(frmMain *form, pgObject *obj)
|
|
{
|
|
ctlTree *browser = form->GetBrowser();
|
|
pgDatabase *database = (pgDatabase *)obj;
|
|
|
|
if (obj->CheckOpenDialogs(browser, browser->GetSelection()))
|
|
{
|
|
wxString msg = _("There are properties dialogues open for one or more objects that would be refreshed. Please close the properties dialogues and try again.");
|
|
wxMessageBox(msg, _("Cannot disconnect database"), wxICON_WARNING | wxOK);
|
|
}
|
|
else
|
|
{
|
|
database->Disconnect();
|
|
database->UpdateIcon(browser);
|
|
browser->DeleteChildren(obj->GetId());
|
|
browser->SelectItem(browser->GetItemParent(obj->GetId()));
|
|
form->execSelChange(browser->GetItemParent(obj->GetId()), true);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool disconnectDatabaseFactory::CheckEnable(pgObject *obj)
|
|
{
|
|
if (obj && obj->IsCreatedBy(databaseFactory))
|
|
return ((pgDatabase *)obj)->GetConnected() && (((pgDatabase *)obj)->GetName() != ((pgDatabase *)obj)->GetServer()->GetDatabaseName());
|
|
|
|
return false;
|
|
}
|