TDbiConnection.cxx

Go to the documentation of this file.
00001 
00002 //////////////////////////////////////////////////////////////////////////
00003 ////////////////////////////     ROOT API     ////////////////////////////
00004 //////////////////////////////////////////////////////////////////////////
00005 
00006 
00007 #include <cctype>
00008 #include <cstdlib>
00009 #include <list>
00010 #include <sstream>
00011 #include <string>
00012 
00013 #include "TList.h"
00014 #include "TString.h"
00015 #include "TSystem.h"
00016 
00017 #include "TDbi.hxx"
00018 #include "TDbiAsciiDbImporter.hxx"
00019 #include "TDbiConnection.hxx"
00020 #include "TDbiExceptionLog.hxx"
00021 #include "TDbiServices.hxx"
00022 #include <TSK_DBI_Log.hxx>
00023 #include <MsgFormat.h>
00024 using std::endl;
00025 #include <TSK_DBI_Log.hxx>
00026 
00027 ClassImp(TDbiConnection)
00028 
00029 
00030 //   Definition of static data members
00031 //   *********************************
00032 
00033 
00034 //    Definition of all member functions (static or otherwise)
00035 //    *******************************************************
00036 //
00037 //    -  ordered: ctors, dtor, operators then in alphabetical order.
00038 
00039 //.....................................................................
00040 //      Throws EBadConnection() if can not make connection
00041 TDbiConnection::TDbiConnection(const std::string& url      /* = "" */,
00042                              const std::string& user     /* = "" */,
00043                              const std::string& password /* = "" */,
00044                              int maxConnects) :
00045 
00046 fUrl(url.c_str()),
00047 fUser(user),
00048 fPassword(password),
00049 fUrlValidated(false),
00050 fNumConnectedStatements(0),
00051 fIsTemporary(true),
00052 fServer(0)
00053 {
00054 //
00055 //
00056 //  Purpose:  Default constructor
00057 
00058   fMaxConnectionAttempts = maxConnects;
00059 
00060   SK_DBI_Trace( "Creating TDbiConnection" << "  ");
00061 
00062   if ( this->Open() ) {
00063     SK_DBI_Info(  "Successfully opened connection to: " << this->GetUrl() << "  ");
00064     fUrlValidated =  true;
00065 
00066     // Initialise the list existing supported tables.
00067     this->SetTableExists();
00068 
00069     //  If URL looks O.K., check that both client and server support prepared statements.
00070     if ( fUrlValidated ) {
00071 #if ROOT_VERSION_CODE >= ROOT_VERSION(5,15,9)
00072       if ( ! fServer->HasStatement() ) {
00073 #else
00074       if ( ! fServer->IsSupportStatement() ) {
00075 #endif
00076         SK_DBI_Severe( "  This client does not support prepared statements." << "  ");
00077         fUrlValidated = false;
00078         #if ROOT_VERSION_CODE >= ROOT_VERSION(5,15,9)
00079       }
00080 #else
00081       }
00082 #endif
00083       
00084       std::string serverInfo(fServer->ServerInfo());
00085       if ( serverInfo < "4.1" ) {
00086         SK_DBI_Severe( "This MySQL server (" << serverInfo
00087                              << ") does NOT support prepared statements." << "  ");
00088         fUrlValidated = false;
00089       }
00090       if ( fUrlValidated ) {
00091         SK_DBI_Info( "This client, and MySQL server (" << serverInfo
00092                              << ") supports prepared statements." << "  ");
00093       }
00094       else {
00095 
00096         SK_DBI_Severe( "\n"
00097            << "This version of MySQL does not support prepared statements.\n"
00098            << "\n"
00099            << "Please upgrade to MySQL (client and server) version 4.1 or greater \n"
00100            << "\n"
00101            << "  ");
00102       }
00103 
00104     }
00105   }
00106     if ( ! fUrlValidated ) {
00107       SK_DBI_Severe( "FATAL: " << "Aborting due to above errors" << "  ");
00108       throw EBadConnection();
00109     }
00110    fDbName = fUrl.GetFile();
00111   
00112 
00113 }
00114 
00115 //.....................................................................
00116 
00117 TDbiConnection::~TDbiConnection() {
00118 //
00119 //
00120 //  Purpose: Destructor
00121 
00122 
00123   SK_DBI_Trace( "Destroying TDbiConnection" << "  ");
00124   this->Close(true);
00125 
00126 }
00127 //.....................................................................
00128 ///\verbatim
00129 ///
00130 ///  Purpose: Close server connection unless active (or always if forced) .
00131 ///
00132 ///  Return:  true if connection now closed.
00133 ///\endverbatim
00134 Bool_t TDbiConnection::Close(Bool_t force /* = false */ ) {
00135 
00136   this->ClearExceptionLog();
00137   if ( this->IsClosed() ) return true;
00138 
00139   if ( fNumConnectedStatements ) {
00140     if ( ! force ) {
00141       SK_DBI_Info( "Unable to close connection: " << this->GetUrl()
00142                             << "; it still has  "
00143                             << fNumConnectedStatements << "active statements. " << "  ");
00144       return false;
00145     }
00146     SK_DBI_Info( "Closing connection: " << this->GetUrl()
00147                           << "; even though it still has "
00148                           << fNumConnectedStatements << " active statements. " << "  ");
00149   }
00150 
00151   delete fServer;
00152   fServer = 0;
00153   SK_DBI_Debug( "Closed connection: " << this->GetUrl() << "  ");
00154   return true;
00155 
00156 }
00157 
00158 //.....................................................................
00159 ///
00160 ///
00161 ///  Purpose:  Close idle connection. Idle means there are no active connections to this database.
00162 
00163 void TDbiConnection::CloseIdleConnection() {
00164 
00165   if ( fIsTemporary &&  fNumConnectedStatements == 0 ) this->Close();
00166 
00167 }
00168 
00169 
00170 //.....................................................................
00171 ///\verbatim
00172 ///
00173 ///  Purpose:  Open if necessary and get a prepared statment.
00174 ///
00175 ///  Return:    Statement - Caller must take ownership.
00176 ///             will be 0 if failure.
00177 ///\endverbatim
00178 TSQLStatement* TDbiConnection::CreatePreparedStatement(const std::string& sql) {
00179 
00180   TSQLStatement* stmt = 0;
00181   if ( ! this->Open() ) return stmt;
00182   stmt = fServer->Statement(sql.c_str());
00183   if ( ! stmt ) {
00184     fExceptionLog.AddEntry(*fServer);
00185   }
00186   else stmt->EnableErrorOutput(false);
00187 
00188   return stmt;
00189 }
00190 //.....................................................................
00191 ///\verbatim
00192 ///
00193 ///  Purpose:  Open if necessary and get a TSQLServer.
00194 ///
00195 ///  Return:    Server ( = 0 if connection not open).
00196 ///
00197 ///  WARNING:  The server returned remains is being borrowed from the
00198 ///            TDbiConnection and remains under its ownership and must
00199 ///            not be deleted.  However the caller must invoke the
00200 ///            Connect() method on this TDbiConnection before borrowing
00201 ///            it and must invoke the DisConnect() when it has finished
00202 ///            using it to ensure the TDbiConnection does not close it
00203 ///            prematurely i.e.:-
00204 ///
00205 ///            void Demo(TDbiConnection* con) {
00206 ///              con->Connect();
00207 ///              TSQLServer* server = con->GetServer();
00208 ///              // Do stuff
00209 ///              con->DisConnect();
00210 ///            }
00211 ///\endverbatim
00212 TSQLServer* TDbiConnection::GetServer() {
00213 
00214 
00215   if ( ! this->Open() ) return 0;
00216   return fServer;
00217 }
00218 
00219 //.....................................................................
00220 ///\verbatim
00221 /// Don't ask me why TUrl::GetUrl() is non-const, just accept that it is!
00222 ///
00223 /// Note: This function returns a reference to a shared string; use the
00224 ///       value or make a copy of it before any subsequent call to this
00225 ///       function.
00226 ///\endverbatim
00227 const std::string& TDbiConnection::GetUrl() const {
00228 
00229   
00230 
00231   static std::string url;
00232   url = const_cast<TDbiConnection*>(this)->fUrl.GetUrl();
00233   return url;
00234 
00235 }
00236 
00237 //.....................................................................
00238 ///
00239 ///  Purpose:  Open connection if necessary.
00240 ///
00241 
00242 Bool_t TDbiConnection::Open() {
00243 
00244 
00245   this->ClearExceptionLog();
00246   if ( ! this->IsClosed() ) return true;
00247 
00248   if ( ! fUrl.IsValid() ) {
00249 
00250     ostringstream oss;
00251     oss << "Unable to open connection: URL '" << fUrl.GetUrl() << "' is invalid";
00252        SK_DBI_Severe( oss.str() << "  ");
00253     fExceptionLog.AddEntry(oss.str());
00254     return false;
00255   }
00256 
00257   // Make several attempts (or more if URL is known to be O.K.) to open connection.
00258   int maxAttempt = fUrlValidated ?  100: fMaxConnectionAttempts ;
00259   for (int attempt = 1; attempt <= maxAttempt; attempt++) {
00260     fServer = TSQLServer::Connect(fUrl.GetUrl(),fUser.c_str(),fPassword.c_str());
00261 
00262     if ( ! fServer ) {
00263       ostringstream oss;
00264       oss << "Failing to open: " << fUrl.GetUrl() << " for user " << fUser
00265           << " and password " << fPassword << " (attempt " << attempt << ")";
00266       fExceptionLog.AddEntry(oss.str());
00267       if ( fMaxConnectionAttempts > attempt ){
00268          
00269          if ( attempt == 1 ) {
00270               SK_DBI_Severe( " retrying ... " << "  ");
00271          }
00272          SK_DBI_Log(" Waiting "<<attempt<<" seconds before trying again");
00273          gSystem->Sleep(attempt*1000);
00274       }
00275     }
00276 
00277     else {
00278       fServer->EnableErrorOutput(false);
00279       if ( attempt > 1 ) SK_DBI_Warn( "... Connection opened on attempt " << attempt << "  ");
00280       SK_DBI_Debug(  "Successfully opened connection to: " << fUrl.GetUrl() << "  ");
00281 
00282       // If this is an ASCII database, populate it and make the connection permanent
00283       // unless even ASCII DB connections are temporary.
00284 
00285       TString ascii_file = fUrl.GetAnchor();
00286       if ( ascii_file.IsNull() ) return true;
00287       gSystem->Setenv("DBI_CATALOGUE_PATH",gSystem->DirName(fUrl.GetAnchor()));
00288       TDbiAsciiDbImporter importer(ascii_file,fServer);
00289       const TDbiExceptionLog& el(importer.GetExceptionLog());
00290       if ( ! el.IsEmpty() ) {
00291         SK_DBI_Severe( "Failed to populate ASCII database from " << fUrl.GetUrl() << "\n"
00292                                << el << "  ");
00293         delete fServer;
00294         fServer = 0;
00295         return false;
00296       }
00297       fIsTemporary = TDbiServices::AsciiDBConectionsTemporary();
00298       // Add imported tables names.
00299       const std::list<std::string> tableNames(importer.GetImportedTableNames());
00300       std::list<std::string>::const_iterator itr(tableNames.begin()), itrEnd(tableNames.end());
00301       while ( itr != itrEnd ) {
00302         this->SetTableExists(*itr);
00303         ++itr;
00304       }
00305       return true;
00306 
00307     }
00308   }
00309      SK_DBI_Severe(  "... Failed to open a connection to: " << fUrl.GetUrl()
00310     << " for user " << fUser << " and pwd " << fPassword << "  ");
00311 
00312   return false;
00313 
00314 }//
00315 //  Purpose:  Record an exception that has occurred while a client was using its TSQLServer.
00316 
00317 
00318 //.....................................................................
00319 ///\verbatim
00320 ///
00321 ///  Purpose:  Print all warning at supplied  Msg log level.
00322 ///
00323 ///  Return:    kTRUE if warnings have occurred
00324 ///\endverbatim
00325 Bool_t TDbiConnection::PrintExceptionLog(Int_t level) const {
00326 
00327 
00328   return fExceptionLog.Size() != 0;
00329 
00330 }
00331 
00332 //.....................................................................
00333 ///
00334 ///  Purpose:  Record an exception that has occurred while a client was using its TSQLServer.
00335 
00336 void  TDbiConnection::RecordException() {
00337 
00338   fExceptionLog.AddEntry(*fServer);
00339 
00340 }
00341 
00342 //.....................................................................
00343 ///\verbatim
00344 ///  Purpose: Add name to list of existing tables (necessary when creating tables)
00345 ///
00346 ///  Note: If tableName is null refresh list from the database.
00347 ///\enbdverbatim
00348 void  TDbiConnection::SetTableExists(const std::string& tableName) {
00349 
00350   if ( tableName == "" ) {
00351     TSQLStatement* stmt =  CreatePreparedStatement("show tables");
00352     if ( stmt ) {
00353       if (stmt->Process()) {
00354         stmt->StoreResult();
00355         while (stmt->NextResultRow()) {
00356           std::string tn(stmt->GetString(0));
00357           this->SetTableExists(tn);
00358         }
00359       }
00360       delete stmt;
00361       stmt = 0;
00362     }
00363   }
00364   else {
00365     if ( ! this->TableExists(tableName) ) {
00366       fExistingTableList += ",'";
00367       fExistingTableList += tableName;
00368       fExistingTableList += "'";
00369     }
00370   }
00371 }
00372 
00373 //.....................................................................
00374 ///  Purpose: Check to see table exists in connected database.
00375 Bool_t  TDbiConnection::TableExists(const std::string& tableName) const {
00376 //
00377 
00378   std::string test("'");
00379   test += tableName;
00380   test += "'";
00381   return fExistingTableList.find(test) != std::string::npos;
00382 }
00383 

Generated on 11 Aug 2013 for SKDatabase by  doxygen 1.6.1