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

Reply via email to