pgadmin3/schema/pgServer.cpp
lsv 8ffa0a1806 Added a parameter for the Keywords server. Used to search for the server after pressing F4
Для удобного поиска серверов к параметрам сервера добавлен Keywords параметр.
Это текстовый параметр в котором через пробел можно перечислить слова по которым будет проводиться
поиск сервера при нажатии F4.
Тут можно например указать IP сервера для соединения использующего туннель putty.
GUI интерфейса для настройки параметра нет.
Если ключевые слова заданы то отображаются в [].
2026-04-22 15:50:33 +05:00

2365 lines
No EOL
66 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// pgServer.cpp - PostgreSQL Server
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/busyinfo.h>
#include <wx/dir.h>
#include <wx/fileconf.h>
#include <wx/wfstream.h>
// App headers
#include "ctl/ctlMenuToolbar.h"
#include "frm/menu.h"
#include "utils/misc.h"
#include "frm/frmMain.h"
#include "frm/frmHint.h"
#include "dlg/dlgConnect.h"
#include "schema/pgDatabase.h"
#include "schema/pgTablespace.h"
#include "schema/pgGroup.h"
#include "schema/pgUser.h"
#include "schema/pgRole.h"
#include "schema/gpResQueue.h"
#include "agent/pgaJob.h"
#include "utils/utffile.h"
#include "utils/pgfeatures.h"
#include "utils/registry.h"
#include "frm/frmReport.h"
#include "dlg/dlgServer.h"
#include "schema/edbResourceGroup.h"
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
#include "utils/sshTunnel.h"
#endif
#define DEFAULT_PG_DATABASE wxT("postgres")
pgServer::pgServer(const wxString &newName, const wxString &newHostAddr, const wxString &newDescription, const wxString &newService,
const wxString &newDatabase, const wxString &newUsername, int newPort, bool _storePwd, const wxString &newRolename, const wxString& newConnStr, bool _restore,
int _ssl, const wxString &_colour, const wxString &_group, bool _sshTunnel, const wxString &newTunnelHost, const wxString &newTunnelUserName,
bool _authModePwd, const wxString &newTunnelPassword, const wxString &newPublicKey, const wxString &newIdentity, const int &sshPort)
: pgObject(serverFactory, newName)
{
description = newDescription;
hostaddr = newHostAddr;
service = newService;
database = newDatabase;
username = newUsername;
port = newPort;
ssl = _ssl;
colour = _colour;
group = _group;
serverIndex = 0;
connected = false;
lastSystemOID = 0;
conn = NULL;
passwordValid = true;
storePwd = _storePwd;
rolename = newRolename;
connstr = newConnStr;
restore = _restore;
superUser = false;
createPrivilege = false;
sshTunnel = _sshTunnel;
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
// SSH Tunnel
tunnelObj = NULL;
tunnelHost = newTunnelHost;
tunnelUserName = newTunnelUserName;
authModePwd = _authModePwd;
tunnelPassword = newTunnelPassword;
publicKeyFile = newPublicKey;
identityFile = newIdentity;
tunnelPort = sshPort;
#endif
#ifdef WIN32
scmHandle = 0;
serviceHandle = 0;
#endif
}
pgServer::~pgServer()
{
if (conn)
delete conn;
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
if(tunnelObj)
{
if(tunnelObj->IsAlive())
{
tunnelObj->Cleanup();
}
tunnelObj = NULL;
}
#endif
#ifdef WIN32
if (serviceHandle)
CloseServiceHandle(serviceHandle);
if (scmHandle)
CloseServiceHandle(scmHandle);
#endif
}
wxString pgServer::GetTranslatedMessage(int kindOfMessage) const
{
wxString message = wxEmptyString;
switch (kindOfMessage)
{
case RETRIEVINGDETAILS:
message = _("Retrieving details on server");
message += wxT(" ") + GetName();
break;
case REFRESHINGDETAILS:
message = _("Refreshing server");
message += wxT(" ") + GetName();
break;
case BACKUPGLOBALS:
message = _("Backup globals of server");
message += wxT(" ") + GetName();
break;
case BACKUPSERVERTITLE:
message = _("Backup server");
message += wxT(" ") + GetName();
break;
case DROPTITLE:
message = _("Drop server?");
break;
case PROPERTIESREPORT:
message = _("Server properties report");
message += wxT(" - ") + GetName();
break;
case PROPERTIES:
message = _("Server properties");
break;
case STATISTICSREPORT:
message = _("Server statistics report");
message += wxT(" - ") + GetName();
break;
case OBJSTATISTICS:
message = _("Server statistics");
break;
}
return message;
}
int pgServer::GetIconId()
{
if (GetConnected())
return serverFactory.GetIconId();
else
return serverFactory.GetClosedIconId();
}
wxMenu *pgServer::GetNewMenu()
{
wxMenu *menu = 0;
if (connected && (GetSuperUser() || GetCreateRole()))
{
menu = new wxMenu();
if (settings->GetDisplayOption(_("Tablespaces")))
tablespaceFactory.AppendMenu(menu);
if (GetConnection()->BackendMinimumVersion(8, 1))
{
if (settings->GetDisplayOption(_("Groups/group Roles")))
groupRoleFactory.AppendMenu(menu);
if (settings->GetDisplayOption(_("Users/login Roles")))
loginRoleFactory.AppendMenu(menu);
if (GetConnection()->GetIsGreenplum())
{
if (settings->GetDisplayOption(_("Resource Queues")))
resQueueFactory.AppendMenu(menu);
}
}
else
{
if (settings->GetDisplayOption(_("Groups/group Roles")))
groupFactory.AppendMenu(menu);
if (settings->GetDisplayOption(_("Users/login Roles")))
userFactory.AppendMenu(menu);
}
// Added Resource Group only for PPAS 9.4 and above
if (conn->GetIsEdb() && conn->EdbMinimumVersion(9, 4))
{
if (settings->GetDisplayOption(_("Resource Groups")))
resourceGroupFactory.AppendMenu(menu);
}
}
return menu;
}
pgServer *pgServer::GetServer() const
{
if (connected)
return (pgServer *)this;
return 0;
}
pgConn *pgServer::CreateConn(wxString dbName, OID oid, wxString applicationname)
{
if (!connected)
return 0;
if (dbName.IsEmpty())
{
dbName = GetDatabaseName();
oid = dbOid;
}
pgConn *conn = NULL;
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
if(sshTunnel)
{
conn = new pgConn(local_listenhost, service, hostaddr, dbName, username, password, local_listenport, rolename,connstr, ssl, oid, applicationname, sslcert, sslkey, sslrootcert, sslcrl, sslcompression);
}
else
#endif
{
conn = new pgConn(GetName(), service, hostaddr, dbName, username, password, port, rolename,connstr, ssl, oid, applicationname, sslcert, sslkey, sslrootcert, sslcrl, sslcompression);
}
if (conn && conn->GetStatus() != PGCONN_OK)
{
wxLogError(wxT("%s"), conn->GetLastError().c_str());
delete conn;
return 0;
}
return conn;
}
wxString pgServer::GetFullName()
{
if (GetDescription().Length() > 0)
return GetDescription() + wxT(" (") + GetIdentifier() + wxT(")");
else
return wxT("(") + GetIdentifier() + wxT(")");
}
wxString pgServer::GetFullIdentifier()
{
return GetFullName();
}
bool pgServer::Disconnect(frmMain *form)
{
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
if(tunnelObj)
{
if(tunnelObj->IsAlive())
{
tunnelObj->Cleanup();
}
tunnelObj = NULL;
}
#endif
if (conn)
{
delete conn;
conn = 0;
connected = false;
expandedKids = false;
ver = wxT("");
versionNum = wxT("");
lastSystemOID = 0;
}
if (form)
UpdateIcon(form->GetBrowser());
return true;
}
bool pgServer::GetCanHint()
{
return connected && conn->BackendMinimumVersion(8, 1) && !autovacuumRunning;
}
void pgServer::ShowHint(frmMain *form, bool force)
{
wxArrayString hints;
if (!autovacuumRunning)
hints.Add(HINT_AUTOVACUUM);
if (force || !hintShown)
frmHint::ShowHint(form, hints, GetFullIdentifier(), force);
hintShown = true;
}
#define SERVICEBUFSIZE 10000
#define QUERYBUFSIZE 256
#ifdef WIN32
wxArrayString pgServer::GetDependentServices(SC_HANDLE handle)
{
wxArrayString services;
LPENUM_SERVICE_STATUS sbuf = (LPENUM_SERVICE_STATUS) new char[SERVICEBUFSIZE];
DWORD servicesReturned = 0, bytesNeeded;
::EnumDependentServices(handle, SERVICE_STATE_ALL, sbuf, SERVICEBUFSIZE, &bytesNeeded, &servicesReturned);
DWORD i;
for (i = 0 ; i < servicesReturned ; i++)
{
SC_HANDLE h =::OpenService(scmHandle, sbuf[i].lpServiceName, SERVICE_QUERY_CONFIG);
if (h)
{
char buffer[QUERYBUFSIZE];
LPQUERY_SERVICE_CONFIG qsc = (LPQUERY_SERVICE_CONFIG)buffer;
if(::QueryServiceConfig(h, qsc, QUERYBUFSIZE, &bytesNeeded))
{
if (qsc->dwStartType != SERVICE_DISABLED)
services.Add(sbuf[i].lpServiceName);
}
::CloseServiceHandle(h);
}
}
delete[] sbuf;
return services;
}
#endif
bool pgServer::StartService()
{
bool done = false;
#ifdef WIN32
if (serviceHandle)
{
done = (::StartService(serviceHandle, 0, 0) != 0);
if (!done)
{
DWORD rc = ::GetLastError();
if (rc == ERROR_SERVICE_ALREADY_RUNNING)
{
GetServerRunning();
return true;
}
// report error
wxLogError(__("Failed to start server %s: Errcode=%d\nCheck event log for details."),
serviceId.c_str(), rc);
}
else
{
GetServerRunning(); // ignore result, just to wait for startup
wxArrayString services = GetDependentServices(serviceHandle);
if (services.GetCount() > 0)
{
size_t i;
wxString serviceString;
for (i = 0 ; i < services.GetCount() ; i++)
serviceString += wxT(" ") + services.Item(i) + wxT("\n");
wxMessageDialog msg(0, _("There are dependent services configured:\n\n")
+ serviceString + _("\nStart dependent services too?"), _("Dependent services"),
wxICON_EXCLAMATION | wxYES_NO | wxYES_DEFAULT);
if (msg.ShowModal() == wxID_YES)
{
for (i = 0 ; i < services.GetCount() ; i++)
{
SC_HANDLE h =::OpenService(scmHandle, services.Item(i), GENERIC_EXECUTE | GENERIC_READ);
if (h)
{
if (!::StartService(h, 0, 0))
done = false;
CloseServiceHandle(h);
}
else
done = false;
}
if (!done)
{
wxMessageDialog msg(0, _("One or more dependent services didn't start; see the eventlog for details."), _("Service start problem"),
wxICON_EXCLAMATION | wxOK);
msg.ShowModal();
done = true;
}
}
}
}
}
#else
wxString res = ExecProcess(serviceId + wxT(" start"));
done = (res.Find(wxT("tarting")) > 0);
#endif
return done;
}
bool pgServer::StopService()
{
bool done = false;
#ifdef WIN32
if (serviceHandle)
{
SERVICE_STATUS st;
done = (::ControlService(serviceHandle, SERVICE_CONTROL_STOP, &st) != 0);
if (!done)
{
if (::GetLastError() == ERROR_DEPENDENT_SERVICES_RUNNING)
{
LPENUM_SERVICE_STATUS sbuf = (LPENUM_SERVICE_STATUS) new char[SERVICEBUFSIZE];
DWORD bytesNeeded, servicesReturned = 0;
::EnumDependentServices(serviceHandle, SERVICE_ACTIVE, sbuf, SERVICEBUFSIZE, &bytesNeeded, &servicesReturned);
done = true;
if (servicesReturned)
{
DWORD i;
wxString services;
for (i = 0 ; i < servicesReturned ; i++)
services += wxT(" ") + wxString(sbuf[i].lpDisplayName) + wxT("\n");
wxMessageDialog msg(0, _("There are dependent services running:\n\n")
+ services + _("\nStop dependent services?"), _("Dependent services"),
wxICON_EXCLAMATION | wxYES_NO | wxYES_DEFAULT);
if (msg.ShowModal() != wxID_YES)
return false;
for (i = 0 ; done && i < servicesReturned ; i++)
{
SC_HANDLE h =::OpenService(scmHandle, sbuf[i].lpServiceName, GENERIC_EXECUTE | GENERIC_READ);
if (h)
{
done = (::ControlService(h, SERVICE_CONTROL_STOP, &st) != 0);
CloseServiceHandle(h);
}
else
done = false;
}
if (done)
{
done = (::ControlService(serviceHandle, SERVICE_CONTROL_STOP, &st) != 0);
int retries = 10;
while (!done && retries > 0)
{
done = (::ControlService(serviceHandle, SERVICE_CONTROL_STOP, &st) != 0);
retries--;
wxSleep(5);
}
}
}
}
// report error
if (!done)
wxLogError(__("Failed to stop server %s: Errcode=%d\nCheck event log for details."),
serviceId.c_str(), ::GetLastError());
}
}
#else
wxString res = ExecProcess(serviceId + wxT(" stop"));
done = (res.Find(wxT("stopped")) > 0);
#endif
return done;
}
bool pgServer::GetServerRunning()
{
bool done = false;
#ifdef WIN32
if (serviceHandle)
{
SERVICE_STATUS st;
int loops;
for (loops = 0 ; loops < 20 ; loops++)
{
if (::QueryServiceStatus(serviceHandle, &st) == 0)
{
DWORD rc = ::GetLastError();
CloseServiceHandle(serviceHandle);
CloseServiceHandle(scmHandle);
serviceHandle = 0;
scmHandle = 0;
return false;
}
done = (st.dwCurrentState == SERVICE_RUNNING);
if (st.dwCurrentState == SERVICE_START_PENDING)
Sleep(100);
else
break;
}
}
#else
wxString res = ExecProcess(serviceId + wxT(" status"));
done = (res.Find(wxT("PID: ")) > 0);
#endif
return done;
}
void pgServer::iSetServiceID(const wxString &s)
{
serviceId = s;
#ifdef WIN32
if (serviceId.Find('\\') < 0)
scmHandle = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, GENERIC_EXECUTE);
else
scmHandle = OpenSCManager(wxT("\\\\") + serviceId.BeforeFirst('\\'), SERVICES_ACTIVE_DATABASE, GENERIC_EXECUTE | GENERIC_READ);
if (scmHandle)
serviceHandle = OpenService(scmHandle, serviceId.AfterLast('\\'), GENERIC_EXECUTE | GENERIC_READ);
#endif
}
bool pgServer::GetServerControllable()
{
#ifdef WIN32
return serviceHandle != 0;
#else
return !serviceId.IsEmpty();
#endif
}
wxString pgServer::passwordFilename()
{
wxString fname = sysSettings::GetConfigFile(sysSettings::PGPASS);
wxLogInfo(wxT("Using password file %s"), fname.c_str());
return fname;
}
bool pgServer::GetPasswordIsStored()
{
wxString fname = passwordFilename();
if (!wxFile::Exists(fname))
return false;
wxUtfFile file(fname, wxFile::read, wxFONTENCODING_SYSTEM);
if (file.IsOpened())
{
wxString before;
file.Read(before);
wxStringTokenizer lines(before, wxT("\n\r"));
wxString seekStr = GetName() + wxT(":")
+ NumToStr((long)GetPort()) + wxT(":*:")
+ username + wxT(":") ;
wxString seekStr2 = wxString(GetName().mb_str(wxConvUTF8), wxConvLibc) + wxT(":")
+ NumToStr((long)GetPort()) + wxT(":*:")
+ wxString(username.mb_str(wxConvUTF8), wxConvLibc) + wxT(":") ;
while (lines.HasMoreTokens())
{
wxString str = lines.GetNextToken();
if (str.Left(seekStr.Length()) == seekStr)
return true;
if (str.Left(seekStr2.Length()) == seekStr2)
return true;
}
}
return false;
}
void pgServer::StorePassword()
{
wxString fname = passwordFilename();
if (!wxFile::Exists(fname))
{
return;
}
wxUtfFile file;
// Don't try to read and write in one OP - it doesn't work well
wxString before;
file.Open(fname, wxFile::read, wxFONTENCODING_SYSTEM);
file.Read(before);
file.Close();
file.Open(fname, wxFile::write, wxFONTENCODING_SYSTEM);
if (file.IsOpened())
{
wxString after;
wxString passwd;
wxString seekStr;
if (GetConnection()->GetNeedUtfConnectString())
{
passwd = wxString(password.mb_str(wxConvUTF8), wxConvLibc);
seekStr = wxString(GetName().mb_str(wxConvUTF8), wxConvLibc) + wxT(":")
+ NumToStr((long)GetPort()) + wxT(":*:")
+ wxString(username.mb_str(wxConvUTF8), wxConvLibc) + wxT(":") ;
}
else
{
passwd = password;
seekStr = GetName() + wxT(":")
+ NumToStr((long)GetPort()) + wxT(":*:")
+ username + wxT(":") ;
}
// Escape ":" and "\" from the password field
if (!passwd.IsEmpty())
{
passwd.Replace(wxT("\\"), wxT("\\\\"));
passwd.Replace(wxT(":") , wxT("\\:"));
}
file.Read(before);
wxStringTokenizer lines(before, wxT("\n\r"));
file.Seek(0);
bool found = false;
while (lines.HasMoreTokens())
{
wxString str = lines.GetNextToken();
if (str.Left(seekStr.Length()) == seekStr && !passwd.IsEmpty())
{
// entry found
found = true;
if (storePwd)
file.Write(seekStr + passwd + END_OF_LINE);
}
else
file.Write(str + END_OF_LINE);
}
if (!found && storePwd && !passwd.IsEmpty())
file.Write(seekStr + passwd + END_OF_LINE);
file.Close();
}
}
int pgServer::Connect(frmMain *form, bool askPassword, const wxString &pwd, bool forceStorePassword, bool askTunnelPassword)
{
wxLogInfo(wxT("Attempting to create a connection object..."));
bool storePassword = false;
if (!conn || conn->GetStatus() != PGCONN_OK)
{
if (conn)
{
delete conn;
conn = 0;
}
if (askPassword)
{
if ((sshTunnel || !passwordValid || !GetPasswordIsStored() || !GetStorePwd()) && GetSSLCert() == wxEmptyString)
{
wxString txt;
txt.Printf(_("Please enter password for user %s\non server %s (%s)"), username.c_str(), description.c_str(), GetName().c_str());
dlgConnect *dlg = NULL;
// if sshTunnel is true then we have to hide 'Stored Password' option
if(sshTunnel)
dlg = new dlgConnect(NULL, txt, false);
else
dlg = new dlgConnect(form, txt, GetStorePwd());
dlg->SetWindowStyleFlag( dlg->GetWindowStyleFlag() | wxSTAY_ON_TOP);
switch (dlg->Go())
{
case wxID_OK:
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(100);
wxSafeYield();
break;
case wxID_CANCEL:
case -1:
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(100);
wxSafeYield();
return PGCONN_ABORTED;
default:
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(100);
wxSafeYield();
wxLogError(__("Couldn't create a connection dialogue!"));
return PGCONN_BAD;
}
iSetStorePwd(dlg->GetStorePwd());
password = dlg->GetPassword();
storePassword = true;
if(dlg)
{
delete dlg;
dlg = NULL;
}
}
}
else
iSetPassword(pwd);
form->StartMsg(_("Connecting to database"));
wxString host;
int iPort;
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
if(sshTunnel)
{
//Ask Tunnel Password
if(askTunnelPassword)
{
wxString txt;
if(GetAuthModePwd())
{
txt.Printf(_("Please enter the SSH tunnel password for user %s\non server %s"), tunnelUserName.c_str(), tunnelHost.c_str());
}
else
{
txt.Printf(_("Please enter the pass phrase for the identity file\n%s"), identityFile.c_str());
}
dlgConnect dlg(NULL, txt, false);
switch (dlg.Go())
{
case wxID_OK:
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(100);
wxSafeYield();
tunnelPassword = dlg.GetPassword();
break;
case wxID_CANCEL:
case -1:
default:
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(100);
wxSafeYield();
form->EndMsg(false);
return PGCONN_ABORTED;
}
}
// Create SSH Tunnel if required
if(!tunnelObj)
{
if(!createSSHTunnel())
{
form->EndMsg(false);
return PGCONN_SSHTUNNEL_ERROR;
}
}
host = local_listenhost;
iPort = local_listenport;
}
else
#endif
{
host = GetName();
iPort = port;
}
wxString usr=wxEmptyString;
#ifndef __WXMSW__
wxGetEnv(wxT("USER"), &usr);
#else
wxGetEnv(wxT("USERNAME"), &usr);
#endif
if (database.IsEmpty())
{
conn = new pgConn(host, service, hostaddr, DEFAULT_PG_DATABASE, username, password, iPort, rolename,connstr, ssl, 0, appearanceFactory->GetLongAppName() +wxT(" - ")+ usr, sslcert, sslkey, sslrootcert, sslcrl, sslcompression);
if (conn->GetStatus() == PGCONN_OK)
database = DEFAULT_PG_DATABASE;
else if (conn->GetStatus() == PGCONN_BAD && conn->GetLastError().Find(
wxT("database \"") DEFAULT_PG_DATABASE wxT("\" does not exist")) >= 0)
{
delete conn;
conn = new pgConn(host, service, hostaddr, wxT("template1"), username, password, iPort, rolename,connstr, ssl, 0, appearanceFactory->GetLongAppName() + wxT(" - Browser"), sslcert, sslkey, sslrootcert, sslcrl, sslcompression);
if (conn && conn->GetStatus() == PGCONN_OK)
database = wxT("template1");
}
}
else
{
conn = new pgConn(host, service, hostaddr, database, username, password, iPort, rolename,connstr, ssl, 0, appearanceFactory->GetLongAppName() + " "+usr, sslcert, sslkey, sslrootcert, sslcrl, sslcompression);
if (!conn)
{
form->EndMsg(false);
wxLogError(__("Couldn't create a connection object!"));
return PGCONN_BAD;
}
}
}
int status = conn->GetStatus();
if (status == PGCONN_OK)
{
dbOid = conn->GetDbOid();
// Check the server version
if (conn->GetIsGreenplum())
{
// Greenplum HAWQ (SQL on Hadoop) is not supported by this pgAdmin version
if (conn->GetIsHawq())
{
wxLogWarning(_("The server you are connecting to is not a version that is supported by this release of %s.\n\n%s may not function as expected."),
appearanceFactory->GetLongAppName().c_str(),
appearanceFactory->GetLongAppName().c_str());
}
else
{
// Check for Greenplum specific version
// Greenplum always shows PG version "8.2.15" for now
// this might change once the merge with recent PG versions makes progress
// therefore also check for the max version
if (!(conn->BackendMinimumVersion(GP_MIN_VERSION_N >> 8, GP_MIN_VERSION_N & 0x00FF)) ||
(conn->BackendMinimumVersion(GP_MAX_VERSION_N >> 8, (GP_MAX_VERSION_N & 0x00FF) + 1)))
{
if (GP_MIN_VERSION_N == GP_MAX_VERSION_N)
{
wxLogWarning(_("The server you are connecting to is not a version that is supported by this release of %s.\n\n%s may not function as expected.\n\nSupported server version is %s."),
appearanceFactory->GetLongAppName().c_str(),
appearanceFactory->GetLongAppName().c_str(),
wxString(GP_MIN_VERSION_T).c_str());
}
else
{
wxLogWarning(_("The server you are connecting to is not a version that is supported by this release of %s.\n\n%s may not function as expected.\n\nSupported server versions are %s to %s."),
appearanceFactory->GetLongAppName().c_str(),
appearanceFactory->GetLongAppName().c_str(),
wxString(GP_MIN_VERSION_T).c_str(),
wxString(GP_MAX_VERSION_T).c_str());
}
}
}
}
else
{
if (!(conn->BackendMinimumVersion(SERVER_MIN_VERSION_N >> 8, SERVER_MIN_VERSION_N & 0x00FF)) ||
(conn->BackendMinimumVersion(SERVER_MAX_VERSION_N >> 8, (SERVER_MAX_VERSION_N & 0x00FF) + 1))) {
wxLogWarning(_("The server you are connecting to is not a version that is supported by this release of %s.\n\n%s may not function as expected.\n\nSupported server versions are %s to %s."),
appearanceFactory->GetLongAppName().c_str(),
appearanceFactory->GetLongAppName().c_str(),
wxString(SERVER_MIN_VERSION_T).c_str(),
wxString(SERVER_MAX_VERSION_T).c_str());
}
}
connected = true;
bool hasUptime = false;
wxString sql = wxT("SELECT usecreatedb, usesuper");
if (conn->BackendMinimumVersion(8, 1))
{
hasUptime = true;
sql += wxT(", pg_postmaster_start_time() as upsince");
}
else if (conn->HasFeature(FEATURE_POSTMASTER_STARTTIME))
{
hasUptime = true;
sql += wxT(", CASE WHEN usesuper THEN pg_postmaster_starttime() ELSE NULL END as upsince");
}
if (conn->BackendMinimumVersion(8, 4))
{
sql += wxT(", pg_conf_load_time() as confloadedsince");
}
if (conn->BackendMinimumVersion(8, 5))
{
sql += wxT(", CASE WHEN usesuper THEN pg_is_in_recovery() ELSE NULL END as inrecovery");
if (conn->BackendMinimumVersion(10, 0))
{
sql += wxT(", CASE WHEN usesuper THEN pg_last_wal_receive_lsn() ELSE NULL END as receiveloc");
sql += wxT(", CASE WHEN usesuper THEN pg_last_wal_replay_lsn() ELSE NULL END as replayloc");
}
else
{
sql += wxT(", CASE WHEN usesuper THEN pg_last_xlog_receive_location() ELSE NULL END as receiveloc");
sql += wxT(", CASE WHEN usesuper THEN pg_last_xlog_replay_location() ELSE NULL END as replayloc");
}
}
if (conn->BackendMinimumVersion(9, 1))
{
sql += wxT(", CASE WHEN usesuper THEN pg_last_xact_replay_timestamp() ELSE NULL END as replay_timestamp");
if (conn->BackendMinimumVersion(10, 0))
sql += wxT(", CASE WHEN usesuper AND pg_is_in_recovery() THEN pg_is_wal_replay_paused() ELSE NULL END as isreplaypaused");
else
sql += wxT(", CASE WHEN usesuper AND pg_is_in_recovery() THEN pg_is_xlog_replay_paused() ELSE NULL END as isreplaypaused");
}
pgSet *set = ExecuteSet(sql + wxT("\n FROM pg_user WHERE usename=current_user"));
if (set)
{
iSetCreatePrivilege(set->GetBool(wxT("usecreatedb")));
iSetSuperUser(set->GetBool(wxT("usesuper")));
if (hasUptime)
iSetUpSince(set->GetDateTime(wxT("upsince")));
if (conn->BackendMinimumVersion(8, 4))
iSetConfLoadedSince(set->GetDateTime(wxT("confloadedsince")));
if (conn->BackendMinimumVersion(8, 5))
{
iSetInRecovery(set->GetBool(wxT("inrecovery")));
iSetReplayLoc(set->GetVal(wxT("replayloc")));
iSetReceiveLoc(set->GetVal(wxT("receiveloc")));
}
if (conn->BackendMinimumVersion(9, 1))
{
iSetReplayTimestamp(set->GetVal(wxT("replay_timestamp")));
SetReplayPaused(set->GetBool(wxT("isreplaypaused")));
}
delete set;
}
if (conn->BackendMinimumVersion(8, 1))
{
set = ExecuteSet(wxT("SELECT rolcreaterole, rolcreatedb FROM pg_roles WHERE rolname = current_user;"));
if (set)
{
iSetCreatePrivilege(set->GetBool(wxT("rolcreatedb")));
iSetCreateRole(set->GetBool(wxT("rolcreaterole")));
delete set;
}
}
else
iSetCreateRole(false);
wxString version, allVersions;
version.Printf(wxT("%d.%d"), conn->GetMajorVersion(), conn->GetMinorVersion());
allVersions = settings->Read(wxT("Updates/pgsql-Versions"), wxEmptyString);
if (allVersions.Find(version) < 0)
{
if (!allVersions.IsEmpty())
allVersions += wxT(", ");
allVersions += version;
settings->Write(wxT("Updates/pgsql-Versions"), allVersions);
}
if (conn->IsSSLconnected())
settings->WriteBool(wxT("Updates/UseSSL"), true);
UpdateIcon(form->GetBrowser());
if (storePassword || forceStorePassword)
StorePassword();
}
else
{
connected = false;
}
form->EndMsg(connected && status == PGCONN_OK);
passwordValid = connected;
return status;
}
wxString pgServer::GetIdentifier() const
{
wxString idstr;
if (GetService().IsEmpty())
{
if (GetName().IsEmpty())
idstr.Printf(wxT("local:.s.PGSQL.%d"), port);
else if (GetName().StartsWith(wxT("/")))
idstr.Printf(wxT("local:%s/.s.PGSQL.%d"), GetName().c_str(), port);
else
idstr.Printf(wxT("%s:%d"), GetName().c_str(), port);
}
else
idstr.Printf(_("service %s"), GetService().c_str());
return idstr;
}
wxString pgServer::GetVersionString()
{
if (connected)
{
if (ver.IsEmpty())
ver = wxString(conn->GetVersionString());
return ver;
}
else
return wxEmptyString;
}
wxString pgServer::GetVersionNumber()
{
if (connected)
{
if (versionNum.IsEmpty())
{
int major = 0, minor = 0;
sscanf(GetVersionString().ToAscii(), "%*s %d.%d", &major, &minor);
versionNum.Printf(wxT("%d.%d"), major, minor);
}
}
return versionNum;
}
OID pgServer::GetLastSystemOID()
{
if (connected)
{
if (lastSystemOID == 0)
lastSystemOID = conn->GetLastSystemOID();
return lastSystemOID;
}
else
return 0;
}
bool pgServer::SetPassword(const wxString &newVal)
{
wxString sql;
sql.Printf(wxT("ALTER USER %s WITH ENCRYPTED PASSWORD %s;"), qtIdent(username).c_str(), qtDbString(conn->EncryptPassword(username, newVal)).c_str());
bool executed = conn->ExecuteVoid(sql);
if (executed)
{
password = newVal;
StorePassword();
return true;
}
else
return false;
}
wxString pgServer::GetLastError() const
{
wxString msg;
if (conn)
{
if (error != wxT(""))
{
if (conn->GetLastError() != wxT(""))
{
msg.Printf(wxT("%s\n%s"), error.c_str(), conn->GetLastError().c_str());
}
else
{
msg = error;
}
}
else
{
msg = conn->GetLastError();
}
}
return msg;
}
void pgServer::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
{
// Add child nodes if necessary
if (GetConnected())
{
// Reset password menu option
// form->fileMenu->Enable(MNU_PASSWORD, true);
if (!expandedKids)
{
expandedKids = true;
// Log
wxLogInfo(wxT("Adding child object to server %s"), GetIdentifier().c_str());
if (settings->GetDisplayOption(_("Databases")))
browser->AppendCollection(this, databaseFactory);
if (conn->BackendMinimumVersion(8, 0) && settings->GetDisplayOption(_("Tablespaces")))
browser->AppendCollection(this, tablespaceFactory);
// Jobs
// We only add the Jobs node if the appropriate objects are the initial DB.
if (settings->GetDisplayOption(_("pgAgent Jobs")))
{
wxString exists = conn->ExecuteScalar(
wxT("SELECT cl.oid FROM pg_class cl JOIN pg_namespace ns ON ns.oid=relnamespace\n")
wxT(" WHERE relname='pga_job' AND nspname='pgagent'"));
if (!exists.IsNull())
{
exists = conn->ExecuteScalar(wxT("SELECT has_schema_privilege('pgagent', 'USAGE')"));
if (exists == wxT("t"))
browser->AppendCollection(this, jobFactory);
}
}
if (conn->BackendMinimumVersion(8, 1))
{
if (settings->GetDisplayOption(_("Groups/group Roles")))
browser->AppendCollection(this, groupRoleFactory);
if (settings->GetDisplayOption(_("Users/login Roles")))
browser->AppendCollection(this, loginRoleFactory);
if (GetConnection()->GetIsGreenplum())
{
if (settings->GetDisplayOption(_("Resource Queues")))
browser->AppendCollection(this, resQueueFactory);
}
}
else
{
if (settings->GetDisplayOption(_("Groups/group Roles")))
browser->AppendCollection(this, groupFactory);
if (settings->GetDisplayOption(_("Users/login Roles")))
browser->AppendCollection(this, userFactory);
}
// Added Resource Group only for PPAS 9.4 and above
if (conn->GetIsEdb() && conn->EdbMinimumVersion(9, 4))
{
if (settings->GetDisplayOption(_("Resource Groups")))
browser->AppendCollection(this, resourceGroupFactory);
}
autovacuumRunning = true;
wxString qry;
if (conn->BackendMinimumVersion(8, 3))
qry = wxT("SELECT setting FROM pg_settings WHERE name IN ('autovacuum', 'track_counts')");
else
qry = wxT("SELECT setting FROM pg_settings WHERE name IN ('autovacuum', 'stats_start_collector', 'stats_row_level')");
pgSetIterator set(conn, qry);
while (autovacuumRunning && set.RowsLeft())
autovacuumRunning = set.GetBool(wxT("setting"));
}
}
if (properties)
{
// Add the properties view columns
CreateListColumns(properties);
// Display the Server properties
properties->AppendItem(_("Description"), GetDescription());
properties->AppendItem(_("Service"), GetService());
if (GetName().IsEmpty() || GetName().StartsWith(wxT("/")))
{
if (GetName().IsEmpty() && !GetService().IsEmpty())
properties->AppendItem(_("Hostname"), wxEmptyString);
else
properties->AppendItem(_("Hostname"), wxT("local:") + GetName());
if (GetPort() == 0 && !GetService().IsEmpty())
properties->AppendItem(_("Port"), wxEmptyString);
else
properties->AppendItem(_("Port"), (long)GetPort());
}
else
{
properties->AppendItem(_("Hostname"), GetName());
properties->AppendItem(_("Host Address"), GetHostAddr());
if (GetPort() == 0 && !GetService().IsEmpty())
properties->AppendItem(_("Port"), wxEmptyString);
else
properties->AppendItem(_("Port"), (long)GetPort());
#ifdef PG_SSL
if (GetConnected())
{
properties->AppendItem(_("Encryption"),
conn->IsSSLconnected() ? _("SSL encrypted") : _("not encrypted"));
}
else
{
if (ssl > 0)
{
wxString sslMode;
switch (ssl)
{
case 1:
sslMode = _("require");
break;
case 2:
sslMode = _("prefer");
break;
case 3:
sslMode = _("allow");
break;
case 4:
sslMode = _("disable");
break;
case 5:
sslMode = _("verify-ca");
break;
case 6:
sslMode = _("verify-full");
break;
}
properties->AppendItem(_("SSL Mode"), sslMode);
}
}
properties->AppendItem(_("SSL Certificate File"), GetSSLCert());
properties->AppendItem(_("SSL Key File"), GetSSLKey());
properties->AppendItem(_("SSL Root Certificate File"), GetSSLRootCert());
properties->AppendItem(_("SSL Certificate Revocation List"), GetSSLCrl());
properties->AppendItem(_("SSL Compression?"), (GetSSLCompression() ? _("yes") : _("no")));
#endif
}
if (!serviceId.IsEmpty())
properties->AppendItem(_("Service ID"), serviceId);
properties->AppendItem(_("Maintenance database"), GetDatabaseName());
properties->AppendItem(_("Username"), GetUsername());
if (!GetRolename().IsEmpty())
properties->AppendItem(_("Default role"), GetRolename());
if (!GetConnStr().IsEmpty())
properties->AppendItem(_("Add connect str"), GetConnStr());
properties->AppendYesNoItem(_("Store password?"), GetStorePwd());
properties->AppendYesNoItem(_("Restore environment?"), GetRestore());
if (GetConnected())
{
properties->AppendItem(_("Version string"), GetVersionString());
properties->AppendItem(_("Version number"), GetVersionNumber());
properties->AppendItem(_("Last system OID"), GetLastSystemOID());
}
properties->AppendYesNoItem(_("Connected?"), GetConnected());
if (GetConnected())
{
if (GetUpSince().IsValid())
properties->AppendItem(_("Up since"), GetUpSince());
if (GetConfLoadedSince().IsValid())
properties->AppendItem(_("Configuration loaded since"), GetConfLoadedSince());
if (conn->BackendMinimumVersion(8, 1))
properties->AppendItem(wxT("Autovacuum"), (autovacuumRunning ? _("running") : _("not running")));
if (conn->BackendMinimumVersion(8, 5))
{
properties->AppendItem(_("In recovery"), (GetInRecovery() ? _("yes") : _("no")));
properties->AppendItem(_("Last XLOG receive location"), GetReceiveLoc());
properties->AppendItem(_("Last XLOG replay location"), GetReplayLoc());
}
if (conn->BackendMinimumVersion(9, 1))
{
properties->AppendItem(_("Last XACT replay timestamp"), GetReplayTimestamp());
if (GetInRecovery())
properties->AppendItem(_("Replay paused"), (GetReplayPaused() ? _("paused") : _("running")));
else
properties->AppendItem(_("Replay paused"), wxEmptyString);
}
}
if (GetServerControllable())
properties->AppendYesNoItem(_("Running?"), GetServerRunning());
if (!GetDbRestriction().IsEmpty())
properties->AppendItem(_("DB restriction"), GetDbRestriction());
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
if(sshTunnel)
{
properties->AppendItem(_("SSH tunneling?"), (sshTunnel ? _("Yes") : _("No")));
properties->AppendItem(_("Tunnel host"), GetTunnelHost());
properties->AppendItem(_("Tunnel username"), GetTunnelUserName());
properties->AppendItem(_("Authentication mode"), (GetAuthModePwd() ? _("Password") : _("Identity file")));
if(!GetAuthModePwd())
{
properties->AppendItem(_("Identity file path"), GetIdentityFile());
}
}
#endif
}
if(!GetConnected())
return;
if (form && GetCanHint() && !hintShown)
{
ShowHint(form, false);
}
}
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
bool pgServer::createSSHTunnel()
{
bool retVal = false;
tunnelObj = new CSSHTunnelThread(tunnelHost, GetName(), port, tunnelUserName, tunnelPassword, publicKeyFile,
identityFile, authModePwd ? AUTH_PASSWORD : AUTH_PUBLICKEY, tunnelPort);
if(tunnelObj)
{
if(tunnelObj->Initialize())
{
if ( tunnelObj->Create() != wxTHREAD_NO_ERROR )
{
delete tunnelObj;
tunnelObj = NULL;
wxLogError(_("SSH Error: Unable to create SSH Tunnling Thread"));
}
else
{
if (tunnelObj->Run() != wxTHREAD_NO_ERROR )
{
delete tunnelObj;
tunnelObj = NULL;
wxLogError(_("SSH Error: Unable to start SSH Tunnling Thread"));
}
SetLocalListenHost(tunnelObj->GetLocalListenIP());
SetLocalListenPort(tunnelObj->GetLocalListenPort());
retVal = true;
}
}
else
{
delete tunnelObj;
tunnelObj = NULL;
}
}
return retVal;
}
#endif
void pgServer::ShowStatistics(frmMain *form, ctlListView *statistics)
{
if (conn)
{
wxString pidcol = GetConnection()->BackendMinimumVersion(9, 2) ? wxT("pid") : wxT("procpid");
wxString querycol = GetConnection()->BackendMinimumVersion(9, 2) ? wxT("query") : wxT("current_query");
wxString sql;
wxString replication_query = wxT("state || ' (' || sent_location || ' sent, ' || write_location || ' written, ' || flush_location || ' flushed, ' || replay_location || ' applied)'");
if (GetConnection()->BackendMinimumVersion(10, 0)) {
replication_query = wxT("state || ' (' || sent_lsn || ' sent, ' || write_lsn || ' written, ' || flush_lsn || ' flushed, ' || replay_lsn || ' applied)'");
}
wxLogInfo(wxT("Displaying statistics for server %s"), GetIdentifier().c_str());
// Add the statistics view columns
statistics->ClearAll();
statistics->AddColumn(wxT("PID"), 35);
statistics->AddColumn(_("User"), 70);
statistics->AddColumn(_("Database"), 70);
if (GetConnection()->BackendMinimumVersion(8, 1))
{
statistics->AddColumn(_("Backend start"), 70);
statistics->AddColumn(_("Client"), 70);
}
statistics->AddColumn(_("Current Query"), 300);
sql = wxT("SELECT ") + pidcol + wxT(" AS pid, usename, datname, backend_start, client_addr, ");
if (GetConnection()->BackendMinimumVersion(9, 1))
sql += wxT("client_hostname, ");
sql += wxT("client_port, ") + querycol + wxT(" AS query FROM pg_stat_activity\n");
if (GetConnection()->BackendMinimumVersion(9, 1))
{
sql += wxT("UNION\n")
wxT("SELECT ") + pidcol + wxT(", usename, '' AS datname, backend_start, client_addr, client_hostname, client_port, ")
+ replication_query + wxT(" AS query FROM pg_stat_replication");
}
pgSet *stats = ExecuteSet(sql);
if (stats)
{
int pos = 0;
while (!stats->Eof())
{
statistics->InsertItem(pos, stats->GetVal(wxT("pid")), 0);
int colpos = 1;
statistics->SetItem(pos, colpos++, stats->GetVal(wxT("usename")));
statistics->SetItem(pos, colpos++, stats->GetVal(wxT("datname")));
if (GetConnection()->BackendMinimumVersion(8, 1))
{
statistics->SetItem(pos, colpos++, stats->GetVal(wxT("backend_start")));
wxString client;
if (GetConnection()->BackendMinimumVersion(9, 1) && !stats->GetVal(wxT("client_hostname")).IsEmpty())
client = stats->GetVal(wxT("client_hostname")) + wxT(":") + stats->GetVal(wxT("client_port"));
else
client = stats->GetVal(wxT("client_addr")) + wxT(":") + stats->GetVal(wxT("client_port"));
if (client == wxT(":-1"))
client = _("local pipe");
statistics->SetItem(pos, colpos++, client);
}
statistics->SetItem(pos, colpos++, stats->GetVal(wxT("query")));
stats->MoveNext();
pos++;
}
delete stats;
}
}
}
void pgServer::ShowDependencies(frmMain *form, ctlListView *Dependencies, const wxString &wh)
{
}
void pgServer::ShowDependents(frmMain *form, ctlListView *referencedBy, const wxString &wh)
{
}
bool pgServer::ReloadConfiguration()
{
wxString sql = wxT("select pg_reload_conf()");
return conn->ExecuteVoid(sql);
}
bool pgServer::PauseReplay()
{
SetReplayPaused(true);
wxString sql = wxT("SELECT pg_xlog_replay_pause()");
if (conn->BackendMinimumVersion(10, 0)) sql = wxT("SELECT pg_wal_replay_pause()");
return conn->ExecuteVoid(sql);
}
bool pgServer::ResumeReplay()
{
SetReplayPaused(false);
wxString sql = wxT("SELECT pg_xlog_replay_resume()");
if (conn->BackendMinimumVersion(10, 0)) sql = wxT("SELECT pg_wal_replay_resume()");
return conn->ExecuteVoid(sql);
}
bool pgServer::AddNamedRestorePoint()
{
wxString namedrestorepoint = wxGetTextFromUser(_("Enter the name of the restore point to add"), _("Restore point name"));
if (!namedrestorepoint.IsEmpty())
{
wxString sql = wxT("select pg_create_restore_point(") + qtDbString(namedrestorepoint) + wxT(")");
return conn->ExecuteVoid(sql);
}
return false;
}
pgServerCollection::pgServerCollection(pgaFactory *factory)
: pgCollection(factory)
{
}
wxString pgServerCollection::GetTranslatedMessage(int kindOfMessage) const
{
wxString message = wxEmptyString;
switch (kindOfMessage)
{
case RETRIEVINGDETAILS:
message = _("Retrieving details on servers");
break;
case REFRESHINGDETAILS:
message = _("Refreshing servers");
break;
case OBJECTSLISTREPORT:
message = _("Servers list report");
break;
}
return message;
}
pgServerObjCollection::pgServerObjCollection(pgaFactory *factory, pgServer *sv)
: pgCollection(factory)
{
server = sv;
}
bool pgServerObjCollection::CanCreate()
{
// We can't create resource queues on Greenplum yet.
if (IsCollectionForType(GP_RESOURCE_QUEUE))
return false;
// We can't create tablespaces on Greenplum
if (server->GetConnection()->GetIsGreenplum() && IsCollectionForType(PGM_TABLESPACE))
return false;
if (server->GetMetaType() == PGM_DATABASE)
return (GetServer()->GetCreatePrivilege() || GetServer()->GetSuperUser());
else
{
if (server->GetConnection()->BackendMinimumVersion(8, 1) && GetMetaType() == PGM_ROLE)
return (server->GetCreateRole() || server->GetSuperUser());
else if (server->GetConnection()->BackendMinimumVersion(8, 1) && GetMetaType() == PGM_DATABASE)
return (server->GetCreatePrivilege() || server->GetSuperUser());
else if (GetMetaType() == PGM_JOB)
return true;
else
return server->GetSuperUser();
}
}
pgObject *pgServerFactory::CreateObjects(pgCollection *obj, ctlTree *browser, const wxString &restr)
{
wxTreeItemId groupitem, serveritem;
wxTreeItemIdValue groupcookie;
bool found;
long numServers = settings->Read(wxT("Servers/Count"), 0L);
long loop, port, ssl = 0;
wxString key, servername, hostaddr, description, service, database, username, lastDatabase, lastSchema;
wxString storePwd, rolename, connstr, restore, serviceID, discoveryID, dbRestriction, colour;
wxString group, sslcert, sslkey, sslrootcert, sslcrl, sslcompression;
wxString keywords;
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
wxString sshTunnel, authModePwd, tunnelHost, tunnelUserName, tunnelPassword, publicKeyFile, identityFile;
long tunnelPort;
#endif
pgServer *server = 0;
wxArrayString discoveredServers;
// Get the hostname for later...
char buf[255];
gethostname(buf, 255);
wxString hostname = wxString(buf, wxConvUTF8);
//wxLogError(wxT("Loading previously registered servers"));
wxLogInfo(wxT("Loading previously registered servers"));
for (loop = 1; loop <= numServers; ++loop)
{
key.Printf(wxT("Servers/%d/"), (int)loop);
settings->Read(key + wxT("Keywords"), &keywords, wxEmptyString);
settings->Read(key + wxT("Server"), &servername, wxEmptyString);
settings->Read(key + wxT("HostAddr"), &hostaddr, wxEmptyString);
settings->Read(key + wxT("Service"), &service, wxEmptyString);
settings->Read(key + wxT("ServiceID"), &serviceID, wxEmptyString);
settings->Read(key + wxT("DiscoveryID"), &discoveryID, serviceID);
settings->Read(key + wxT("Description"), &description, wxEmptyString);
settings->Read(key + wxT("StorePwd"), &storePwd, wxEmptyString);
settings->Read(key + wxT("Rolename"), &rolename, wxEmptyString);
settings->Read(key + wxT("ConnStr"), &connstr, wxEmptyString);
settings->Read(key + wxT("Restore"), &restore, wxT("true"));
settings->Read(key + wxT("Port"), &port, 0);
settings->Read(key + wxT("Database"), &database, wxEmptyString);
settings->Read(key + wxT("Username"), &username, wxEmptyString);
settings->Read(key + wxT("LastDatabase"), &lastDatabase, wxEmptyString);
settings->Read(key + wxT("LastSchema"), &lastSchema, wxEmptyString);
settings->Read(key + wxT("DbRestriction"), &dbRestriction, wxEmptyString);
settings->Read(key + wxT("Colour"), &colour, wxEmptyString);
settings->Read(key + wxT("Group"), &group, wxT("Servers"));
settings->Read(key + wxT("SSLCert"), &sslcert, wxEmptyString);
settings->Read(key + wxT("SSLKey"), &sslkey, wxEmptyString);
settings->Read(key + wxT("SSLRootCert"), &sslrootcert, wxEmptyString);
settings->Read(key + wxT("SSLCrl"), &sslcrl, wxEmptyString);
settings->Read(key + wxT("SSLCompression"), &sslcompression, wxT("true"));
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
settings->Read(key + wxT("SSHTunnel"), &sshTunnel, wxT("false"));
settings->Read(key + wxT("TunnelHost"), &tunnelHost, wxEmptyString);
settings->Read(key + wxT("TunnelUserName"), &tunnelUserName, wxEmptyString);
settings->Read(key + wxT("TunnelModePwd"), &authModePwd, wxT("true"));
settings->Read(key + wxT("PublicKeyFile"), &publicKeyFile, wxEmptyString);
settings->Read(key + wxT("IdentityFile"), &identityFile, wxEmptyString);
settings->Read(key + wxT("TunnelPort"), &tunnelPort, DEFAULT_SSH_PORT);
#endif
// Sanitize the colour
colour = colour.Trim();
if (!colour.IsEmpty())
{
wxColour cColour;
if (cColour.Set(colour))
colour = cColour.GetAsString(wxC2S_HTML_SYNTAX);
else
colour = wxEmptyString;
}
if (colour.IsEmpty())
{
wxColour cColour;
cColour.Set(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW).GetAsString(wxC2S_HTML_SYNTAX));
colour = cColour.GetAsString(wxC2S_HTML_SYNTAX);
}
// SSL mode
#ifdef PG_SSL
settings->Read(key + wxT("SSL"), &ssl, 0);
#endif
// Sanitize the group
if (group.IsEmpty())
{
group = _("Servers");
}
// Add the Server node
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
server = new pgServer(servername, hostaddr, description, service, database, username, port, StrToBool(storePwd), rolename, connstr, StrToBool(restore), ssl,
colour, group, StrToBool(sshTunnel), tunnelHost, tunnelUserName, StrToBool(authModePwd), tunnelPassword, publicKeyFile, identityFile, tunnelPort);
#else
server = new pgServer(servername, hostaddr, description, service, database, username, port, StrToBool(storePwd), rolename, connstr, StrToBool(restore), ssl,
colour, group);
#endif
server->iSetLastDatabase(lastDatabase);
server->iSetLastSchema(lastSchema);
server->iSetService(service);
server->iSetServiceID(serviceID);
server->iSetDiscoveryID(discoveryID);
server->iSetDiscovered(false);
server->iSetDbRestriction(dbRestriction);
server->iSetServerIndex(loop);
server->SetSSLCert(sslcert);
server->SetSSLKey(sslkey);
server->SetSSLRootCert(sslrootcert);
server->SetSSLCrl(sslcrl);
server->iSetSSLCompression(StrToBool(sslcompression));
server->iSetKeywords(keywords);
found = false;
if (browser->ItemHasChildren(obj->GetId()))
{
groupitem = browser->GetFirstChild(obj->GetId(), groupcookie);
while (!found && groupitem)
{
if (browser->GetItemText(groupitem).StartsWith(group))
found = true;
else
groupitem = browser->GetNextChild(obj->GetId(), groupcookie);
}
}
if (!found)
{
groupitem = browser->AppendItem(obj->GetId(), group, obj->GetIconId());
}
serveritem = browser->AppendItem(groupitem, server->GetFullName(), server->GetIconId(), -1, server);
browser->SortChildren(groupitem);
if (!server->GetColour().IsEmpty())
browser->SetItemBackgroundColour(serveritem, wxColour(server->GetColour()));
// Note if we're reloading a discovered server
if (!discoveryID.IsEmpty())
discoveredServers.Add(discoveryID);
}
group = _("Servers");
#ifdef __WXMSW__
// Add local servers. Will currently only work on Win32 with >= BETA3
// of the Win32 PostgreSQL installer.
wxLogInfo(wxT("Loading servers registered on the local machine"));
pgRegKey::PGREGWOWMODE wowMode = pgRegKey::PGREG_WOW_DEFAULT;
if (::wxIsPlatform64Bit())
wowMode = pgRegKey::PGREG_WOW32;
pgRegKey *pgKey = pgRegKey::OpenRegKey(HKEY_LOCAL_MACHINE, wxT("Software\\PostgreSQL\\Services"), pgRegKey::PGREG_READ, wowMode);
if (pgKey == NULL)
{
wowMode = pgRegKey::PGREG_WOW64;
pgKey = pgRegKey::OpenRegKey(HKEY_LOCAL_MACHINE, wxT("Software\\PostgreSQL\\Services"), pgRegKey::PGREG_READ, wowMode);
}
while (pgKey != NULL)
{
pgRegKey *svcKey = NULL;
wxString svcName;
long cookie = 0;
DWORD tmpport = 0;
bool flag = false;
flag = pgKey->GetFirstKey(svcKey, cookie);
while (flag != false)
{
svcName = svcKey->GetKeyName();
// On Windows, the discovery ID is always the service name.
// Only load the server if we didn't load it with all the others.
if (discoveredServers.Index(svcName, false) < 0)
{
servername = wxT("localhost");
database = wxEmptyString;
svcKey->QueryValue(wxT("Display Name"), description);
svcKey->QueryValue(wxT("Database Superuser"), username);
svcKey->QueryValue(wxT("Port"), &tmpport);
// Add the Server node
server = new pgServer(servername, wxEmptyString, description, wxEmptyString, database, username, (long)tmpport, false, wxEmptyString, wxEmptyString, false);
server->iSetDiscoveryID(svcName);
server->iSetDiscovered(true);
server->iSetServiceID(svcName);
server->iSetGroup(group);
found = false;
if (browser->ItemHasChildren(browser->GetRootItem()))
{
groupitem = browser->GetFirstChild(browser->GetRootItem(), groupcookie);
while (!found && groupitem)
{
if (browser->GetItemText(groupitem).StartsWith(group))
found = true;
else
groupitem = browser->GetNextChild(browser->GetRootItem(), groupcookie);
}
}
if (!found)
{
groupitem = browser->AppendItem(browser->GetRootItem(), group, obj->GetIconId());
browser->SortChildren(browser->GetRootItem());
}
browser->AppendItem(groupitem, server->GetFullName(), server->GetIconId(), -1, server);
browser->SortChildren(groupitem);
}
// Release the current registry key
delete svcKey;
// Get the next one...
flag = pgKey->GetNextKey(svcKey, cookie);
}
/* Release current registry key */
delete pgKey;
pgKey = NULL;
/*
* If wowMode is equal to WOW32, that means this machine is a 64 bit machine and we need to read now 64 bit registry
*/
if (wowMode == pgRegKey::PGREG_WOW32)
{
wowMode = pgRegKey::PGREG_WOW64;
pgKey = pgRegKey::OpenRegKey(HKEY_LOCAL_MACHINE, wxT("Software\\PostgreSQL\\Services"), pgRegKey::PGREG_READ, wowMode);
}
}
#endif // __WXMSW__
// Add local servers on non-Win32 platforms (on Win32, they will be picked up above)
#ifndef WIN32
// On Unix/Mac, the discovery ID can be anything. We use the PostgreSQL
// package config filename if it's present, as that is the only thing vaguely
// discoverable and unique to a given installation. We can do the same for
// other distros in the future if they drop a suitable file someplace.
// Look for any files that match the basic postgres*.ini pattern.
wxLogInfo(wxT("Loading servers registered on the local machine"));
if (wxFile::Exists(REGISTRY_FILE))
{
wxString version, locale;
long cookie;
wxFileInputStream fst(REGISTRY_FILE);
wxFileConfig *cnf = new wxFileConfig(fst);
// PostgreSQL servers
cnf->SetPath(wxT("/PostgreSQL"));
bool flag = cnf->GetFirstGroup(version, cookie);
while (flag)
{
// If there is no Version entry, this is probably an uninstalled server
if (cnf->Read(version + wxT("/Version"), wxEmptyString) != wxEmptyString)
{
// Only load this server if we haven't read it from the pgAdmin config
if (discoveredServers.Index(cnf->GetPath() + wxT("/") + version, false) < 0)
{
// Basic details
servername = wxT("localhost");
cnf->Read(version + wxT("/Description"), &description, wxT("PostgreSQL ") + version);
cnf->Read(version + wxT("/Superuser"), &username, wxEmptyString);
cnf->Read(version + wxT("/Port"), &port, 0);
// Add the item, if it looks sane
if (port != 0 && username != wxEmptyString)
{
server = new pgServer(servername, wxEmptyString, description, wxEmptyString, wxT("postgres"), username, port, false, rolename, connstr, 0);
server->iSetDiscoveryID(cnf->GetPath() + wxT("/") + version);
server->iSetDiscovered(true);
server->iSetGroup(group);
found = false;
if (browser->ItemHasChildren(browser->GetRootItem()))
{
groupitem = browser->GetFirstChild(browser->GetRootItem(), groupcookie);
while (!found && groupitem)
{
if (browser->GetItemText(groupitem).StartsWith(group))
found = true;
else
groupitem = browser->GetNextChild(browser->GetRootItem(), groupcookie);
}
}
if (!found)
{
groupitem = browser->AppendItem(browser->GetRootItem(), group, obj->GetIconId());
browser->SortChildren(browser->GetRootItem());
}
browser->AppendItem(groupitem, server->GetFullName(), server->GetIconId(), -1, server);
browser->SortChildren(groupitem);
}
}
}
flag = cnf->GetNextGroup(version, cookie);
}
// EnterpriseDB servers
cnf->SetPath(wxT("/EnterpriseDB"));
flag = cnf->GetFirstGroup(version, cookie);
while (flag)
{
// If there is no Version entry, this is probably an uninstalled server
if (cnf->Read(version + wxT("/Version"), wxEmptyString) != wxEmptyString)
{
// Only load this server if we haven't read it from the pgAdmin config
if (discoveredServers.Index(cnf->GetPath() + wxT("/") + version, false) < 0)
{
// Basic details
servername = wxT("localhost");
cnf->Read(version + wxT("/Description"), &description, wxT("EnterpriseDB ") + version);
cnf->Read(version + wxT("/Superuser"), &username, wxEmptyString);
cnf->Read(version + wxT("/Port"), &port, 0);
// Add the item, if it looks sane
if (port != 0 && username != wxEmptyString)
{
server = new pgServer(servername, wxEmptyString, description, wxEmptyString, wxT("edb"), username, port, false, rolename, connstr, 0);
server->iSetDiscoveryID(cnf->GetPath() + wxT("/") + version);
server->iSetDiscovered(true);
groupitem = browser->GetFirstChild(obj->GetId(), groupcookie);
if (!groupitem.IsOk())
groupitem = browser->AppendItem(obj->GetId(), group, obj->GetIconId());
browser->AppendItem(groupitem, server->GetFullName(), server->GetIconId(), -1, server);
}
}
}
flag = cnf->GetNextGroup(version, cookie);
}
delete cnf;
browser->SortChildren(obj->GetId());
}
#endif // !WIN32
return server;
}
#include "images/servers.pngc"
#include "images/server.pngc"
#include "images/server-sm.pngc"
#include "images/serverbad.pngc"
#include "images/serverbad-sm.pngc"
pgServerFactory::pgServerFactory()
: pgaFactory(__("Server"), __("New Server Registration"), __("Create a new Server registration."), server_png_img, server_sm_png_img)
{
metaType = PGM_SERVER;
closedId = addIcon(serverbad_png_img);
smallClosedId = addIcon(serverbad_sm_png_img);
}
pgCollection *pgServerFactory::CreateCollection(pgObject *obj)
{
return new pgCollection(GetCollectionFactory());
}
pgCollection *pgServerObjFactory::CreateCollection(pgObject *obj)
{
return new pgServerObjCollection(GetCollectionFactory(), (pgServer *)obj);
}
pgServerFactory serverFactory;
static pgaCollectionFactory cf(&serverFactory, __("Servers"), servers_png_img);
#include "images/connect.pngc"
addServerFactory::addServerFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : actionFactory(list)
{
mnu->Append(id, _("&Add Server..."), _("Add a connection to a server."));
toolbar->AddTool(id, _("Add Server"), GetBundleSVG(connect_png_bmp, "connect.svg", wxSize(32, 32)), _("Add a connection to a server."), wxITEM_NORMAL);
}
wxWindow *addServerFactory::StartDialog(frmMain *form, pgObject *obj)
{
int rc = PGCONN_BAD;
dlgServer dlg(&serverFactory, form, 0);
dlg.CenterOnParent();
while (rc != PGCONN_OK)
{
if (dlg.GoNew() != wxID_OK)
return 0;
pgServer *server = (pgServer *)dlg.CreateObject(0);
if (dlg.GetTryConnect())
{
wxBusyInfo waiting(wxString::Format(_("Connecting to server %s (%s:%d)"),
server->GetDescription().c_str(), server->GetName().c_str(), server->GetPort()), form);
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(100);
wxSafeYield();
rc = server->Connect(form, false, dlg.GetPassword(), true);
}
else
{
rc = PGCONN_OK;
server->InvalidatePassword();
}
switch (rc)
{
case PGCONN_OK:
{
int icon;
ctlTree *browser = form->GetBrowser();
wxTreeItemId groupitem, parentitem;
wxTreeItemIdValue groupcookie;
int total;
wxString label;
if (server->GetConnected())
icon = serverFactory.GetIconId();
else
icon = serverFactory.GetClosedIconId();
wxLogInfo(wxT("pgServer object initialised as required."));
// Add the new server in its group
wxString group = server->GetGroup();
if (group.Length() == 0)
group = _("Servers");
// Get the parent group
groupitem = browser->GetFirstChild(browser->GetRootItem(), groupcookie);
while (!parentitem && groupitem)
{
if (browser->GetItemText(groupitem).StartsWith(group))
parentitem = groupitem;
groupitem = browser->GetNextChild(browser->GetRootItem(), groupcookie);
}
if (!parentitem)
parentitem = browser->AppendItem(browser->GetRootItem(), group, icon, -1);
browser->AppendItem(parentitem, server->GetFullName(), icon, -1, server);
browser->SortChildren(parentitem);
browser->Expand(parentitem);
total = browser->GetChildrenCount(parentitem, false);
label = group + wxT(" (") + NumToStr((long)total) + wxT(")");
browser->SetItemText(parentitem, label);
form->StoreServers();
return 0;
}
case PGCONN_DNSERR:
{
delete server;
break;
}
case PGCONN_BAD:
case PGCONN_BROKEN:
{
form->ReportConnError(server);
delete server;
break;
}
case PGCONN_SSHTUNNEL_ERROR:
{
delete server;
break;
}
default:
{
wxLogInfo(__("pgServer object didn't initialise because the user aborted."));
delete server;
return 0;
}
}
}
return 0;
}
startServiceFactory::startServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Start Service"), _("Start PostgreSQL Service"));
}
wxWindow *startServiceFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
form->StartMsg(_("Starting Service"));
bool rc = server->StartService();
if (rc)
form->execSelChange(server->GetId(), true);
form->EndMsg(rc);
return 0;
}
bool startServiceFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
{
pgServer *server = (pgServer *)obj;
return server->GetServerControllable() && !server->GetServerRunning();
}
return false;
}
stopServiceFactory::stopServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Stop Service"), _("Stop PostgreSQL Service"));
}
wxWindow *stopServiceFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
wxMessageDialog msg(form, _("Are you sure you wish to shutdown this server?"),
_("Stop Service"), wxYES_NO | wxICON_QUESTION);
if (msg.ShowModal() == wxID_YES)
{
form->StartMsg(_("Stopping service"));
bool done = server->StopService();
if (done)
{
if (server->Disconnect(form))
{
form->GetBrowser()->DeleteChildren(server->GetId());
form->execSelChange(server->GetId(), true);
}
}
form->EndMsg(done);
}
return 0;
}
bool stopServiceFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
{
pgServer *server = (pgServer *)obj;
return server->GetServerControllable() && server->GetServerRunning();
}
return false;
}
connectServerFactory::connectServerFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("&Connect"), _("Connect to the selected server."));
}
wxWindow *connectServerFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
form->ReconnectServer(server);
return 0;
}
bool connectServerFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
return !((pgServer *)obj)->GetConnected();
return false;
}
disconnectServerFactory::disconnectServerFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
wxString msg=_("Disconnec&t server")+"\tCtrl-W";
mnu->Append(id, msg, _("Disconnect from the selected server."));
}
wxWindow *disconnectServerFactory::StartDialog(frmMain *form, pgObject *obj)
{
{
pgServer *server=NULL;
wxTreeItemId idserver;
if (obj) {
if (!obj->IsCreatedBy(serverFactory)) {
server=obj->GetServer();
if (server) {
idserver=server->GetId();
form->execSelChange(idserver, false);
form->GetBrowser()->SelectItem(idserver);
} else return 0;
} else {
idserver=obj->GetId();
server = (pgServer *)obj;
}
if (server->CheckOpenDialogs(form->GetBrowser(), idserver))
{
wxString msg = _("There are properties dialogues open for one or more objects belonging to a database which will be disconnected. Please close the properties dialogues and try again.");
wxMessageBox(msg, _("Cannot disconnect database"), wxICON_WARNING | wxOK);
} else
{
server->Disconnect(form);
server->UpdateIcon(form->GetBrowser());
form->GetBrowser()->DeleteChildren(idserver);
form->execSelChange(idserver, true);
}
}
}
return 0;
}
bool disconnectServerFactory::CheckEnable(pgObject *obj)
{
if (obj) {
pgServer *pgs=obj->GetServer();
if (pgs) return pgs->GetConnected();
}
// if (obj && obj->IsCreatedBy(serverFactory))
// return ((pgServer *)obj)->GetConnected();
return false;
}
disconnectServerFactoryAll::disconnectServerFactoryAll(menuFactoryList* list, wxMenu* mnu, ctlMenuToolbar* toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Disconnec&t all servers"), _("Disconnect from all servers."));
}
wxWindow* disconnectServerFactoryAll::StartDialog(frmMain* form, pgObject* obj)
{
wxTreeItemIdValue foldercookie, servercookie;
wxTreeItemId folderitem, serveritem;
pgObject* object;
pgServer* server;
folderitem = form->GetBrowser()->GetFirstChild(form->GetBrowser()->GetRootItem(), foldercookie);
while (folderitem)
{
if (form->GetBrowser()->ItemHasChildren(folderitem))
{
serveritem = form->GetBrowser()->GetFirstChild(folderitem, servercookie);
while (serveritem)
{
object = form->GetBrowser()->GetObject(serveritem);
if (object && object->IsCreatedBy(serverFactory))
{
if (CheckEnable(object)) {
if (object->CheckOpenDialogs(form->GetBrowser(), serveritem))
{
wxString msg = _("There are properties dialogues open for one or more objects belonging to a database which will be disconnected. Please close the properties dialogues and try again.");
wxMessageBox(msg, _("Cannot disconnect database"), wxICON_WARNING | wxOK);
}
else
{
server = (pgServer*)object;
server->Disconnect(form);
server->UpdateIcon(form->GetBrowser());
form->GetBrowser()->DeleteChildren(object->GetId());
form->execSelChange(object->GetId(), true);
}
}
}
serveritem = form->GetBrowser()->GetNextChild(folderitem, servercookie);
}
}
folderitem = form->GetBrowser()->GetNextChild(form->GetBrowser()->GetRootItem(), foldercookie);
}
return 0;
}
bool disconnectServerFactoryAll::CheckEnable(pgObject* obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
return ((pgServer*)obj)->GetConnected();
return false;
}
reloadconfServiceFactory::reloadconfServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Reload configuration"), _("Reload configuration"));
}
wxWindow *reloadconfServiceFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
form->StartMsg(_("Reloading configuration"));
bool rc = server->ReloadConfiguration();
form->EndMsg(rc);
return 0;
}
bool reloadconfServiceFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
{
pgServer *server = (pgServer *)obj;
return server->GetConnected() && server->connection()->BackendMinimumVersion(8, 1);
}
return false;
}
pausereplayServiceFactory::pausereplayServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Pause replay of WAL"), _("Pause replay of WAL"));
}
wxWindow *pausereplayServiceFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
form->StartMsg(_("Pausing replay of WAL"));
bool rc = server->PauseReplay();
form->EndMsg(rc);
return 0;
}
bool pausereplayServiceFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
{
pgServer *server = (pgServer *)obj;
return server->GetConnected() &&
server->connection()->BackendMinimumVersion(9, 1) &&
server->GetInRecovery() &&
!server->GetReplayPaused();
}
return false;
}
resumereplayServiceFactory::resumereplayServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Resume replay of WAL"), _("Resume replay of WAL"));
}
wxWindow *resumereplayServiceFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
form->StartMsg(_("Resuming replay of WAL"));
bool rc = server->ResumeReplay();
form->EndMsg(rc);
return 0;
}
bool resumereplayServiceFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
{
pgServer *server = (pgServer *)obj;
return server->GetConnected() &&
server->connection()->BackendMinimumVersion(9, 1) &&
server->GetInRecovery() &&
server->GetReplayPaused();
}
return false;
}
addnamedrestorepointServiceFactory::addnamedrestorepointServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Add named restore point"), _("Add named restore point"));
}
wxWindow *addnamedrestorepointServiceFactory::StartDialog(frmMain *form, pgObject *obj)
{
pgServer *server = (pgServer *)obj;
form->StartMsg(_("Add named restore point"));
bool rc = server->AddNamedRestorePoint();
form->EndMsg(rc);
return 0;
}
bool addnamedrestorepointServiceFactory::CheckEnable(pgObject *obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
{
pgServer *server = (pgServer *)obj;
return server->GetConnected() && server->connection()->BackendMinimumVersion(9, 1) && !server->GetInRecovery();
}
return false;
}