Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

DbiConnection.cxx

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

Generated on Mon Feb 15 11:06:34 2010 for loon by  doxygen 1.3.9.1