TDbiCascader.cxx

Go to the documentation of this file.
00001 
00002 //////////////////////////////////////////////////////////////////////////
00003 ////////////////////////////     ROOT API     ////////////////////////////
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 //   Definition of static data members
00029 //   *********************************
00030 
00031 
00032 //    Definition of all member functions (static or otherwise)
00033 //    *******************************************************
00034 //
00035 //    -  ordered: ctors, dtor, operators then in alphabetical order.
00036 
00037 //.....................................................................
00038 ///\verbatim
00039 ///
00040 ///  Purpose:  Default constructor
00041 ///
00042 ///  Arguments: beQuiet - If true, fail quietly. If not be more verbose.
00043 ///
00044 ///  Return:    n/a
00045 ///
00046 ///  Throws:  ENoEnvironment(); if there is no environment defined
00047 ///               throw EBadDatabase(); if any other error occured
00048 ///  Contact:   N. West
00049 ///
00050 ///  Specification:-
00051 ///  =============
00052 ///
00053 ///  o  Create Cascader.
00054 ///
00055 ///
00056 ///  Program Notes:-
00057 ///  =============
00058 ///
00059 ///  Current cascader configuration comes from 3 environmental
00060 ///  variables:-
00061 ///
00062 ///    SK_TSQL_URL             a semi-colon separated list of URLs
00063 ///    SK_TSQL_USER            user name (one or a semi-colon separated list)
00064 ///    SK_TSQL_PSWD            password (one or a semi-colon separated list)
00065 ///
00066 ///    or the _UPDATE alternatives e.g. SK_TSQL_UPDATE_USER
00067 ///
00068 /// The _UPDATE versions take priority.
00069 ///\endverbatim
00070 TDbiCascader::TDbiCascader(bool beQuiet):
00071 fGlobalSeqNoDbNo(-1)
00072 {
00073 
00074 
00075   SK_DBI_Trace( "Creating TDbiCascader" << "  ");
00076 
00077 // Extract args from  SK_TSQL environmental variables
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        If beQuiet is true be mute about why we are failing, leave it to the caller to deal with.
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     // Handle empty password designated as '\0' (an empty null terminated character string)
00121     if ( pswd == "\\0" ) pswd = "";
00122 
00123     std::cout << "Make connection " << url << " " << user << " " << pswd << std::endl;
00124     TDbiConnection* con;
00125     // If we are testing the cascade for validity, just try connecting once, otherwise use defaults.
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 //  Attempt to locate first GlobalSeqNo/GLOBALSEQNO table.
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 //  Check for presence of a DBI_STATE_FLAG table
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 //  Abort, if there have been any failures.
00166   if ( fail ) {
00167     throw EBadDatabase();
00168   }
00169 
00170 }
00171 
00172 //.....................................................................
00173 
00174 TDbiCascader::~TDbiCascader() {
00175 //
00176 //
00177 //  Purpose: Destructor
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 ///\verbatim
00189 ///
00190 ///  Purpose:  Output TDbiCascader status to message stream.
00191 ///
00192 ///  Arguments:
00193 ///    os           in    ostream to output on
00194 ///    cascader     in    Cascader to be output
00195 ///
00196 ///  Return:        ostream
00197 ///
00198 ///  Contact:   N. West
00199 ///
00200 ///  Specification:-
00201 ///  =============
00202 ///
00203 ///  o Output TDbiCascader status to ostream.
00204 ///
00205 ///  Program Notes:-
00206 ///  =============
00207 ///
00208 ///  None.
00209 ///\endverbatim
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       //   test.NOOP();
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 ///\verbatim
00244 ///
00245 ///  Purpose:  Allocate a unique (either locally or globally) SEQNO.
00246 ///
00247 ///  Arguments:
00248 ///   tableName       in    The table for which the SEQNO is required.
00249 ///   requireGlobal   in    The type of SEQNO required:-
00250 ///                           > 0  Must be global
00251 ///                           = 0  Must be global if supplied dbNo is authorising
00252 ///                                and table isn't temporary otherwise local
00253 ///                           < 0  Must be local
00254 ///   dbNo            in     The entry in the cascade for which the SEQNO is required
00255 ///
00256 ///  Return:    The allocated SEQNO or 0 if failure.
00257 ///
00258 ///  Contact:   N. West
00259 ///
00260 ///  Program Notes:-
00261 ///  =============
00262 ///
00263 ///  Requests for SEQNOs take account of any pre-existing entries; local entries
00264 ///  should only be used for development and this allows for the LOCALSEQNO table
00265 ///  and the local data to be wiped at different times without causing conflicts.
00266 ///  Global entries should not be a problem as the GLOBALSEQNO table isn't wiped
00267 ///  but it provides protection in case the table is damaged (which has happened!).
00268 ///\endverbatim
00269 Int_t TDbiCascader::AllocateSeqNo(const string& tableName,
00270                                  Int_t requireGlobal, /* =0 */
00271                                  Int_t dbNo  /* = 0 */) const {
00272 
00273 
00274 
00275   bool isTemporary = IsTemporaryTable(tableName,dbNo);
00276 
00277   //  Deal with global requests.
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   // Deal with local requests
00293 
00294   return this->ReserveNextSeqNo(tableName,false,dbNo);
00295 
00296 }
00297 
00298 //.....................................................................
00299 ///\verbatim
00300 ///
00301 ///  Purpose:  Return a Statement to caller.
00302 ///
00303 ///  Arguments:
00304 ///    dbNo     in    Database no. in cascade for which statement
00305 ///                   is required.
00306 ///
00307 ///  Return:    Created statement (or 0 if creation failed).
00308 ///             User must delete.
00309 ///  Program Notes:-
00310 ///  =============
00311 ///
00312 ///  As the caller is responsible for destroying the statement after use
00313 ///  consider:-
00314 ///
00315 ///  #include <memory>
00316 ///  using std::auto_ptr;
00317 ///
00318 ///  ...
00319 ///
00320 ///  auto_ptr<TDbiStatement> stmt(cascader.CreateStatement(dbNo));
00321 ///\endverbatim
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 ///\verbatim
00334 ///
00335 ///  Purpose:  Creat temporary table with associated validity table
00336 ///
00337 ///  Arguments:
00338 ///    tableNameMc  in    Table name
00339 ///    tableDescr   in    Table description as parenthesised comma
00340 ///                       separated list e.g.:-
00341 ///                       "(MyInt int, MyFloat float, MyString text)"
00342 ///
00343 ///  Return:    The database cascade number on which the table was
00344 ///             created or = -1 if unable to create.
00345 ///
00346 ///  Contact:   N. West
00347 ///
00348 ///  Specification:-
00349 ///  =============
00350 ///
00351 ///  o Loop over all databases in cascade, starting at the highest
00352 ///    priority i.e. entry 0 and find the first that will accept create
00353 ///    temporary table requests.  Return -1 if none will.
00354 ///
00355 ///  o Make connection permanent so that temporary data won't be lost.
00356 ///
00357 ///  o Generate and submit requests to create temporary table with
00358 ///    associated validity table and return the cascade number of
00359 ///    the database that has accepted the tables, recording table
00360 ///    name in fTemporaryTables.
00361 ///
00362 ///\endverbatim
00363 Int_t TDbiCascader::CreateTemporaryTable(const string& tableNameMc,
00364                                        const string& tableDescr) {
00365 
00366 
00367 //  Check that input args look plausible.
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 // Find a DB that will accept the command.
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 // Make connection permanent if not already.
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 // Create SQL to create auxillary validity table and write to same Db.
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 ///  Purpose:  Return a connection to caller (TDbiCascader retains ownership)
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 ///  Purpose:  Return a connection to caller (TDbiCascader retains ownership)
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 ///  Purpose:  Return Database Name for cascade entry number.
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 ///  Purpose:  Return number of first DB in cascade with name dbName.
00474 ///
00475 ///  Return:   Database number corresponding to dbName or -1 if none.
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 ///  Purpose:  Return DB connection status as a string.
00492 ///
00493 ///  Arguments:
00494 ///    dbNo         in    Database number (0..GetNumDb()-1)
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 ///  Purpose:  Return cascade number of first database that holds table
00511 ///            or -1 if none.
00512 Int_t TDbiCascader::GetTableDbNo(const string& tableName,
00513                                 Int_t selectDbNo /* -1 */) const {
00514 
00515 
00516 // If selectDbNo >= 0 only look in this entry in the cascade.
00517 
00518 // If table name has any lower case letters then fail.
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 // Loop over cascade looking for table.
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 ///\verbatim
00536 ///
00537 ///  Purpose: Hold temporary connections open
00538 ///
00539 ///  Specification:-
00540 ///  =============
00541 ///
00542 ///  Hold all connections open by telling them that they have a
00543 ///  connected statement.
00544 ///
00545 ///  Program Notes:-
00546 ///  =============
00547 ///
00548 ///  See TDbiConnectionMaintainer for use.
00549 ///\endverbatim
00550 void TDbiCascader::HoldConnections() {
00551 
00552 
00553   for (UInt_t dbNo = 0; dbNo < fConnections.size(); ++dbNo )
00554     fConnections[dbNo]->ConnectStatement();
00555 }
00556 
00557 //.....................................................................
00558 ///  Purpose:  Return kTRUE if tableName is temporary in cascade member dbNo
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 // Private Locker object
00574 
00575 //.....................................................................
00576 ///\verbatim
00577 ///
00578 ///  Purpose:  Ctor: Create a lock on a table accessed via connection.
00579 ///            (will be released by dtor)
00580 ///  Arguments:
00581 ///          stmtDB    in  TDbiStatement (given to Lock).
00582 ///\endverbatim
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 ///  Purpose:  Dtor: Clear lock
00601 TDbiCascader::Lock::~Lock() {
00602 //
00603 //
00604 
00605 
00606   this->SetLock(kFALSE);
00607   delete fStmt;
00608   fStmt = 0;
00609 
00610 }
00611 
00612 //.....................................................................
00613 ///\verbatim
00614 ///
00615 ///  Purpose:  Set or clear lock.
00616 ///
00617 ///  Arguments:
00618 ///    setting      in    Required setting of lock. [default kTRUE]
00619 ///
00620 ///  Program Notes:-
00621 ///  =============
00622 ///
00623 ///  No-op if locked otherwise use the LOCK TABLES command.
00624 ///\endverbatim
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 ///\verbatim
00650 ///
00651 ///  Purpose: Release temporary connections held open by HoldConnections.
00652 ///
00653 ///
00654 ///  Specification:-
00655 ///  =============
00656 ///
00657 ///  Undo HoldConnections() by telling all connections that they no longer
00658 /// have a connected statement.
00659 ///
00660 ///  Program Notes:-
00661 ///  =============
00662 ///
00663 ///  See TDbiConnectionMaintainer for use.
00664 ///\endverbatim
00665 void TDbiCascader::ReleaseConnections() {
00666 
00667 
00668   for (UInt_t dbNo = 0; dbNo < fConnections.size(); ++dbNo )
00669     fConnections[dbNo]->DisConnectStatement();
00670 }
00671 //.....................................................................
00672 ///\verbatim
00673 ///
00674 ///  Purpose:  Reserve the next higher available unique (either locally or globally) SEQNO.
00675 ///            in the appropriate SEQNO table.
00676 ///
00677 ///  Arguments:
00678 ///   tableName       in    The table for which the SEQNO is required.
00679 ///   isGlobal        in    = true - reserve in GLOBALSEQNO table(dbNo must be authorizing)
00680 ///                         = false - reserve in LOCALSEQNO table (creating if required)
00681 ///   dbNo            in    The entry in the cascade holding the SEQNO table.
00682 ///
00683 ///  Return:    The allocated SEQNO or 0 if failure.
00684 ///
00685 ///  Contact:   N. West
00686 ///
00687 ///  Program Notes:-
00688 ///  =============
00689 ///
00690 ///  Requests for local SEQNOs may result in the creation of a LOCALSEQNO table.
00691 ///\endverbatim
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   // Check that required SEQNO table exists.
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 // Lock seqno table by creating a lock object on the stack.
00736 // Table will be unlocked when lock object destroyed.
00737 
00738   string dataTable;
00739 // Only pass in table name if it's not temporary and exists in
00740 // the selected DB otherwise Lock will try to lock a non-existent table.
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 // Find row containing last used SeqNo for this table.
00750 // Not that comparison is case insensitive.
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 //  If the table exists, make sure that the seqNo hasn't already been used.
00777 //  This is paranoia code and expensive, so only do the check once for
00778 //  each tableName/isGlobal/dbNo combination.
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     // Queries returning group function results can be null.
00797     if (  stmt && stmt->NextResultRow() && ! stmt->IsNull(0) ) {
00798       minValue = stmt->GetInt(0);
00799      if ( minValue <= 0 ) minValue = 0;  // Should never happen.
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 //  Update last used SeqNo and record in table.
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 ///  Purpose: Set connection permanent.
00838 void TDbiCascader::SetPermanent(UInt_t dbNo,
00839                                Bool_t permanent /* = true */ ) {
00840 //
00841 //
00842 
00843 
00844   if ( dbNo < fConnections.size() ) fConnections[dbNo]->SetPermanent(permanent);
00845 
00846 }
00847 

Generated on 11 Aug 2013 for SKDatabase by  doxygen 1.6.1