00001
00005
00006
00007 #include <cstdlib>
00008 #include <memory>
00009 using std::auto_ptr;
00010 #include <sstream>
00011
00012 #include "TList.h"
00013 #include "TROOT.h"
00014 #include "TSQLStatement.h"
00015 #include "TSystem.h"
00016
00017 #include "DatabaseInterface/Dbi.h"
00018 #include "DatabaseInterface/DbiCascader.h"
00019 #include "DatabaseInterface/DbiString.h"
00020 #include "LeakChecker/Lea.h"
00021 #include "MessageService/MsgService.h"
00022 #include "Util/UtilString.h"
00023
00024 ClassImp(DbiCascader)
00025
00026
00027
00028
00029 CVSID("$Id: DbiCascader.cxx,v 1.73 2009/04/18 08:12:37 west Exp $");
00030
00031
00032
00033
00034
00035
00036
00037
00038 DbiCascader::DbiCascader():
00039 fGlobalSeqNoDbNo(-1)
00040 {
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071 LEA_CTOR
00072
00073 MSG("Dbi", Msg::kVerbose) << "Creating DbiCascader" << endl;
00074
00075
00076 const char* strUser = gSystem->Getenv("ENV_TSQL_UPDATE_USER");
00077 if ( ! strUser ) strUser = gSystem->Getenv("ENV_TSQL_USER");
00078 const char* strPswd = gSystem->Getenv("ENV_TSQL_UPDATE_PSWD");
00079 if ( ! strPswd ) strPswd = gSystem->Getenv("ENV_TSQL_PSWD");
00080 const char* strUrl = gSystem->Getenv("ENV_TSQL_UPDATE_URL");
00081 if ( !strUrl ) strUrl = gSystem->Getenv("ENV_TSQL_URL");
00082 string userList = ( strUser ) ? strUser : "";
00083 string pswdList = ( strPswd ) ? strPswd : "";
00084 string urlList = ( strUrl ) ? strUrl : "";
00085
00086 if ( urlList == "" || userList == "" || pswdList == "" ) {
00087 MSG("Dbi",Msg::kFatal)
00088 << "Cannnot open a Database cascade;\n"
00089 << " the environmental variables ENV_TSQL_*:-" << endl
00090 << "USER: \"" << userList << "\" PSWD:\"" << pswdList
00091 << "\" URL:\"" << urlList << endl
00092 << " are either not defined or empty.\n"
00093 << " Please check your settings of ENV_TSQL_USER,"
00094 << " ENV_TSQL_PSWD and ENV_TSQL_URL\n"
00095 << "Aborting due to above errors" << endl;
00096 exit(1);
00097 }
00098
00099 std::vector<std::string> users, pswds, urls;
00100 UtilString::StringTok(users, userList, ";");
00101 UtilString::StringTok(pswds, pswdList, ";");
00102 UtilString::StringTok(urls, urlList, ";");
00103
00104 bool fail = false;
00105
00106 for (unsigned entry = 0; entry < urls.size(); ++entry ) {
00107 string url = urls[entry];
00108 string user = ( entry >= users.size() ) ? users[0] : users[entry];
00109 string pswd = ( entry >= pswds.size() ) ? pswds[0] : pswds[entry];
00110
00111
00112 if ( pswd == "\\0" ) pswd = "";
00113
00114 DbiConnection* con = new DbiConnection(url,user,pswd);
00115 fConnections.push_back(con);
00116 if ( ! con->Open() ) {
00117 fail = true;
00118 continue;
00119 }
00120
00121
00122 if ( fGlobalSeqNoDbNo != -1 ) continue;
00123 auto_ptr<DbiStatement> stmtDb(new DbiStatement(*con));
00124 if ( ! stmtDb.get() ) continue;
00125 TSQLStatement* stmt = stmtDb->ExecuteQuery("Select * from GLOBALSEQNO where 1=0");
00126 if ( stmt ) {
00127 fGlobalSeqNoDbNo = fConnections.size()-1;
00128 delete stmt;
00129 stmt = 0;
00130 }
00131
00132
00133
00134 if ( this->GetTableDbNo("DBI_STATE_FLAGS",entry) != -1 ) {
00135 MSG("Dbi",Msg::kFatal) << " POSSIBLE VERSION SHEAR DETECTED !!!\n"
00136 << " The DBI_STATE_FLAGS table is present on cascade entry " << entry << ". This table will\n"
00137 << " only be introduced to manage backward incompatible changes that could lead\n"
00138 << " to version shear between the code and the database. This version of the\n"
00139 << " code does not support the change the presence of that table indicates\n"
00140 << " so has to shut down. \n";
00141 fail = true;
00142 }
00143 }
00144
00145 MSG("Dbi",Msg::kInfo) << *this;
00146
00147
00148 if ( fail ) {
00149 MSG("Dbi",Msg::kFatal)
00150 << "Aborting due to above errors" << endl;
00151 exit(1);
00152 }
00153
00154 }
00155
00156
00157
00158 DbiCascader::~DbiCascader() {
00159
00160
00161
00162
00163
00164 LEA_DTOR
00165
00166 MSG("Dbi", Msg::kVerbose) << "Destroying DbiCascader" << endl;
00167
00168 for (Int_t dbNo = this->GetNumDb()-1; dbNo >= 0; --dbNo) delete fConnections[dbNo];
00169
00170 }
00171
00172
00173
00174 ostream& operator<<(ostream& os, const DbiCascader& cascader) {
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197 os << "DbiCascader Status:- " << endl
00198 << "Status URL" << endl << endl;
00199
00200 int maxDb = cascader.GetNumDb();
00201 for (Int_t dbNo = 0; dbNo < maxDb; ++dbNo)
00202 os << cascader.GetStatusAsString(dbNo) << " "
00203 << ( ( dbNo == cascader.fGlobalSeqNoDbNo ) ? "(auth) " : " ")
00204 << cascader.GetURL(dbNo) << endl;
00205 os << endl;
00206 return os;
00207
00208 }
00209
00210
00211
00212 Int_t DbiCascader::AllocateSeqNo(const string& tableName,
00213 Int_t requireGlobal,
00214 Int_t dbNo ) const {
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241 bool isTemporary = IsTemporaryTable(tableName,dbNo);
00242
00243
00244
00245 if ( requireGlobal > 0
00246 || ( requireGlobal == 0 && dbNo == fGlobalSeqNoDbNo && ! isTemporary ) ) {
00247 if ( fGlobalSeqNoDbNo < 0 ) {
00248 MSG("Dbi",Msg::kWarning) << "Unable to issue global SEQNO - no authorising DB in cascade\n"
00249 << " will issue local one instead" << endl;
00250 }
00251 else if ( isTemporary ) {
00252 MSG("Dbi",Msg::kWarning) << "Unable to issue global SEQNO - " << tableName << " is temporary\n"
00253 << " will issue local one instead" << endl;
00254 }
00255 else return this->ReserveNextSeqNo(tableName,true,fGlobalSeqNoDbNo);
00256 }
00257
00258
00259
00260 return this->ReserveNextSeqNo(tableName,false,dbNo);
00261
00262 }
00263
00264
00265
00266 DbiStatement* DbiCascader::CreateStatement(UInt_t dbNo) const {
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291 if ( this->GetStatus(dbNo) == kFailed ) return 0;
00292 DbiConnection& conDb = *fConnections[dbNo];
00293 DbiStatement* stmtDb = new DbiStatement(conDb);
00294 stmtDb->PrintExceptions();
00295 return stmtDb;
00296
00297 }
00298
00299
00300 Int_t DbiCascader::CreateTemporaryTable(const string& tableNameMc,
00301 const string& tableDescr) {
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335 string tableName = UtilString::ToUpper(tableNameMc);
00336 if ( tableName == ""
00337 || tableDescr[0] != '('
00338 || tableDescr[tableDescr.size()-1] != ')' ) {
00339 MAXMSG("Dbi",Msg::kError,20) << "Illegal input args:-" << endl
00340 << " Table Name: " << tableName
00341 << " Table Description: " << tableDescr
00342 <<endl;
00343 return -1;
00344 }
00345
00346
00347 string sqlMakeTable;
00348
00349 Int_t dbNoAcc = -1;
00350 auto_ptr<DbiStatement> stmtDb;
00351 for (UInt_t dbNoTry = 0; dbNoTry < fConnections.size(); ++dbNoTry ) {
00352 stmtDb.reset(this->CreateStatement(dbNoTry));
00353 if ( stmtDb.get() ) {
00354 sqlMakeTable = " create temporary table ";
00355 sqlMakeTable += tableName;
00356 sqlMakeTable += " ";
00357 sqlMakeTable += tableDescr;
00358 sqlMakeTable += ";";
00359 stmtDb->ExecuteUpdate(sqlMakeTable.c_str());
00360 if ( stmtDb->GetExceptionLog().IsEmpty() ) {
00361 dbNoAcc = dbNoTry;
00362 this->GetConnection(dbNoAcc)->SetTableExists(tableName);
00363 break;
00364 }
00365 if ( MsgService::Instance()->IsActive("Dbi",Msg::kSynopsis) )stmtDb->PrintExceptions();
00366 }
00367 }
00368
00369 if ( dbNoAcc < 0 ) {
00370 if ( stmtDb.get()) stmtDb->PrintExceptions();
00371 return -1;
00372 }
00373
00374
00375 DbiConnection& conDb = *fConnections[dbNoAcc];
00376 if ( conDb.IsTemporary() ) {
00377 conDb.SetPermanent();
00378 MSG("Dbi",Msg::kInfo) << "Making connection: " << conDb.GetUrl()
00379 << " permanent to preserve temporary tables." << endl;
00380 }
00381
00382
00383 sqlMakeTable = Dbi::GetVldDescr(tableName.c_str(),true);
00384
00385 MSG("Dbi",Msg::kSynopsis) << "Validity Table creation: "
00386 << " Database: " << dbNoAcc << " "
00387 << sqlMakeTable << endl;
00388 stmtDb->ExecuteUpdate(sqlMakeTable.c_str());
00389 if ( stmtDb->PrintExceptions() ) return -1;
00390 this->GetConnection(dbNoAcc)->SetTableExists(tableName+"VLD");
00391 fTemporaryTables[tableName] = dbNoAcc;
00392 return dbNoAcc;
00393
00394 }
00395
00396
00397
00398 const DbiConnection* DbiCascader::GetConnection(UInt_t dbNo) const{
00399
00400
00401
00402
00403
00404 if ( this->GetStatus(dbNo) == kFailed ) return 0;
00405 return fConnections[dbNo];
00406
00407 }
00408
00409
00410 DbiConnection* DbiCascader::GetConnection(UInt_t dbNo) {
00411
00412
00413
00414
00415
00416 if ( this->GetStatus(dbNo) == kFailed ) return 0;
00417 return fConnections[dbNo];
00418
00419 }
00420
00421
00422
00423
00424 string DbiCascader::GetDbName(UInt_t dbNo) const {
00425
00426
00427
00428
00429 string dbName;
00430
00431 if ( dbNo < this->GetNumDb() ) dbName = fConnections[dbNo]->GetDbName();
00432 else MSG("Dbi",Msg::kWarning) << "Database does not contain entry " << dbNo << endl;
00433 return dbName;
00434
00435 }
00436
00437
00438
00439 Int_t DbiCascader::GetDbNo(const string& dbName) const {
00440
00441
00442
00443
00444
00445
00446 for ( unsigned dbNo = 0; dbNo < this->GetNumDb(); ++dbNo) {
00447 if ( dbName == fConnections[dbNo]->GetDbName() ) return dbNo;
00448 }
00449
00450 MSG("Dbi",Msg::kWarning) << "Database does not contain entry " << dbName << endl;
00451 return -1;
00452
00453 }
00454
00455
00456
00457 string DbiCascader::GetStatusAsString(UInt_t dbNo) const {
00458
00459
00460
00461
00462
00463
00464
00465 Int_t status = GetStatus(dbNo);
00466
00467 switch ( status ) {
00468 case kClosed: return "Closed";
00469 case kOpen: return "Open ";
00470 default: return "Failed";
00471 }
00472
00473 }
00474
00475
00476 Int_t DbiCascader::GetTableDbNo(const string& tableName,
00477 Int_t selectDbNo ) const {
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489 string::const_iterator itr = tableName.begin();
00490 string::const_iterator itrEnd = tableName.end();
00491 while ( itr != itrEnd ) if ( islower(*itr++) ) return -1;
00492
00493
00494
00495 for (UInt_t dbNoTry = 0; dbNoTry < fConnections.size(); ++dbNoTry ) {
00496 if ( selectDbNo >= 0 && (UInt_t) selectDbNo != dbNoTry ) continue;
00497 const DbiConnection* con = this->GetConnection(dbNoTry);
00498 if ( con && con->TableExists(tableName) ) return dbNoTry;
00499 }
00500
00501 return -1;
00502
00503 }
00504
00505
00506 void DbiCascader::HoldConnections() {
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522 for (UInt_t dbNo = 0; dbNo < fConnections.size(); ++dbNo )
00523 fConnections[dbNo]->ConnectStatement();
00524 }
00525
00526
00527
00528 Bool_t DbiCascader::IsTemporaryTable(const string& tableName,
00529 Int_t dbNo) const {
00530
00531
00532
00533
00534 map<string,Int_t>::const_iterator itr
00535 = fTemporaryTables.find(tableName);
00536 return ( itr != fTemporaryTables.end()
00537 && (*itr).second == dbNo );
00538
00539 }
00540
00541
00542
00543
00544
00545
00546 DbiCascader::Lock::Lock(DbiStatement* stmtDB, const string& seqnoTable, const string& dataTable) :
00547 fStmt(stmtDB),
00548 fSeqnoTableName(seqnoTable),
00549 fDataTableName(dataTable),
00550 fLocked(kFALSE)
00551 {
00552
00553
00554
00555
00556
00557
00558
00559 LEA_CTOR_NM("DbiCascader::Lock",this);
00560
00561 if ( ! fStmt ) {
00562 MAXMSG("Dbi",Msg::kError,20) << "Cannot obtain statment to set lock" << endl;
00563 return;
00564 }
00565
00566 this->SetLock(kTRUE);
00567
00568 }
00569
00570
00571 DbiCascader::Lock::~Lock() {
00572
00573
00574
00575
00576 LEA_DTOR_NM("DbiCascader::Lock",this);
00577
00578 this->SetLock(kFALSE);
00579 delete fStmt;
00580 fStmt = 0;
00581
00582 }
00583
00584
00585
00586 void DbiCascader::Lock::SetLock(Bool_t setting) {
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600 if ( setting == fLocked || ! fStmt ) return;
00601
00602 if ( fStmt->GetDBType() != Dbi::kMySQL ) {
00603 fLocked = setting;
00604 return;
00605 }
00606
00607 string sql;
00608
00609 if ( setting ) {
00610 sql = "LOCK TABLES ";
00611 sql += fSeqnoTableName + " WRITE";
00612 if ( fDataTableName != "" ) sql += ", " + fDataTableName + "VLD WRITE";
00613 }
00614 else {
00615 sql = "UNLOCK TABLES;";
00616 }
00617 MSG("Dbi",Msg::kSynopsis) << "Lock requested: " << setting
00618 << " issuing lock command: " << sql << endl;
00619 fStmt->ExecuteUpdate(sql.c_str());
00620 if ( fStmt->GetExceptionLog().IsEmpty() ) fLocked = setting;
00621 fStmt->PrintExceptions();
00622
00623 }
00624
00625
00626 void DbiCascader::ReleaseConnections() {
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643 for (UInt_t dbNo = 0; dbNo < fConnections.size(); ++dbNo )
00644 fConnections[dbNo]->DisConnectStatement();
00645 }
00646
00647
00648 Int_t DbiCascader::ReserveNextSeqNo(const string& tableName,
00649 Bool_t isGlobal,
00650 UInt_t dbNo) const {
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671 DbiString sql;
00672
00673 string seqnoTableName = isGlobal ? "GLOBALSEQNO" : "LOCALSEQNO";
00674 bool seqnoTableNameExists = this->TableExists(seqnoTableName,dbNo);
00675 bool tableNameExists = this->TableExists(tableName,dbNo);
00676
00677 auto_ptr<DbiStatement> stmtDb(this->CreateStatement(dbNo) );
00678 if ( ! stmtDb.get() ) return 0;
00679
00680
00681
00682 if ( isGlobal ) {
00683 if ( ! seqnoTableNameExists ) {
00684 MAXMSG("Dbi",Msg::kError,20) << "Unable to issue global SEQNO - " << dbNo
00685 << " is not an authorising DB" << endl;
00686 return 0;
00687 }
00688 }
00689 else {
00690 if ( ! seqnoTableNameExists ) {
00691 sql.Clear();
00692 sql << "CREATE TABLE " << seqnoTableName
00693 << "(TABLENAME CHAR(64) NOT NULL PRIMARY KEY,\n"
00694 << " LASTUSEDSEQNO INT )";
00695
00696 MSG("Dbi",Msg::kSynopsis) << "Database: " << dbNo
00697 << " create local SEQNO table query: " << sql.c_str() << endl;
00698 stmtDb->ExecuteUpdate(sql.c_str());
00699 if ( stmtDb->PrintExceptions() ) return 0;
00700 sql.Clear();
00701 sql << "INSERT INTO " << seqnoTableName << " VALUES ('*',0)";
00702 MSG("Dbi",Msg::kSynopsis) << "Database: " << dbNo
00703 << " prime local SEQNO table query: " << sql.c_str() << endl;
00704 stmtDb->ExecuteUpdate(sql.c_str());
00705 if ( stmtDb->PrintExceptions() ) return 0;
00706 }
00707 }
00708
00709
00710
00711
00712 string dataTable;
00713
00714
00715 if ( ! this->IsTemporaryTable(tableName,dbNo)
00716 && tableNameExists ) dataTable = tableName;
00717 Lock lock(this->CreateStatement(dbNo),seqnoTableName,dataTable);
00718 if ( ! lock.IsLocked() ) {
00719 MAXMSG("Dbi",Msg::kError,20) << "Unable to lock " << seqnoTableName << endl;
00720 return 0;
00721 }
00722
00723
00724
00725 sql.Clear();
00726 sql << "select * from " << seqnoTableName << " where TABLENAME = '*' or TABLENAME = '";
00727 sql << tableName + "' order by TABLENAME";
00728 MSG("Dbi",Msg::kSynopsis) << seqnoTableName << " query: " << sql.c_str() << endl;
00729 TSQLStatement* stmt = stmtDb->ExecuteQuery(sql.c_str());
00730 stmtDb->PrintExceptions(Msg::kDebug);
00731 Int_t seqNoDefault = 0;
00732 if ( stmt && stmt->NextResultRow() ) {
00733 seqNoDefault = stmt->GetInt(1);
00734 }
00735 else {
00736 MAXMSG("Dbi",Msg::kError,20) << "Unable to find default SeqNo"
00737 << " due to above error" << endl;
00738 delete stmt;
00739 stmt = 0;
00740 return 0;
00741 }
00742 Int_t seqNoTable = seqNoDefault;
00743 if ( stmt->NextResultRow() ) {
00744 seqNoTable = stmt->GetInt(1);
00745 }
00746 delete stmt;
00747 stmt = 0;
00748 MSG("Dbi",Msg::kSynopsis) << " query returned last used seqno: " << seqNoTable << endl;
00749
00750
00751
00752
00753
00754 static std::string checkedCombinations;
00755 ostringstream combination;
00756 combination << ":" << tableName << isGlobal << dbNo << ":";
00757 bool notChecked = checkedCombinations.find(combination.str()) == std::string::npos;
00758 if ( notChecked ) checkedCombinations += combination.str();
00759 if ( tableNameExists && notChecked ) {
00760 Int_t seqNoMin = seqNoDefault;
00761 Int_t seqNoMax = seqNoDefault + Dbi::kMAXLOCALSEQNO;
00762 sql.Clear();
00763 sql << "select max(SEQNO) from " << tableName << "VLD"
00764 << " where SEQNO between " << seqNoMin << " and " << seqNoMax;
00765 MSG("Dbi",Msg::kSynopsis) << "Database: " << dbNo
00766 << " max SEQNO query: " << sql.c_str() << endl;
00767 stmt = stmtDb->ExecuteQuery(sql.c_str());
00768 if ( stmtDb->PrintExceptions() ) return 0;
00769 Int_t minValue = 0;
00770
00771 if ( stmt && stmt->NextResultRow() && ! stmt->IsNull(0) ) {
00772 minValue = stmt->GetInt(0);
00773 if ( minValue <= 0 ) minValue = 0;
00774 }
00775 delete stmt;
00776 stmt = 0;
00777
00778 if ( minValue > seqNoTable ) {
00779 MAXMSG("Dbi",Msg::kError,20)
00780 << "Database: " << dbNo << " "
00781 << seqnoTableName << " has last used SEQNO of "
00782 << seqNoTable << " for table " << tableName
00783 << ",\n but the highest SEQNO in the band " << seqNoMin << " to " << seqNoMax
00784 << " is " << minValue << " for that table\n "
00785 << seqnoTableName << " is out of date! It will be updated for " << tableName << endl;
00786 seqNoTable = minValue;
00787 }
00788 }
00789
00790
00791
00792 sql.Clear();
00793 sql << "delete from " << seqnoTableName << " where TABLENAME='";
00794 sql << tableName + "'";
00795 MSG("Dbi",Msg::kSynopsis) << "SEQNO entry removal: " << sql.c_str() << endl;
00796 stmtDb->ExecuteUpdate(sql.c_str());
00797 if ( stmtDb->PrintExceptions() ) return 0;
00798
00799 seqNoTable++;
00800
00801 sql.Clear();
00802 sql << "insert into " << seqnoTableName << " values('";
00803 sql << tableName + "'," << seqNoTable << ")";
00804 MSG("Dbi",Msg::kSynopsis) << "SEQNO entry add: " << sql.c_str() << endl;
00805 stmtDb->ExecuteUpdate(sql.c_str());
00806 if ( stmtDb->PrintExceptions() ) return 0;
00807
00808 return seqNoTable;
00809
00810 }
00811
00812
00813 void DbiCascader::SetPermanent(UInt_t dbNo,
00814 Bool_t permanent ) {
00815
00816
00817
00818
00819 if ( dbNo < fConnections.size() ) fConnections[dbNo]->SetPermanent(permanent);
00820
00821 }