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