diff --git a/pgadmin/db/pgConn.cpp b/pgadmin/db/pgConn.cpp index efa080d..b06bc92 100644 --- a/pgadmin/db/pgConn.cpp +++ b/pgadmin/db/pgConn.cpp @@ -13,6 +13,7 @@ // wxWindows headers #include +#include // PostgreSQL headers #include @@ -320,6 +321,44 @@ void pgConn::Close() } +// Check the connection status, and offer to reconnect if necessary +bool pgConn::CheckConnection() +{ + if (!(GetStatus() == PGCONN_BROKEN || GetStatus() == PGCONN_BAD)) + return true; + + if (wxIsBusy) + wxEndBusyCursor(); + + wxMessageDialog dlg(NULL, _("Do you want to attempt to reconnect to the server?\n\nFailure to reconnect is an unrecoverable error and will cause the application to exit."), + wxString::Format(_("Connection to server %s lost."), GetName().c_str()), wxICON_EXCLAMATION | wxYES_NO | wxYES_DEFAULT); + + if (dlg.ShowModal() == wxID_NO) + { + wxLogError(_("Exiting following unrecoverable connection loss.")); + exit(-1); + return false; + } + + // It's kinda icky to do UI stuff here, but in this case it gets *really* + // messy to do it higher up. + { + wxBusyInfo waiting(wxString::Format(_("Reconnecting to server %s (%s:%d)"), GetName().c_str(), GetHost().c_str(), save_port), NULL); + + // Give the UI a chance to redraw + wxSafeYield(); + wxMilliSleep(100); + wxSafeYield(); + + if (Reconnect()) + wxMessageBox(_("Database connection re-established successfully.\n\nPlease retry the operation."), _("Connection re-established"), wxICON_INFORMATION); + } + + // Re-check the connection + return CheckConnection(); +} + + // Reconnect to the server bool pgConn::Reconnect() { @@ -342,6 +381,9 @@ bool pgConn::Reconnect() pgConn *pgConn::Duplicate() { + if (!CheckConnection()) + return NULL; + return new pgConn(wxString(save_server), wxString(save_service), wxString(save_hostaddr), wxString(save_database), wxString(save_username), wxString(save_password), save_port, save_rolename, save_sslmode, save_oid, save_applicationname, save_sslcert, save_sslkey, save_sslrootcert, save_sslcrl); @@ -386,6 +428,9 @@ bool pgConn::GetIsGreenplum() wxString pgConn::SystemNamespaceRestriction(const wxString &nsp) { + if (!CheckConnection()) + return wxEmptyString; + if (reservedNamespaces.IsEmpty()) { reservedNamespaces = wxT("'information_schema'"); @@ -416,6 +461,9 @@ wxString pgConn::SystemNamespaceRestriction(const wxString &nsp) bool pgConn::HasPrivilege(const wxString &objTyp, const wxString &objName, const wxString &priv) { + if (!CheckConnection()) + return false; + wxString res = ExecuteScalar( wxT("SELECT has_") + objTyp.Lower() + wxT("_privilege(") + qtDbString(objName) @@ -429,6 +477,9 @@ bool pgConn::HasPrivilege(const wxString &objTyp, const wxString &objName, const bool pgConn::BackendMinimumVersion(int major, int minor) { + if (!CheckConnection()) + return false; + if (!majorVersion) { wxString version = GetVersionString(); @@ -471,6 +522,9 @@ bool pgConn::EdbMinimumVersion(int major, int minor) bool pgConn::HasFeature(int featureNo, bool forceCheck) { + if (!CheckConnection()) + return false; + if (!features[FEATURE_INITIALIZED] || forceCheck) { features[FEATURE_INITIALIZED] = true; @@ -601,7 +655,7 @@ wxString pgConn::GetName() const if (dbHost.IsEmpty()) str.Printf(_("%s on local socket"), save_database.c_str()); else - str.Printf(_("%s on %s@%s:%d"), save_database.c_str(), GetUser().c_str(), dbHost.c_str(), GetPort()); + str.Printf(_("%s on %s@%s:%d"), save_database.c_str(), GetUser().c_str(), dbHost.c_str(), save_port); } else str.Printf(_("service %s"), save_service.c_str()); @@ -678,8 +732,8 @@ int pgConn::GetTxStatus() bool pgConn::ExecuteVoid(const wxString &sql, bool reportError) { - if (GetStatus() != PGCONN_OK) - return false; + if (!CheckConnection()) + return false; // Execute the query and get the status. PGresult *qryRes; @@ -707,73 +761,74 @@ bool pgConn::ExecuteVoid(const wxString &sql, bool reportError) wxString pgConn::ExecuteScalar(const wxString &sql) { + if (!CheckConnection()) + return wxEmptyString; + wxString result; - if (GetStatus() == PGCONN_OK) - { - // Execute the query and get the status. - 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); - SetLastResultError(qryRes); + // Execute the query and get the status. + 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); + SetLastResultError(qryRes); - // Check for errors - if (lastResultStatus != PGRES_TUPLES_OK && lastResultStatus != PGRES_COMMAND_OK) - { - LogError(); - PQclear(qryRes); - return wxEmptyString; - } + // Check for errors + if (lastResultStatus != PGRES_TUPLES_OK && lastResultStatus != PGRES_COMMAND_OK) + { + LogError(); + PQclear(qryRes); + return wxEmptyString; + } - // Check for a returned row - if (PQntuples(qryRes) < 1) - { - wxLogInfo(wxT("Query returned no tuples")); - PQclear(qryRes); - return wxEmptyString; - } + // Check for a returned row + if (PQntuples(qryRes) < 1) + { + wxLogInfo(wxT("Query returned no tuples")); + PQclear(qryRes); + return wxEmptyString; + } - // Retrieve the query result and return it. - result = wxString(PQgetvalue(qryRes, 0, 0), *conv); + // Retrieve the query result and return it. + result = wxString(PQgetvalue(qryRes, 0, 0), *conv); - wxLogSql(wxT("Query result: %s"), result.c_str()); + wxLogSql(wxT("Query result: %s"), result.c_str()); - // Cleanup & exit - PQclear(qryRes); - } + // Cleanup & exit + PQclear(qryRes); return result; } pgSet *pgConn::ExecuteSet(const wxString &sql) { + if (!CheckConnection()) + return NULL; + // Execute the query and get the status. - if (GetStatus() == PGCONN_OK) - { - PGresult *qryRes; - wxLogSql(wxT("Set query (%s:%d): %s"), this->GetHost().c_str(), this->GetPort(), sql.c_str()); - qryRes = PQexec(conn, sql.mb_str(*conv)); + PGresult *qryRes; + wxLogSql(wxT("Set query (%s:%d): %s"), this->GetHost().c_str(), this->GetPort(), sql.c_str()); + qryRes = PQexec(conn, sql.mb_str(*conv)); - lastResultStatus = PQresultStatus(qryRes); - SetLastResultError(qryRes); + lastResultStatus = PQresultStatus(qryRes); + SetLastResultError(qryRes); - if (lastResultStatus == PGRES_TUPLES_OK || lastResultStatus == PGRES_COMMAND_OK) - { - pgSet *set = new pgSet(qryRes, this, *conv, needColQuoting); - if (!set) - { - wxLogError(__("Couldn't create a pgSet object!")); - PQclear(qryRes); - } - return set; - } - else + if (lastResultStatus == PGRES_TUPLES_OK || lastResultStatus == PGRES_COMMAND_OK) + { + pgSet *set = new pgSet(qryRes, this, *conv, needColQuoting); + if (!set) { - LogError(); + wxLogError(__("Couldn't create a pgSet object!")); PQclear(qryRes); } + return set; + } + else + { + LogError(); + PQclear(qryRes); } + return new pgSet(); } @@ -823,8 +878,8 @@ void pgConn::LogError(const bool quiet) bool pgConn::IsAlive() { - if (GetStatus() != PGCONN_OK) - return false; + if (!CheckConnection()) + return false; PGresult *qryRes = PQexec(conn, "SELECT 1;"); lastResultStatus = PQresultStatus(qryRes); @@ -974,6 +1029,9 @@ wxString pgConn::qtString(const wxString &value) // Check if, TABLE (tblname) has column with name colname bool pgConn::TableHasColumn(wxString schemaname, wxString tblname, const wxString &colname) { + if (!CheckConnection()) + return false; + // // SELECT a.attname // FROM pg_catalog.pg_attribute a @@ -993,38 +1051,35 @@ bool pgConn::TableHasColumn(wxString schemaname, wxString tblname, const wxStrin if (schemaname.IsEmpty()) schemaname = wxT("public"); - if (this && GetStatus() == PGCONN_OK) + tblname.Replace(wxT("\\"), wxT("\\\\")); + tblname.Replace(wxT("'"), wxT("''")); + schemaname.Replace(wxT("\\"), wxT("\\\\")); + schemaname.Replace(wxT("'"), wxT("''")); + + wxString sql + = wxT("SELECT a.attname AS colname FROM pg_catalog.pg_attribute a ") \ + wxT("WHERE a.attrelid = (SELECT c.oid FROM pg_catalog.pg_class c ") \ + wxT(" LEFT JOIN pg_catalog.pg_namespace n ON ") \ + wxT(" n.oid = c.relnamespace ") \ + wxT(" WHERE c.relname ~ '^(") + tblname + wxT(")$' AND ") \ + wxT(" n.nspname ~ '^(") + schemaname + wxT(")$') AND ") \ + wxT(" a.attnum > 0 AND NOT a.attisdropped ") \ + wxT("ORDER BY a.attnum"); + + pgSet *set = ExecuteSet(sql); + if (set) { - tblname.Replace(wxT("\\"), wxT("\\\\")); - tblname.Replace(wxT("'"), wxT("''")); - schemaname.Replace(wxT("\\"), wxT("\\\\")); - schemaname.Replace(wxT("'"), wxT("''")); - - wxString sql - = wxT("SELECT a.attname AS colname FROM pg_catalog.pg_attribute a ") \ - wxT("WHERE a.attrelid = (SELECT c.oid FROM pg_catalog.pg_class c ") \ - wxT(" LEFT JOIN pg_catalog.pg_namespace n ON ") \ - wxT(" n.oid = c.relnamespace ") \ - wxT(" WHERE c.relname ~ '^(") + tblname + wxT(")$' AND ") \ - wxT(" n.nspname ~ '^(") + schemaname + wxT(")$') AND ") \ - wxT(" a.attnum > 0 AND NOT a.attisdropped ") \ - wxT("ORDER BY a.attnum"); - - pgSet *set = ExecuteSet(sql); - if (set) + while (!set->Eof()) { - while (!set->Eof()) + if (set->GetVal(wxT("colname")) == colname) { - if (set->GetVal(wxT("colname")) == colname) - { - delete set; - return true; - } - set->MoveNext(); + delete set; + return true; } + set->MoveNext(); } - delete set; } + delete set; return false; } diff --git a/pgadmin/db/pgQueryThread.cpp b/pgadmin/db/pgQueryThread.cpp index 010e48d..8f43d47 100644 --- a/pgadmin/db/pgQueryThread.cpp +++ b/pgadmin/db/pgQueryThread.cpp @@ -45,6 +45,8 @@ pgQueryThread::pgQueryThread(pgConn *_conn, const wxString &qry, int _resultToRe eventId = _eventId; data = _data; + conn->CheckConnection(); + wxLogSql(wxT("Thread query (%s:%d): %s"), conn->GetHost().c_str(), conn->GetPort(), qry.c_str()); conn->RegisterNoticeProcessor(pgNoticeProcessor, this); diff --git a/pgadmin/dlg/dlgProperty.cpp b/pgadmin/dlg/dlgProperty.cpp index 834687f..58b078c 100644 --- a/pgadmin/dlg/dlgProperty.cpp +++ b/pgadmin/dlg/dlgProperty.cpp @@ -2107,7 +2107,7 @@ propertyFactory::propertyFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuTool wxWindow *propertyFactory::StartDialog(frmMain *form, pgObject *obj) { if (!dlgProperty::EditObjectDialog(form, form->GetSqlPane(), obj)) - form->CheckAlive(); + obj->GetConnection()->CheckConnection(); return 0; } @@ -2130,7 +2130,7 @@ createFactory::createFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar wxWindow *createFactory::StartDialog(frmMain *form, pgObject *obj) { if (!dlgProperty::CreateObjectDialog(form, obj, 0)) - form->CheckAlive(); + obj->GetConnection()->CheckConnection(); return 0; } diff --git a/pgadmin/frm/events.cpp b/pgadmin/frm/events.cpp index f725d39..55d0ace 100644 --- a/pgadmin/frm/events.cpp +++ b/pgadmin/frm/events.cpp @@ -52,7 +52,6 @@ BEGIN_EVENT_TABLE(frmMain, pgFrame) EVT_MENU(MNU_OBJECTBROWSER, frmMain::OnToggleObjectBrowser) EVT_MENU(MNU_TOOLBAR, frmMain::OnToggleToolBar) EVT_MENU(MNU_DEFAULTVIEW, frmMain::OnDefaultView) - EVT_MENU(MNU_CHECKALIVE, frmMain::OnCheckAlive) EVT_MENU(MNU_CONTEXTMENU, frmMain::OnContextMenu) EVT_AUINOTEBOOK_PAGE_CHANGED(wxID_ANY, frmMain::OnPageChange) @@ -215,6 +214,9 @@ void frmMain::UpdateAllMacrosList() void frmMain::OnAction(wxCommandEvent &ev) { + if (currentObject && currentObject->GetConnection()) + currentObject->GetConnection()->CheckConnection(); + actionFactory *af = menuFactories->GetFactory(ev.GetId()); if (af) { @@ -267,13 +269,6 @@ void frmMain::OnExpand(wxTreeEvent &event) } -void frmMain::OnCheckAlive(wxCommandEvent &event) -{ - CheckAlive(); -} - - - void frmMain::OnPropSelChanged(wxListEvent &event) { if (properties->GetSelectedItemCount() == 1) @@ -454,7 +449,7 @@ void frmMain::setDisplay(pgObject *data, ctlListView *props, ctlSQLBox *sqlbox) pgConn *conn = data->GetConnection(); if (conn && (conn->GetStatus() == PGCONN_BROKEN || conn->GetStatus() == PGCONN_BAD)) { - CheckAlive(); + conn->CheckConnection(); return; } @@ -926,9 +921,8 @@ void frmMain::OnNew(wxCommandEvent &ev) { if (currentObject && currentObject->IsCreatedBy(serverFactory)) { - pgServer *server = (pgServer *)currentObject; - if (!server->GetConnected()) - ReconnectServer(server); + pgConn *conn = currentObject->GetConnection(); + conn->CheckConnection(); } return; } @@ -936,7 +930,10 @@ void frmMain::OnNew(wxCommandEvent &ev) if (currentObject) { if (!dlgProperty::CreateObjectDialog(this, currentObject, factory)) - CheckAlive(); + { + pgConn *conn = currentObject->GetConnection(); + conn->CheckConnection(); + } } } diff --git a/pgadmin/frm/frmMain.cpp b/pgadmin/frm/frmMain.cpp index 8248cc3..f252a84 100644 --- a/pgadmin/frm/frmMain.cpp +++ b/pgadmin/frm/frmMain.cpp @@ -545,7 +545,7 @@ void frmMain::Refresh(pgObject *data) // If the connection is dead, just return here if (data->GetConnection()->GetStatus() != PGCONN_OK) { - CheckAlive(); + data->GetConnection()->CheckConnection(); browser->Thaw(); return; } @@ -746,150 +746,6 @@ ctlListView *frmMain::GetReferencedBy() return dependents; } -bool frmMain::CheckAlive() -{ - bool userInformed = false; - bool closeIt = false; - - wxTreeItemIdValue foldercookie; - wxTreeItemId folderitem = browser->GetFirstChild(browser->GetRootItem(), foldercookie); - while (folderitem) - { - if (browser->ItemHasChildren(folderitem)) - { - wxCookieType cookie; - wxTreeItemId serverItem = browser->GetFirstChild(folderitem, cookie); - while (serverItem) - { - pgServer *server = (pgServer *)browser->GetObject(serverItem); - - if (server && server->IsCreatedBy(serverFactory) && server->connection()) - { - if (server->connection()->IsAlive()) - { - wxCookieType cookie2; - wxTreeItemId item = browser->GetFirstChild(serverItem, cookie2); - while (item) - { - pgObject *obj = browser->GetObject(item); - if (obj && obj->IsCreatedBy(databaseFactory.GetCollectionFactory())) - { - wxCookieType cookie3; - item = browser->GetFirstChild(obj->GetId(), cookie3); - while (item) - { - pgDatabase *db = (pgDatabase *)browser->GetObject(item); - if (db && db->IsCreatedBy(databaseFactory)) - { - pgConn *conn = db->GetConnection(); - if (conn) - { - if (!conn->IsAlive() && (conn->GetStatus() == PGCONN_BROKEN || conn->GetStatus() == PGCONN_BAD)) - { - conn->Close(); - if (!userInformed) - { - wxMessageDialog dlg(this, _("Do you want to attempt to reconnect to the database?"), - wxString::Format(_("Connection to database %s lost."), db->GetName().c_str()), - wxICON_EXCLAMATION | wxYES_NO | wxYES_DEFAULT); - - closeIt = (dlg.ShowModal() == wxID_NO); - userInformed = true; - } - if (closeIt) - { - db->Disconnect(); - - browser->DeleteChildren(db->GetId()); - db->UpdateIcon(browser); - } - else - { - // Create a server object and connect it. - wxBusyInfo waiting(wxString::Format(_("Reconnecting to database %s"), - db->GetName().c_str()), this); - - // Give the UI a chance to redraw - wxSafeYield(); - wxMilliSleep(100); - wxSafeYield(); - - if (!conn->Reconnect()) - { - db->Disconnect(); - - browser->DeleteChildren(db->GetId()); - db->UpdateIcon(browser); - } - else - // Indicate things are back to normal - userInformed = false; - } - - } - } - } - item = browser->GetNextChild(obj->GetId(), cookie3); - } - } - item = browser->GetNextChild(serverItem, cookie2); - } - } - else - { - if (server->connection()->GetStatus() == PGCONN_BROKEN || server->connection()->GetStatus() == PGCONN_BAD) - { - server->connection()->Close(); - if (!userInformed) - { - wxMessageDialog dlg(this, _("Do you want to attempt to reconnect to the server?"), - wxString::Format(_("Connection to server %s lost."), server->GetName().c_str()), - wxICON_EXCLAMATION | wxYES_NO | wxYES_DEFAULT); - - closeIt = (dlg.ShowModal() == wxID_NO); - userInformed = true; - } - if (closeIt) - { - server->Disconnect(this); - browser->SelectItem(serverItem); - execSelChange(serverItem, true); - browser->DeleteChildren(serverItem); - } - else - { - // Create a server object and connect it. - wxBusyInfo waiting(wxString::Format(_("Reconnecting to server %s (%s:%d)"), - server->GetDescription().c_str(), server->GetName().c_str(), server->GetPort()), this); - - // Give the UI a chance to redraw - wxSafeYield(); - wxMilliSleep(100); - wxSafeYield(); - - if (!server->connection()->Reconnect()) - { - server->Disconnect(this); - browser->SelectItem(serverItem); - execSelChange(serverItem, true); - browser->DeleteChildren(serverItem); - } - else - // Indicate things are back to normal - userInformed = false; - } - } - } - } - - serverItem = browser->GetNextChild(serversObj->GetId(), cookie); - } - } - folderitem = browser->GetNextChild(browser->GetRootItem(), foldercookie); - } - return userInformed; -} - wxTreeItemId frmMain::RestoreEnvironment(pgServer *server) { diff --git a/pgadmin/frm/frmQuery.cpp b/pgadmin/frm/frmQuery.cpp index ce38908..f72e4ff 100644 --- a/pgadmin/frm/frmQuery.cpp +++ b/pgadmin/frm/frmQuery.cpp @@ -2439,18 +2439,10 @@ void frmQuery::OnQueryComplete(wxCommandEvent &ev) wxLogQuietError(wxT("%s"), conn->GetLastError().Trim().c_str()); err.statement_pos.ToLong(&errPos); - if (err.sql_state.IsEmpty()) - { - if (wxMessageBox(_("Do you want to attempt to reconnect to the database?"), - wxString::Format(_("Connection to database %s lost."), conn->GetDbname().c_str()), - wxICON_EXCLAMATION | wxYES_NO) == wxYES) - { - conn->Reset(); - errMsg2 = _("Connection reset."); - } - } - showMessage(wxString::Format(wxT("********** %s **********\n"), _("Error"))); + + conn->CheckConnection(); + showMessage(errMsg); if (!errMsg2.IsEmpty()) showMessage(errMsg2); diff --git a/pgadmin/include/db/pgConn.h b/pgadmin/include/db/pgConn.h index cdacbba..222f401 100644 --- a/pgadmin/include/db/pgConn.h +++ b/pgadmin/include/db/pgConn.h @@ -122,6 +122,7 @@ public: } void Close(); + bool CheckConnection(); bool Reconnect(); bool ExecuteVoid(const wxString &sql, bool reportError = true); wxString ExecuteScalar(const wxString &sql); diff --git a/pgadmin/include/frm/frmMain.h b/pgadmin/include/frm/frmMain.h index 455f253..1d2086c 100644 --- a/pgadmin/include/frm/frmMain.h +++ b/pgadmin/include/frm/frmMain.h @@ -94,7 +94,6 @@ public: { currentObject = data; } - bool CheckAlive(); void execSelChange(wxTreeItemId item, bool currentNode); void Refresh(pgObject *data); @@ -219,8 +218,6 @@ private: void OnDelete(wxCommandEvent &ev); void OnCopy(wxCommandEvent &ev); - void OnCheckAlive(wxCommandEvent &event); - void OnPositionStc(wxStyledTextEvent &event); void ResetLists(); diff --git a/pgadmin/schema/pgObject.cpp b/pgadmin/schema/pgObject.cpp index e085ee8..1142af1 100644 --- a/pgadmin/schema/pgObject.cpp +++ b/pgadmin/schema/pgObject.cpp @@ -717,15 +717,9 @@ void pgObject::ShowDependents(frmMain *form, ctlListView *referencedBy, const wx void pgObject::ShowTree(frmMain *form, ctlTree *browser, ctlListView *properties, ctlSQLBox *sqlPane) { pgConn *conn = GetConnection(); - if (conn) - { - int status = conn->GetStatus(); - if (status == PGCONN_BROKEN || status == PGCONN_BAD) - { - form->SetStatusText(_(" Connection broken.")); - return; - } - } + + if (!(conn && conn->CheckConnection())) + return; wxLogInfo(wxT("Displaying properties for %s %s"), GetTypeName().c_str(), GetIdentifier().c_str()); diff --git a/pgadmin/slony/slCluster.cpp b/pgadmin/slony/slCluster.cpp index f27db7e..94cc686 100644 --- a/pgadmin/slony/slCluster.cpp +++ b/pgadmin/slony/slCluster.cpp @@ -557,7 +557,7 @@ wxWindow *slonyRestartFactory::StartDialog(frmMain *form, pgObject *obj) wxMessageDialog dlg(form, wxString::Format(_("Node \"%s\" not running"), cluster->GetLocalNodeName().c_str()), _("Can't restart node"), wxICON_EXCLAMATION | wxOK); dlg.ShowModal(); - form->CheckAlive(); + cluster->GetConnection()->CheckConnection(); return 0; } @@ -570,7 +570,7 @@ wxWindow *slonyRestartFactory::StartDialog(frmMain *form, pgObject *obj) if (!cluster->GetDatabase()->ExecuteVoid( wxT("NOTIFY ") + qtIdent(notifyName))) - form->CheckAlive(); + cluster->GetConnection()->CheckConnection(); return 0; }