Index: TODO.txt
===================================================================
RCS file: /projects/pgadmin3/TODO.txt,v
retrieving revision 1.92
retrieving revision 1.93
diff -LTODO.txt -LTODO.txt -u -w -r1.92 -r1.93
--- TODO.txt
+++ TODO.txt
@@ -4,14 +4,12 @@
pgDatatype to encapsulate all typmod handling, rewrite all other classes to use it
Use latest wxWindows snapshot when wxChmHelpController for gtk is included
Improve database connection docs for newbies.
- Handle unexpected server disconnection cleanly.
Enhancements
- Display server log
- Keyboard navigation (wx issue)
-
- function property: show line number
- EditGrid: refresh row after insert to show default values
- columns of Views: show properties
- Most property dlgs: check for readOnly if no sufficient rights are available
Index: CHANGELOG.txt
===================================================================
RCS file: /projects/pgadmin3/CHANGELOG.txt,v
retrieving revision 1.100
retrieving revision 1.101
diff -LCHANGELOG.txt -LCHANGELOG.txt -u -w -r1.100 -r1.101
--- CHANGELOG.txt
+++ CHANGELOG.txt
@@ -16,6 +16,9 @@
+ - 2004-05-28 AP handle lost connections safely
+
- 2004-05-28 AP fix server status not cleaning old connections
+
- 2004-05-28 AP line numbers in function definition property dialog
- 2004-05-26 AP warn when deleting superuser privilege
- 2004-05-10 DP Generate function SQL correctly when the return type needs a schema specification
- 2004-05-04 DP Prevent duplication of functional indexes
Index: events.cpp
===================================================================
RCS file: /projects/pgadmin3/src/ui/events.cpp,v
retrieving revision 1.88
retrieving revision 1.89
diff -Lsrc/ui/events.cpp -Lsrc/ui/events.cpp -u -w -r1.88 -r1.89
--- src/ui/events.cpp
+++ src/ui/events.cpp
@@ -109,6 +109,7 @@
EVT_MENU(MNU_NEW+PGA_JOB, frmMain::OnNew)
EVT_MENU(MNU_NEW+PGA_STEP, frmMain::OnNew)
EVT_MENU(MNU_NEW+PGA_SCHEDULE, frmMain::OnNew)
+ EVT_MENU(MNU_CHECKALIVE, frmMain::OnCheckAlive)
EVT_MENU(MNU_CONTEXTMENU, frmMain::OnContextMenu)
EVT_NOTEBOOK_PAGE_CHANGED(CTL_NOTEBOOK, frmMain::OnPageChange)
EVT_LIST_ITEM_SELECTED(CTL_PROPVIEW, frmMain::OnPropSelChanged)
@@ -334,6 +335,87 @@
}
}
+
+void frmMain::OnCheckAlive(wxCommandEvent &event)
+{
+ bool userInformed = false;
+ bool closeIt;
+
+ wxCookieType cookie;
+ wxTreeItemId item=browser->GetFirstChild(servers, cookie);
+ while (item)
+ {
+ pgServer *server=(pgServer*)browser->GetItemData(item);
+ if (server && server->GetType() == PG_SERVER && server->connection())
+ {
+ if (server->connection()->IsAlive())
+ {
+ wxCookieType cookie2;
+ item = browser->GetFirstChild(server->GetId(), cookie2);
+ while (item)
+ {
+ pgObject *obj=(pgObject*)browser->GetItemData(item);
+ if (obj && obj->GetType() == PG_DATABASES)
+ {
+ wxCookieType cookie3;
+ item = browser->GetFirstChild(obj->GetId(), cookie3);
+ while (item)
+ {
+ pgDatabase *db=(pgDatabase*)browser->GetItemData(item);
+ if (db && db->GetType() == PG_DATABASE && db->GetConnected() && db->connection())
+ {
+ if (!db->connection()->IsAlive())
+ {
+ if (!userInformed)
+ {
+ wxMessageDialog dlg(this, _("Close database browser? If you abort, the object browser will not show accurate data."),
+ wxString::Format(_("Connection to database %s lost."), db->GetName().c_str()),
+ wxICON_EXCLAMATION|wxYES_NO|wxYES_DEFAULT);
+
+ closeIt = (dlg.ShowModal() == wxID_YES);
+ userInformed = true;
+ }
+ if (closeIt)
+ {
+ browser->DeleteChildren(db->GetId());
+ browser->SetItemImage(db->GetId(), PGICON_CLOSEDDATABASE, wxTreeItemIcon_Selected);
+ browser->SetItemImage(db->GetId(), PGICON_CLOSEDDATABASE, wxTreeItemIcon_Selected);
+ db->Disconnect();
+ }
+ }
+ }
+ item = browser->GetNextChild(obj->GetId(), cookie3);
+ }
+ }
+ item = browser->GetNextChild(server->GetId(), cookie2);
+ }
+ }
+ else
+ {
+ if (!userInformed)
+ {
+ wxMessageDialog dlg(this, _("Close server browser? If you abort, the object browser will not show accurate data."),
+ wxString::Format(_("Connection to server %s lost."), server->GetName().c_str()),
+ wxICON_EXCLAMATION|wxYES_NO|wxYES_DEFAULT);
+
+ closeIt = (dlg.ShowModal() == wxID_YES);
+ userInformed = true;
+ }
+ if (closeIt)
+ {
+ browser->DeleteChildren(server->GetId());
+ browser->SetItemImage(server->GetId(), PGICON_SERVERBAD, wxTreeItemIcon_Normal);
+ browser->SetItemImage(server->GetId(), PGICON_SERVERBAD, wxTreeItemIcon_Selected);
+ server->Disconnect();
+ }
+ }
+ }
+
+ item = browser->GetNextChild(servers, cookie);
+ }
+}
+
+
void frmMain::OnPassword(wxCommandEvent& event)
{
frmPassword *winPassword = new frmPassword(this);
Index: frmMain.h
===================================================================
RCS file: /projects/pgadmin3/src/include/frmMain.h,v
retrieving revision 1.41
retrieving revision 1.42
diff -Lsrc/include/frmMain.h -Lsrc/include/frmMain.h -u -w -r1.41 -r1.42
--- src/include/frmMain.h
+++ src/include/frmMain.h
@@ -123,6 +123,8 @@
void OnDisconnect(wxCommandEvent &ev);
void OnQueryBuilder(wxCommandEvent &ev);
+ void OnCheckAlive(wxCommandEvent& event);
+
void execSelChange(wxTreeItemId item, bool currentNode);
void setDisplay(pgObject *data, ctlListView *props=0, ctlSQLBox *sqlbox=0);
void StoreServers();
Index: pgServer.h
===================================================================
RCS file: /projects/pgadmin3/src/include/pgServer.h,v
retrieving revision 1.29
retrieving revision 1.30
diff -Lsrc/include/pgServer.h -Lsrc/include/pgServer.h -u -w -r1.29 -r1.30
--- src/include/pgServer.h
+++ src/include/pgServer.h
@@ -22,6 +22,8 @@
// Class declarations
+class frmMain;
+
class pgServer : public pgObject
{
public:
@@ -47,6 +49,8 @@
bool GetSuperUser() const { return superUser; }
void iSetSuperUser(const bool b) { superUser=b; }
+ frmMain *GetParentFrame() { return parentWin; }
+
wxString GetLastDatabase() const { return lastDatabase; }
void iSetLastDatabase(const wxString& s) { lastDatabase=s; }
@@ -89,7 +93,7 @@
bool trusted, createPrivilege, superUser;
OID lastSystemOID;
wxString versionNum;
- wxFrame *parentWin;
+ frmMain *parentWin;
};
#endif
Index: pgConn.h
===================================================================
RCS file: /projects/pgadmin3/src/include/pgConn.h,v
retrieving revision 1.23
retrieving revision 1.24
diff -Lsrc/include/pgConn.h -Lsrc/include/pgConn.h -u -w -r1.23 -r1.24
--- src/include/pgConn.h
+++ src/include/pgConn.h
@@ -63,6 +63,8 @@
wxString GetOptions() const { return wxString(PQoptions(conn), *conv); }
int GetBackendPID() const { return PQbackendPID(conn); }
int GetStatus() const;
+ int GetLastResultStatus() const { return lastResultStatus; }
+ bool IsAlive();
wxString GetLastError() const { return wxString(PQerrorMessage(conn), *conv); }
wxString GetVersionString();
OID GetLastSystemOID() const { return lastSystemOID; }
@@ -80,6 +82,7 @@
private:
PGconn *conn;
+ int lastResultStatus;
int minorVersion, majorVersion;
wxMBConv *conv;
bool resolvedIP, needColQuoting;
Index: pgDatabase.h
===================================================================
RCS file: /projects/pgadmin3/src/include/pgDatabase.h,v
retrieving revision 1.32
retrieving revision 1.33
diff -Lsrc/include/pgDatabase.h -Lsrc/include/pgDatabase.h -u -w -r1.32 -r1.33
--- src/include/pgDatabase.h
+++ src/include/pgDatabase.h
@@ -62,9 +62,10 @@
bool DropObject(wxFrame *frame, wxTreeCtrl *browser);
bool CanMaintenance() { return true; }
bool RequireDropConfirm() { return true; }
- pgConn *connection() { return conn; }
+ pgConn *connection();
int Connect();
void Disconnect();
+ void CheckAlive();
void AppendSchemaChange(const wxString &sql);
wxString GetSchemaChanges() { return schemaChanges; }
void ClearSchemaChanges() { schemaChanges=wxEmptyString; }
@@ -75,6 +76,8 @@
private:
pgConn *conn;
+ bool connected;
+ bool useServerConnection;
wxString searchPath, path, encoding, variables;
wxString prettyOption;
bool allowConnections, createPrivilege;
Index: menu.h
===================================================================
RCS file: /projects/pgadmin3/src/include/menu.h,v
retrieving revision 1.13
retrieving revision 1.14
diff -Lsrc/include/menu.h -Lsrc/include/menu.h -u -w -r1.13 -r1.14
--- src/include/menu.h
+++ src/include/menu.h
@@ -76,6 +76,7 @@
MNU_RELOAD,
MNU_CLEARHISTORY,
MNU_SAVEHISTORY,
+ MNU_CHECKALIVE,
MNU_RECENT, // leave space for recent file entries
MNU_NEW=MNU_RECENT+15
};
Index: pgConn.cpp
===================================================================
RCS file: /projects/pgadmin3/src/db/pgConn.cpp,v
retrieving revision 1.46
retrieving revision 1.47
diff -Lsrc/db/pgConn.cpp -Lsrc/db/pgConn.cpp -u -w -r1.46 -r1.47
--- src/db/pgConn.cpp
+++ src/db/pgConn.cpp
@@ -233,18 +233,19 @@
wxLogSql(wxT("Void query (%s:%d): %s"), this->GetHost().c_str(), this->GetPort(), sql.c_str());
qryRes = PQexec(conn, sql.mb_str(*conv));
- int res = PQresultStatus(qryRes);
+ lastResultStatus = PQresultStatus(qryRes);
// Check for errors
- if (res != PGRES_TUPLES_OK &&
- res != PGRES_COMMAND_OK)
+ if (lastResultStatus != PGRES_TUPLES_OK &&
+ lastResultStatus != PGRES_COMMAND_OK)
{
LogError();
+ return false;
}
// Cleanup & exit
PQclear(qryRes);
- return res == PGRES_TUPLES_OK || res == PGRES_COMMAND_OK;
+ return true;
}
@@ -269,9 +270,10 @@
PGresult *qryRes;
wxLogSql(wxT("Scalar query (%s:%d): %s"), this->GetHost().c_str(), this->GetPort(), sql.c_str());
qryRes = PQexec(conn, sql.mb_str(*conv));
+ lastResultStatus = PQresultStatus(qryRes);
// Check for errors
- if (PQresultStatus(qryRes) != PGRES_TUPLES_OK)
+ if (lastResultStatus != PGRES_TUPLES_OK)
{
LogError();
PQclear(qryRes);
@@ -307,9 +309,9 @@
wxLogSql(wxT("Set query (%s:%d): %s"), this->GetHost().c_str(), this->GetPort(), sql.c_str());
qryRes = PQexec(conn, sql.mb_str(*conv));
- int status= PQresultStatus(qryRes);
+ lastResultStatus= PQresultStatus(qryRes);
- if (status == PGRES_TUPLES_OK || status == PGRES_COMMAND_OK)
+ if (lastResultStatus == PGRES_TUPLES_OK || lastResultStatus == PGRES_COMMAND_OK)
{
pgSet *set = new pgSet(qryRes, this, *conv, needColQuoting);
if (!set)
@@ -349,6 +351,28 @@
}
+
+bool pgConn::IsAlive()
+{
+ if (GetStatus() != PGCONN_OK)
+ return false;
+
+ PGresult *qryRes = PQexec(conn, "SELECT 1;");
+ lastResultStatus = PQresultStatus(qryRes);
+ PQclear(qryRes);
+
+ // Check for errors
+ if (lastResultStatus != PGRES_TUPLES_OK)
+ {
+ PQfinish(conn);
+ conn=0;
+ return false;
+ }
+
+ return true;
+}
+
+
int pgConn::GetStatus() const
{
if(!resolvedIP)
Index: pgDatabase.cpp
===================================================================
RCS file: /projects/pgadmin3/src/schema/pgDatabase.cpp,v
retrieving revision 1.52
retrieving revision 1.53
diff -Lsrc/schema/pgDatabase.cpp -Lsrc/schema/pgDatabase.cpp -u -w -r1.52 -r1.53
--- src/schema/pgDatabase.cpp
+++ src/schema/pgDatabase.cpp
@@ -20,6 +20,8 @@
#include "pgServer.h"
#include "pgCollection.h"
#include "pgaAgent.h"
+#include "menu.h"
+#include "frmMain.h"
pgDatabase::pgDatabase(const wxString& newName)
@@ -27,7 +29,8 @@
{
wxLogInfo(wxT("Creating a pgDatabase object"));
- allowConnections = TRUE;
+ allowConnections = true;
+ connected = false;
conn = NULL;
}
@@ -36,8 +39,11 @@
{
wxLogInfo(wxT("Destroying a pgDatabase object"));
if (conn)
+ {
+ if (conn)
delete conn;
}
+}
wxMenu *pgDatabase::GetNewMenu()
@@ -59,43 +65,72 @@
if (!allowConnections)
return PGCONN_REFUSED;
- if (conn)
- return conn->GetStatus();
+ if (!connected)
+ {
+ if (GetName() == server->GetDatabaseName() && server->connection()->GetStatus() == PGCONN_OK)
+ {
+ useServerConnection = true;
+ conn=0;
+ }
else
{
- conn = new pgConn(GetServer()->GetName(), GetName(), GetServer()->GetUsername(), GetServer()->GetPassword(), GetServer()->GetPort(), GetServer()->GetSSL());
- if (conn->GetStatus() == PGCONN_OK)
+ useServerConnection = false;
+ conn = new pgConn(server->GetName(), GetName(), server->GetUsername(), server->GetPassword(), server->GetPort(), server->GetSSL());
+
+ if (conn->GetStatus() != PGCONN_OK)
{
+ wxLogError(wxT("%s"), conn->GetLastError().c_str());
+ delete conn;
+ conn=0;
+ connected = false;
+ return PGCONN_BAD;
+ }
+ }
+
// Now we're connected.
- iSetComment(conn->ExecuteScalar(wxT("SELECT description FROM pg_description WHERE objoid=") + GetOidStr()));
+ iSetComment(connection()->ExecuteScalar(wxT("SELECT description FROM pg_description WHERE objoid=") + GetOidStr()));
// check for extended ruleutils with pretty-print option
- wxString exprname=conn->ExecuteScalar(wxT("SELECT proname FROM pg_proc WHERE proname='pg_get_viewdef' AND proargtypes[1]=16"));
+ 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");
- searchPath = conn->ExecuteScalar(wxT("SHOW search_path"));
+ searchPath = connection()->ExecuteScalar(wxT("SHOW search_path"));
+ connected = true;
+ }
- return PGCONN_OK;
+ return connection()->GetStatus();
}
- else
+
+
+pgConn *pgDatabase::connection()
{
- delete conn;
- conn=0;
- wxLogError(wxT("%s"), conn->GetLastError().c_str());
- return PGCONN_BAD;
+ if (useServerConnection)
+ return server->connection();
+ return conn;
+
+}
+
+void pgDatabase::CheckAlive()
+{
+ if (connected)
+ {
+ connected = connection()->IsAlive();
+ if (!connected)
+ {
+ // backend died
+ wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, MNU_CHECKALIVE);
+ wxPostEvent(GetServer()->GetParentFrame(), event);
}
}
}
-
void pgDatabase::Disconnect()
{
-#if 0
+ connected=false;
if (conn)
delete conn;
conn=0;
-#endif
}
@@ -104,9 +139,9 @@
pgSet *set=0;
if (conn)
{
- set=conn->ExecuteSet(sql);
- if (!set && conn->GetStatus() == PGCONN_BAD)
- Disconnect();
+ set=connection()->ExecuteSet(sql);
+ if (!set)
+ CheckAlive();
}
return set;
}
@@ -117,9 +152,9 @@
wxString str;
if (conn)
{
- str = conn->ExecuteScalar(sql);
- if (str.IsEmpty() && conn->GetStatus() == PGCONN_BAD)
- Disconnect();
+ str = connection()->ExecuteScalar(sql);
+ if (str.IsEmpty() && connection()->GetLastResultStatus() != PGRES_TUPLES_OK)
+ CheckAlive();
}
return str;
}
@@ -130,9 +165,9 @@
bool rc;
if (conn)
{
- rc = conn->ExecuteVoid(sql);
- if (!rc && conn->GetStatus() == PGCONN_BAD)
- Disconnect();
+ rc = connection()->ExecuteVoid(sql);
+ if (!rc)
+ CheckAlive();
}
return rc;
}
@@ -193,6 +228,13 @@
bool pgDatabase::DropObject(wxFrame *frame, wxTreeCtrl *browser)
{
+ if (useServerConnection)
+ {
+ wxMessageDialog(frame, _("Initial database can't be dropped."),
+ _("Dropping database not allowed"), wxICON_EXCLAMATION | wxOK);
+
+ return false;
+ }
Disconnect();
bool done=server->ExecuteVoid(wxT("DROP DATABASE ") + GetQuotedIdentifier() + wxT(";"));
@@ -256,7 +298,7 @@
// pgAgent
pgaAgent::ReadObjects(this, browser);
- missingFKs = StrToLong(conn->ExecuteScalar(
+ missingFKs = StrToLong(connection()->ExecuteScalar(
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")
@@ -313,7 +355,7 @@
sql=wxT("");
iSetAcl(database->GetAcl());
iSetVariables(database->GetVariables());
- iSetComment(conn->ExecuteScalar(wxT("SELECT description FROM pg_description WHERE objoid=") + GetOidStr()));
+ iSetComment(connection()->ExecuteScalar(wxT("SELECT description FROM pg_description WHERE objoid=") + GetOidStr()));
delete database;
}
}
Index: pgServer.cpp
===================================================================
RCS file: /projects/pgadmin3/src/schema/pgServer.cpp,v
retrieving revision 1.40
retrieving revision 1.41
diff -Lsrc/schema/pgServer.cpp -Lsrc/schema/pgServer.cpp -u -w -r1.40 -r1.41
--- src/schema/pgServer.cpp
+++ src/schema/pgServer.cpp
@@ -79,6 +79,7 @@
{
wxLogInfo(wxT("Attempting to create a connection object..."));
+ parentWin = form;
if (!conn || conn->GetStatus() != PGCONN_OK)
{
if (conn)