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
00029
00030
00031 CVSID("$Id: DbiConnection.cxx,v 1.32 2009/04/18 08:12:37 west Exp $");
00032
00033
00034
00035
00036
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
00056
00057 LEA_CTOR
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
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
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
00127
00128 LEA_DTOR
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 ) {
00137
00138
00139
00140
00141
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
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
00183
00184
00185
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
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221 if ( ! this->Open() ) return 0;
00222 return fServer;
00223 }
00224
00225
00226 const std::string& DbiConnection::GetUrl() const {
00227
00228
00229
00230
00231
00232
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
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
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
00280
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
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
00320
00321
00322
00323 MSG("Dbi",level) << fExceptionLog;
00324 return fExceptionLog.Size() != 0;
00325
00326 }
00327
00328
00329
00330 void DbiConnection::RecordException() {
00331
00332
00333
00334 fExceptionLog.AddEntry(*fServer);
00335
00336 }
00337
00338
00339
00340 void DbiConnection::SetTableExists(const std::string& tableName) {
00341
00342
00343
00344
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
00374
00375 std::string test("'");
00376 test += tableName;
00377 test += "'";
00378 return fExistingTableList.find(test) != std::string::npos;
00379 }