Log Message: ----------- handle server connection losses Modified Files: -------------- pgadmin3: CHANGELOG.txt (r1.100 -> r1.101) TODO.txt (r1.92 -> r1.93) pgadmin3/src/ui: events.cpp (r1.88 -> r1.89) pgadmin3/src/include: frmMain.h (r1.41 -> r1.42) menu.h (r1.13 -> r1.14) pgConn.h (r1.23 -> r1.24) pgDatabase.h (r1.32 -> r1.33) pgServer.h (r1.29 -> r1.30) pgadmin3/src/db: pgConn.cpp (r1.46 -> r1.47) pgadmin3/src/schema: pgDatabase.cpp (r1.52 -> r1.53) pgServer.cpp (r1.40 -> r1.41)
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 @@ <li>pgDatatype to encapsulate all typmod handling, rewrite all other classes to use it <li>Use latest wxWindows snapshot when wxChmHelpController for gtk is included <li>Improve database connection docs for newbies. - <li>Handle unexpected server disconnection cleanly. </ul> <li>Enhancements <ul> <li>Display server log <li>Keyboard navigation (wx issue) - <li>function property: show line number <li>EditGrid: refresh row after insert to show default values <li>columns of Views: show properties <li>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 @@ </ul> <br> <ul> + <li>2004-05-28 AP handle lost connections safely + <li>2004-05-28 AP fix server status not cleaning old connections + <li>2004-05-28 AP line numbers in function definition property dialog <li>2004-05-26 AP warn when deleting superuser privilege <li>2004-05-10 DP Generate function SQL correctly when the return type needs a schema specification <li>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)
---------------------------(end of broadcast)--------------------------- TIP 6: Have you searched our list archives? http://archives.postgresql.org