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

DbiSqlValPacket.cxx

Go to the documentation of this file.
00001 // $Id: DbiSqlValPacket.cxx,v 1.43 2007/04/26 14:19:57 west Exp $
00002 
00003 #include <algorithm>
00004 #include <memory>
00005 using std::auto_ptr;
00006 #include <sstream>
00007 #include <vector>
00008 
00009 #include "DatabaseInterface/Dbi.h"
00010 #include "DatabaseInterface/DbiCascader.h"
00011 #include "DatabaseInterface/DbiConfigSet.h"
00012 #include "DatabaseInterface/DbiOutRowStream.h"
00013 #include "DatabaseInterface/DbiResultPtr.h"
00014 #include "DatabaseInterface/DbiResultSet.h"
00015 #include "DatabaseInterface/DbiSqlValPacket.h"
00016 #include "DatabaseInterface/DbiStatement.h"
00017 #include "DatabaseInterface/DbiTableProxy.h"
00018 #include "DatabaseInterface/DbiTableRow.h"
00019 #include "DatabaseInterface/DbiTableProxyRegistry.h"
00020 #include "DatabaseInterface/DbiValidityRec.h"
00021 #include "DatabaseInterface/DbiValRecSet.h"
00022 
00023 #include "LeakChecker/Lea.h"
00024 #include "MessageService/MsgService.h"
00025 #include "Util/UtilString.h"
00026 #include "Validity/VldRange.h"
00027 
00028 ClassImp(DbiSqlValPacket)
00029 
00030 #ifdef IRIX6
00031 // SGI barfs at generating an operator != for this enum if it
00032 // is anonymous and in the Fill() method.
00033 enum EFillState { kLOOKING_FOR_HEADER,
00034                   kLOOKING_FOR_TRAILER };
00035 #endif
00036 
00037 //   Definition of static data members
00038 //   *********************************
00039 
00040 Bool_t DbiSqlValPacket::fgLastMetaHadRowCounter = false;
00041 
00042 CVSID("$Id: DbiSqlValPacket.cxx,v 1.43 2007/04/26 14:19:57 west Exp $");
00043 
00044 //   Definition of file static members functions
00045 //   *******************************************
00046 
00047 static bool compStringPtrs(const string* str1, const string* str2 ) {
00048   return *str1 < *str2; }
00049 
00050 //    Definition of all member functions (static or otherwise)
00051 //    *******************************************************
00052 //
00053 //    -  ordered: ctors, dtor, operators then in alphabetical order.
00054 
00055 //.....................................................................
00056 
00057 DbiSqlValPacket::DbiSqlValPacket() :
00058 fNumErrors(0),
00059 fSeqNo(0),
00060 fNumStmts(0)
00061 {
00062 //
00063 //
00064 //  Purpose:  Default ctor.
00065 
00066   LEA_CTOR    //Leak Checker
00067 
00068   MSG("Dbi", Msg::kVerbose) << "Creating DbiSqlValPacket" << endl;
00069 }
00070 
00071 //.....................................................................
00072 
00073 DbiSqlValPacket::DbiSqlValPacket(std::ifstream& is) :
00074 fNumErrors(0),
00075 fSeqNo(0),
00076 fNumStmts(0)
00077 {
00078 //
00079 //
00080 //  Purpose:  Constructor reading from input stream.
00081 //
00082 //  Arguments:
00083 //      is    in/out    Input stream.
00084 //
00085 //  Return:   n/a. 
00086 //
00087 //  Contact:   N. West
00088 //
00089 //  Specification:-
00090 //  =============
00091 //
00092 //  o  Create DbiSqlValPacket and fill from input stream.
00093 
00094 
00095 //  Program Notes:-
00096 //  =============
00097 
00098 //  None.
00099 
00100   LEA_CTOR    //Leak Checker
00101 
00102   MSG("Dbi", Msg::kVerbose) << "Creating DbiSqlValPacket" << endl;
00103 
00104   Fill(is);
00105 
00106 }
00107 //.....................................................................
00108 
00109 DbiSqlValPacket::DbiSqlValPacket(const DbiValidityRec& vrec) :
00110 fNumErrors(0),
00111 fSeqNo(vrec.GetSeqNo()),
00112 fNumStmts(0),
00113 fTableName(vrec.GetTableProxy()->GetTableName()),
00114 fCreationDate(vrec.GetCreationDate())
00115 {
00116 //
00117 //
00118 //  Purpose:  Constructor from a DbiValidityRec.
00119 //
00120 //  Arguments:
00121 //      vrec  in   DbiValidityRec from which to create packet.     .
00122 //
00123 //  Return:   n/a. 
00124 //
00125 //  Contact:   N. West
00126 //
00127 //  Specification:-
00128 //  =============
00129 //
00130 //  o  Create DbiSqlValPacket from DbiValidityRec and associated data.
00131 
00132 
00133 //  Program Notes:-
00134 //  =============
00135 
00136 //  This member function uses DbiConfigSet as a generic concrete
00137 //  DbiTableRow that can be used to create SQL for any type of table.
00138 
00139   LEA_CTOR    //Leak Checker
00140 
00141   MSG("Dbi", Msg::kVerbose) << "Creating DbiSqlValPacket" << endl;
00142 
00143   const DbiTableProxy& tableProxy = *vrec.GetTableProxy();
00144   Int_t seqNo  = vrec.GetSeqNo();
00145   UInt_t dbNo  = vrec.GetDbNo();
00146 
00147   // Create the SQL for the DbiValidityRec itself.
00148   this->AddRow(tableProxy,0,vrec);
00149 
00150   // Create the SQL for the rows.
00151 
00152   const DbiDBProxy& dbProxy = tableProxy.GetDBProxy();
00153   DbiResultSet* rset = dbProxy.QuerySeqNo(seqNo,dbNo);
00154 
00155   
00156   for(; ! rset->IsExhausted(); rset->FetchRow()) {
00157     string str;
00158     rset->RowAsCsv(str);
00159     this->AddRow(str);
00160   }
00161   delete rset;
00162   rset = 0;
00163 }
00164 
00165 //.....................................................................
00166 
00167 DbiSqlValPacket::~DbiSqlValPacket() {
00168 //
00169 //
00170 //  Purpose: Destructor
00171 //
00172 //  Arguments: 
00173 //    None.
00174 //
00175 //  Return:    n/a
00176 //
00177 //  Contact:   N. West
00178 //
00179 //  Specification:-
00180 //  =============
00181 //
00182 //  o  Destroy DbiSqlValPacket.
00183 
00184 
00185 //  Program Notes:-
00186 //  =============
00187 
00188 //  None.
00189 
00190   LEA_DTOR    //Leak Checker
00191 
00192   MSG("Dbi", Msg::kVerbose) << "Destroying DbiSqlValPacket" << endl;
00193 
00194   Clear();
00195 
00196 }
00197 
00198 //.....................................................................
00199 
00200 Bool_t DbiSqlValPacket::AddDataRow(const DbiTableProxy& tblProxy,
00201                                    const DbiValidityRec* vrec,
00202                                    const DbiTableRow& row){
00203 //
00204 //
00205 //  Purpose: Add data row. 
00206 //
00207 
00208   if ( this->GetNumSqlStmts() == 0 ) {
00209     MAXMSG("Dbi",Msg::kError,20) 
00210       << "Cannot add data row - packet does not have a VLD row"  << endl;
00211     ++fNumErrors;
00212     return kFALSE;
00213   }
00214 
00215   return this->AddRow(tblProxy,vrec,row);
00216 
00217 }
00218 
00219 //.....................................................................
00220 
00221 void DbiSqlValPacket::AddRow(const string & row){
00222 //
00223 //
00224 //  Purpose: Add row. 
00225 //
00226 
00227   string sql("INSERT INTO ");
00228   sql += this->GetTableName();
00229   if ( this->GetNumSqlStmts() == 0 ) sql += "VLD";
00230   sql += " VALUES (" + row + ");";
00231   ostringstream seqno;
00232   seqno << this->GetSeqNo();
00233   this->SetSeqNoOnRow(sql,seqno.str());
00234   fSqlStmts.push_back(sql);
00235   ++fNumStmts;
00236 
00237 }
00238 
00239 //.....................................................................
00240 
00241 Bool_t DbiSqlValPacket::AddRow(const DbiTableProxy& tblProxy,
00242                                const DbiValidityRec* vrec,
00243                                const DbiTableRow& row){
00244 //
00245 //
00246 //  Purpose: Add row. 
00247 //
00248 
00249 
00250   bool isVld = this->GetNumSqlStmts() == 0;
00251   const DbiTableMetaData& meta = isVld ? tblProxy.GetMetaValid() : tblProxy.GetMetaData(); 
00252   DbiOutRowStream outRow(&meta);
00253 
00254 // Store dummy SEQNO, and, if necessary ROW_COUNTER, for data rows. 
00255   if ( ! isVld ) {
00256     outRow << 0;  // dummy SEQNO
00257     if ( outRow.HasRowCounter() )  outRow <<  fNumStmts; // ROW_COUNTER
00258   }
00259   row.Store(outRow,vrec);
00260   if ( ! outRow.HasGoodData() ) {
00261     if ( ! outRow.IsComplete() ) {
00262       MAXMSG("Dbi",Msg::kError,20)
00263         << "Incomplete data supplied for row " << this->GetNumSqlStmts()-1
00264         << " of table " 
00265         << tblProxy.GetTableName() << endl;
00266     }
00267     ++fNumErrors;
00268     return kFALSE;
00269   }
00270   this->AddRow(outRow.GetCSV());
00271   return kTRUE;
00272 }
00273 //.....................................................................
00274 
00275 DbiSqlValPacket::CompResult_t DbiSqlValPacket::Compare(
00276                                 const DbiSqlValPacket& that,
00277                                 Bool_t log,
00278                                 const Char_t* thisName,
00279                                 const Char_t* thatName ) const {
00280 //
00281 //
00282 //  Purpose:  Compare to another DbiSqlValPacket
00283 //
00284 //  Arguments: 
00285 //    that         in    The other DbiSqlValPacket to be compared.
00286 //    log          in    If true list differences to MSG("Dbi",kInfo)
00287 //    thisName     in    Optional name for this packet (default: this)
00288 //    thatName     in    Optional name for that packet (default: that)
00289 //
00290 //  Return:    kIdentical   if identical (apart from InsertDate)
00291 //             kUpdate      "that" is more up to date
00292 //             kOutOfDate   "that" is out of date
00293 //             kConflict    records are incompatible
00294 //
00295 //  Contact:   N. West
00296 //
00297 
00298   if ( this->IsEqual(that,log,thisName,thatName ) ) return kIdentical;
00299 
00300   if (    fSeqNo           != that.fSeqNo
00301        || fTableName       != that.fTableName     ) return kConflict;
00302 
00303   std::vector<std::string> valuesThis = this->GetStmtValues(0);
00304   std::vector<std::string> valuesThat = that.GetStmtValues(0);
00305 
00306   // Assume CreationDate is the 7th element (counting from 0).
00307   int comp = valuesThis[7].compare(valuesThat[7]);
00308 
00309   if ( comp < 0 ) {
00310     if ( log ) MSG("Dbi",Msg::kInfo) 
00311       << "  Difference classified as Update" << endl;;
00312     return kUpdate;
00313   }
00314   else if ( comp > 0 ) {
00315     if ( log ) MSG("Dbi",Msg::kInfo) 
00316       << "  Difference classified as OutOfDate" << endl;
00317     return kOutOfDate;
00318   }
00319   return kConflict;
00320 
00321 }
00322 
00323 
00324 //.....................................................................
00325 
00326 Bool_t DbiSqlValPacket::CreateTable(UInt_t dbNo) const {
00327 //
00328 //
00329 //  Purpose:  Create table in specified database.
00330 //
00331 //  Arguments: 
00332 //    dbNo         in    Number of database in the cascade.
00333 //
00334 //  Return:    kTRUE if successfully created.
00335 //
00336 //  Contact:   N. West
00337 //
00338 //  Specification:-
00339 //  =============
00340 //
00341 //  o If SQL to create tables is available, use it to create 
00342 //    the main and auxiliary tables and refresh the corresponding 
00343 //    DbiTableProxy.
00344 
00345 //  Program Notes:-
00346 //  =============
00347 
00348 //  None.
00349 
00350   if ( ! CanBeStored() ) return kFALSE; 
00351 
00352   // Locate required DbiStatement.
00353   auto_ptr<DbiStatement> stmtDb(DbiTableProxyRegistry::Instance()
00354                                .GetCascader()
00355                                .CreateStatement(dbNo));
00356   if ( ! stmtDb.get() ) {
00357     MSG("Dbi",Msg::kWarning) 
00358       << "Attempting to write to non-existant cascade entry " << dbNo
00359       << endl;
00360     return kFALSE;
00361   }
00362   if ( fSqlMySqlMetaMain == "" || fSqlMySqlMetaVld  == "" ) {
00363     MSG("Dbi",Msg::kWarning) 
00364       << "No SQL available to create table " << fTableName
00365       << " in cascade entry: " << dbNo << endl;
00366     return kFALSE;
00367   }
00368 
00369   stmtDb->ExecuteUpdate(fSqlMySqlMetaVld.c_str());
00370   if ( stmtDb->PrintExceptions() ) return kFALSE;
00371   stmtDb->ExecuteUpdate(fSqlMySqlMetaMain.c_str());
00372   if ( stmtDb->PrintExceptions() ) return kFALSE;
00373 
00374   DbiTableProxyRegistry::Instance().RefreshMetaData(this->GetTableName());
00375 
00376   return kTRUE;
00377 
00378 }
00379 
00380 //.....................................................................
00381 
00382 Bool_t DbiSqlValPacket::Fill(std::ifstream& is) {
00383 //
00384 //
00385 //  Purpose:  Refill object from input string.
00386 //
00387 //  Arguments: 
00388 //      is    in/out    Input stream.
00389 //
00390 //  Return:   kTRUE is object has been refilled. 
00391 //
00392 //  Contact:   N. West
00393 //
00394 //  Specification:-
00395 //  =============
00396 //
00397 //  o Collect SQL statements, and possibly metadata (as SQL to
00398 //    create table) bracketed by >>>> ... <<<<< checking that
00399 //    the table name and SeqNos match.  Count and discard any data that
00400 //    looks bad. 
00401 
00402 //  Program Notes:-
00403 //  =============
00404 
00405 //  None.
00406 
00407 #ifdef IRIX6
00408   EFillState state = kLOOKING_FOR_HEADER;
00409 #else
00410   enum { kLOOKING_FOR_HEADER,
00411          kLOOKING_FOR_TRAILER } state = kLOOKING_FOR_HEADER;
00412 #endif
00413 
00414   enum { kMAXTABLENAMELEN        = Dbi::kMAXTABLENAMELEN,
00415          kHEADER_TRAILER_MAX_LEN = kMAXTABLENAMELEN + 20 };
00416 
00417   string nameHead;
00418   string nameTrail;
00419   UInt_t seqNoHead  = 0;
00420   UInt_t seqNoTrail = 0;
00421 
00422   string line;
00423   string msg;
00424   string sql;
00425   int lineNum = 0;
00426 
00427   this->Reset();
00428   //  Loop until EOF reading lines.
00429 
00430   while ( ! is.eof() ) {
00431     getline(is,line);
00432     ++lineNum;
00433     // Skip null lines.
00434     if (line.size() == 0 ) continue;
00435 
00436     // Look for header line
00437     if ( state == kLOOKING_FOR_HEADER ) {
00438       if ( line.substr(0,5) == ">>>>>" ) {
00439         if ( line.size() >= kHEADER_TRAILER_MAX_LEN ) {
00440           Report("Bad header",lineNum,line);
00441           continue;
00442         }
00443 
00444         // Look for optional metadata.
00445         if ( line.find("Metadata") != string::npos ) {
00446           getline(is,fSqlMySqlMetaVld);
00447           ++lineNum;
00448           getline(is,fSqlMySqlMetaMain);
00449           // Record whether or not table has a ROW_COUNTER column.
00450           fgLastMetaHadRowCounter = 
00451             fSqlMySqlMetaMain.find("ROW_COUNTER",fSqlMySqlMetaMain.find("(")) != string::npos;
00452           ++lineNum;
00453           getline(is,line);
00454           ++lineNum;
00455           if (   line.substr(0,5) != "<<<<<" 
00456               || line.find("Metadata") == string::npos ) {
00457             Report("Bad metadata",lineNum,line);
00458             continue;
00459           }
00460           getline(is,line);
00461           ++lineNum;
00462           if ( line.size() >= kHEADER_TRAILER_MAX_LEN ) {
00463             Report("Bad header",lineNum,line);
00464             continue;
00465           }
00466         }
00467           
00468         //  Collect table name and SeqNo.
00469         istringstream istr(line.substr(5));
00470         istr.width(kMAXTABLENAMELEN);
00471         istr >> nameHead >> seqNoHead;
00472         if ( ! istr.eof() ) {
00473           Report("Input error",lineNum,line);
00474           continue;
00475         }
00476 
00477         // Header looks good, start to collect SQL. 
00478         state = kLOOKING_FOR_TRAILER;
00479         sql = "";
00480       }
00481       else {
00482         Report("Not header",lineNum,line);
00483       }
00484     }
00485 
00486     //Look for trailer line.
00487 
00488     else {
00489       if ( line.substr(0,5) == "<<<<<" ) {
00490         if ( line.size() >= kHEADER_TRAILER_MAX_LEN 
00491            ) msg = "Bad trailer";
00492 
00493         else {
00494 
00495           //  Collect table name and SeqNo.
00496           istringstream istr(line.substr(5));
00497           istr.width(kMAXTABLENAMELEN);
00498           istr >> nameTrail >> seqNoTrail;
00499           if ( ! istr.eof() ) msg = "Input error";
00500 
00501           else if (    nameTrail != nameHead
00502                     || seqNoHead != seqNoTrail ) {
00503             msg = "Header/Trailer mismatch: Header: ";
00504             msg += istr.str();
00505           }
00506           else if ( GetNumSqlStmts() == 0 
00507                   ) msg = "No SQL statements between Header/Trailer";
00508           else {
00509 
00510             // Trailer looks good return with object filled. 
00511             fSeqNo     = seqNoHead;
00512             fTableName = nameHead;
00513 
00514             //Dig out the creation date from the first record.
00515             string date = this->GetStmtValues(0)[7];
00516             //Remove the quotes.
00517             date.erase(0,1);
00518             date.erase(date.size()-1,1);
00519             fCreationDate = Dbi::MakeTimeStamp(date);
00520             // Temporary hack: set for Agg No = -1 for DBUSUBRUNSUMMARY
00521             if ( fTableName  == "DBUSUBRUNSUMMARY" ) {
00522               std::vector<std::string> ls;
00523               UtilString::StringTok(ls,*fSqlStmts.begin(),",");
00524               // Agg. no is element 6.
00525               if (ls.size() >= 7 && ls[6] != "-1") {
00526                 static bool warnOnce = true;
00527                 if ( warnOnce ) {
00528                   MSG("Dbi",Msg::kWarning)
00529                    << "Setting aggregate number for DBUSUBRUNSUMMARY = -1\n"
00530                    << "  Expect this message once.  DBUSUBRUNSUMMARY needs to be "
00531                    << " fixed!" << endl;
00532                    warnOnce = false;
00533                 }
00534                 ls[6] = "-1";
00535                 sql = ls[0];
00536                 for (unsigned i=1; i<ls.size(); ++i ) sql += "," + ls[i];
00537                 fSqlStmts.pop_front();
00538                 fSqlStmts.push_front(sql);  
00539               }
00540             }
00541             return kTRUE;
00542 
00543           }
00544         }
00545 
00546         // Trailer bad, start again!
00547         Report(msg.c_str(),lineNum,line);
00548         state = kLOOKING_FOR_HEADER;
00549       }
00550 
00551       // Not a trailer line, must be SQL, collect and append lines
00552       // until a trailing ; found.
00553       else {
00554         sql += line;
00555         if ( sql[sql.size()-1] == ';') {
00556           fSqlStmts.push_back(sql);
00557           ++fNumStmts;
00558           sql = "";
00559         }
00560       } 
00561     }
00562 
00563   }
00564 
00565   if ( state != kLOOKING_FOR_HEADER 
00566      ) Report("Unexpected EOF",lineNum,"EOF");
00567   this->Reset();
00568   return kFALSE;
00569 
00570 }
00571 //.....................................................................
00572 
00573 string DbiSqlValPacket::GetStmt(UInt_t stmtNo) const {
00574 //
00575 //
00576 //  Purpose:  Return a selected statment
00577 
00578   if ( stmtNo >= this->GetNumSqlStmts() ) return "";
00579 
00580   // Locate statement
00581   std::list<std::string>::const_iterator itr = fSqlStmts.begin();
00582   while ( stmtNo ) { ++itr; --stmtNo; }
00583 
00584   return *itr;
00585 
00586 }
00587 //.....................................................................
00588 
00589 std::vector<std::string> DbiSqlValPacket::GetStmtValues(UInt_t stmtNo) const {
00590 //
00591 //
00592 //  Purpose:  Return all the values associated with a selected statment
00593 //
00594 //  Arguments: 
00595 //    stmtNo       in    The statement number (starting at 0)
00596 //
00597 //  Return:              vector of string values.  
00598 //                       Empty vector if stmt does not exits. 
00599 //
00600 //  Contact:   N. West
00601 
00602   std::vector<std::string> vec;
00603   if ( stmtNo >= this->GetNumSqlStmts() ) return vec;
00604 
00605   // Locate statement
00606   std::string str = this->GetStmt(stmtNo);
00607 
00608   // Extract ...(list-of-values)... and parse it into tokens.
00609   std::string::size_type pos = str.find('(');
00610   ++pos;
00611   std::string::size_type n = str.find(')') - pos;
00612   UtilString::StringTok(vec,str.substr(pos,n),",");
00613 
00614   return vec;
00615 
00616 }
00617 
00618 //.....................................................................
00619 
00620 Bool_t DbiSqlValPacket::IsEqual(const DbiSqlValPacket& that,
00621                                 Bool_t log,
00622                                 const Char_t* thisName,
00623                                 const Char_t* thatName ) const {
00624 //
00625 //
00626 //  Purpose:  Test for equality to another DbiSqlValPacket
00627 //
00628 //  Arguments: 
00629 //    that         in    The other DbiSqlValPacket to be compared.
00630 //    log          in    If true list differences to MSG("Dbi",kInfo)
00631 //    thisName     in    Optional name for this packet (default: this)
00632 //    thatName     in    Optional name for that packet (default: that)
00633 //
00634 //  Return:    kTRUE if identical (apart from InsertDate).
00635 //
00636 //  Contact:   N. West
00637 //
00638 //  Specification:-
00639 //  =============
00640 //
00641 //  o Compare to another DbiSqlValPacket.
00642 
00643 //  Program Notes:-
00644 //  =============
00645 
00646 //  The InsertDate reflects the date when the data was written into the
00647 // local database so has to be excluded from the comparison.
00648 
00649   if (    fSeqNo           != that.fSeqNo
00650        || fTableName       != that.fTableName
00651        || fNumStmts != that.fNumStmts ) {
00652     if ( log ) MSG("Dbi",Msg::kInfo) << "Conflict found:"
00653              << " for : " << thisName << " , " << thatName << " :-\n"
00654              << "  SeqNo " << fSeqNo << "," << that.fSeqNo 
00655              << "\n  TableName " << fTableName << "," << that.fTableName
00656              << "\n  Size " << fNumStmts << "," 
00657              << that.fNumStmts << endl;
00658     return kFALSE;
00659   }
00660 
00661   list<string>::const_iterator itrThisBegin = fSqlStmts.begin();
00662   list<string>::const_iterator itrThisEnd   = fSqlStmts.end();
00663   list<string>::const_iterator itrThatBegin = that.fSqlStmts.begin();
00664   list<string>::const_iterator itrThatEnd   = that.fSqlStmts.end();
00665 
00666   list<string>::const_iterator itrThis = itrThisBegin;
00667   list<string>::const_iterator itrThat = itrThatBegin;
00668 
00669   Bool_t isEqual = kTRUE;
00670 
00671   // Strip off InsertDate from first statement (assume its
00672   // the last parameter in list).
00673 
00674   string strThis = (*itrThis).substr(0,(*itrThis).rfind(','));
00675   string strThat = (*itrThat).substr(0,(*itrThat).rfind(','));
00676   if ( strThis != strThat ) {
00677   if ( ! log ) return kFALSE;
00678   isEqual = kFALSE;
00679   MSG("Dbi",Msg::kInfo) 
00680       << "Difference on VLD record " << ":-\n"
00681       << "  " << thisName << ": " << strThis  << endl
00682       << "  " << thatName << ": " << strThat  << endl;
00683   }
00684 
00685   // Rows can come in any order (after the first) so we have
00686   // to sort before comparing.  However, if we are lucky, they
00687   // may be in the same order or inverse order so do a quick 
00688   // test to see if there are no conflicts assuming these 
00689   // relative orderings.
00690 
00691   ++itrThis;
00692   ++itrThat;
00693   while ( itrThis != itrThisEnd && (*itrThis) == (*itrThat) ) {
00694 //     cout << "Debug: trying forward compare ..." << *itrThis 
00695 //       << "::" << *itrThat << endl;
00696     ++itrThis;
00697     ++itrThat;
00698   }
00699   if ( itrThis == itrThisEnd ) return isEqual;
00700 
00701   itrThis = itrThisBegin;
00702   itrThat = itrThatEnd;
00703   ++itrThis;
00704   --itrThat;
00705 
00706   while ( itrThis != itrThisEnd &&  (*itrThis) == (*itrThat) ) {
00707 //   cout << "Debug: trying reverse compare ..." << *itrThis 
00708 //       << "::" << *itrThat << endl;
00709     ++itrThis;
00710     --itrThat;
00711   }
00712   if ( itrThis == itrThisEnd ) return isEqual;
00713 
00714   // O.K., we are out of luck so set up pointers to both sets
00715   // and sort these.
00716 
00717   typedef vector<const string*>      shadow_list_t;
00718   typedef shadow_list_t::iterator  shadow_list_itr_t;
00719 
00720   shadow_list_t shadowThis;
00721   shadow_list_t shadowThat;
00722 
00723   itrThis = itrThisBegin;
00724   itrThat = itrThatBegin;
00725   ++itrThis;
00726   ++itrThat;
00727 
00728   while ( itrThis != itrThisEnd ) {
00729     shadowThis.push_back(&(*itrThis));
00730     shadowThat.push_back(&(*itrThat));
00731     ++itrThis;
00732     ++itrThat;
00733   }
00734 
00735   shadow_list_itr_t shadowThisItr    = shadowThis.begin();
00736   shadow_list_itr_t shadowThisItrEnd = shadowThis.end();
00737   shadow_list_itr_t shadowThatItr    = shadowThat.begin();
00738   shadow_list_itr_t shadowThatItrEnd = shadowThat.end();
00739 
00740   sort(shadowThisItr,shadowThisItrEnd,compStringPtrs);
00741   sort(shadowThatItr,shadowThatItrEnd,compStringPtrs);
00742 
00743   while ( shadowThisItr != shadowThisItrEnd ) {
00744     if ( (**shadowThisItr) != (**shadowThatItr) ){
00745       if ( ! log ) return kFALSE;
00746       isEqual = kFALSE;
00747       MSG("Dbi",Msg::kInfo) << "Difference on data record "
00748          << ":-\n"
00749          << "  " << thisName << ": " << **shadowThisItr  << endl
00750          << "  " << thatName << ": " << **shadowThatItr  << endl;
00751     }
00752    ++shadowThisItr;
00753    ++shadowThatItr;
00754   }
00755 
00756   return isEqual;
00757 
00758 }
00759 
00760 //.....................................................................
00761 
00762 void DbiSqlValPacket::Print(Option_t * /* option */) const {
00763 //
00764 //
00765 //  Purpose:  Print the current state.
00766 
00767   MSG("Dbi",Msg::kInfo) 
00768     << "DbiSQLValPacket:"
00769     << " table \"" << fTableName << "\" "
00770     << " SeqNo " << fSeqNo
00771     << " NumErrors " << fNumErrors
00772     << endl
00773     << "   CreationDate " << fCreationDate
00774     << endl;
00775 
00776   MSG("Dbi",Msg::kInfo) 
00777     << "   MySQL Main table creation: \"" << fSqlMySqlMetaMain << "\"" << endl;
00778 
00779   MSG("Dbi",Msg::kInfo) 
00780     << "   MySQL VLD table creation: \"" << fSqlMySqlMetaVld << "\"" << endl;
00781 
00782   if ( GetNumSqlStmts() > 0 ) {
00783     std::list<string>::const_iterator itr    = fSqlStmts.begin();
00784     std::list<string>::const_iterator itrEnd = fSqlStmts.end();
00785     for (; itr != itrEnd; ++itr) 
00786       MSG("Dbi",Msg::kInfo) 
00787         << "   SqlStmt \"" << *itr << "\"" << endl;
00788   }
00789   else {
00790     MSG("Dbi",Msg::kInfo) << "   No SqlStmts." << endl;
00791   }
00792 
00793 }
00794 
00795 
00796 //.....................................................................
00797 
00798 void DbiSqlValPacket::Recreate(const string& tableName,
00799                                const VldRange& vr,
00800                                Int_t aggNo,
00801                                Dbi::Task task,             /*  Default: 0 */
00802                                VldTimeStamp creationDate   /*  Default: now */ 
00803                                ) {
00804 //
00805 //
00806 //  Purpose:  Recreate and define first row (VLD - the validity record).
00807 
00808 
00809   this->Clear();
00810 
00811   DbiTableProxyRegistry& tablePR = DbiTableProxyRegistry::Instance();
00812   if ( ! tablePR.GetCascader().TableExists(tableName) ) {
00813     MAXMSG("Dbi",Msg::kError,20) << "Cannot create packet - table " << tableName
00814                            << " does not exist." << endl;
00815     fNumErrors = 1;
00816     return;
00817   }
00818 
00819   fTableName = tableName;
00820 
00821 
00822   // Create a DbiValidityRec from the supplied data.
00823   DbiValidityRec vrec(vr,task,aggNo,0);
00824 
00825   //  Create a DbiOutRowStream that can serialise this validity record
00826   DbiConfigSet dummy;     // For validity row any DbiTableRow will do.
00827   const DbiTableMetaData&  metaValid = tablePR.GetTableProxy(tableName,&dummy)
00828                                               .GetMetaValid();
00829   DbiOutRowStream buff(&metaValid);
00830 
00831   vrec.Store(buff,0);
00832   this->AddRow(buff.GetCSV());
00833   this->SetCreationDate(creationDate);
00834 
00835 }  
00836  
00837 //.....................................................................
00838 
00839 void DbiSqlValPacket::Report(const char* msg, 
00840                              UInt_t lineNum,
00841                              const string& line) {
00842 //
00843 //
00844 //  Purpose:  Report and count errors.
00845 //
00846 //  Arguments: 
00847 //    msg          in    Message to be reported.
00848 //    lineNum      in    Line number.
00849 //    line         in    Input line causing error.
00850 //
00851 //  Return:    
00852 //
00853 //  Contact:   N. West
00854 //
00855 //  Specification:-
00856 //  =============
00857 //
00858 //  o Reset object, report and count errors.
00859 
00860 //  Program Notes:-
00861 //  =============
00862 
00863 //  None.
00864 
00865   MAXMSG("Dbi",Msg::kError,20) << msg << " on line " << lineNum
00866                          <<":- \n  " << line << endl;
00867   this->Reset();
00868   ++fNumErrors;
00869 
00870 }
00871 //.....................................................................
00872 
00873 void DbiSqlValPacket::Reset() {
00874 //
00875 //
00876 //  Purpose:  Clear out back to unfilled status except for fNumErrors.
00877 
00878   fSeqNo       = 0;
00879   fSqlMySqlMetaMain = "";
00880   fSqlMySqlMetaVld  = "";
00881   fSqlStmts.clear();
00882   fNumStmts    = 0;
00883   fTableName   = "";
00884 
00885 }
00886 //.....................................................................
00887 
00888 void DbiSqlValPacket::SetCreationDate(VldTimeStamp ts) {
00889 //
00890 //
00891 //  Purpose:  Set creation date.
00892 
00893   fCreationDate = ts;
00894 
00895   //  Update the validity row assuming:  "...,'creationdate',insertdate);"
00896   if ( this->GetNumSqlStmts() == 0 ) return;
00897 
00898   string& vldRow = *fSqlStmts.begin();
00899   string::size_type locEnd = vldRow.rfind(',');
00900   if ( locEnd == string::npos ) return;
00901   locEnd -=2;
00902   string::size_type locStart = vldRow.rfind(',',locEnd);
00903   if ( locStart == string::npos ) return;
00904   locStart+=2;
00905   vldRow.replace(locStart,locEnd-locStart+1,ts.AsString("s"));
00906 
00907 }
00908 
00909 //.....................................................................
00910 
00911 void DbiSqlValPacket::SetMetaData() const {
00912 //
00913 //
00914 //  Purpose:  Set up meta-data as SQL table creation statements.
00915 
00916   DbiTableProxyRegistry& tbprxreg = DbiTableProxyRegistry::Instance();
00917 
00918   //  Locate the table in the cascade.
00919   DbiCascader& cas = tbprxreg.GetCascader();
00920   Int_t dbNo = cas.GetTableDbNo(this->GetTableName());
00921   if ( dbNo < 0 ) return;
00922 
00923   //  Any table proxy will do to get the meta-data so use the one for a 
00924   //  DbiConfigSet;
00925   DbiConfigSet dummy;
00926   const DbiTableMetaData & metaVld =  tbprxreg.GetTableProxy(this->GetTableName(),&dummy)
00927                                      .GetMetaValid();
00928   const DbiTableMetaData & metaMain = tbprxreg.GetTableProxy(this->GetTableName(),&dummy)
00929                                      .GetMetaData();
00930   fSqlMySqlMetaVld   = metaVld.Sql();
00931   fSqlMySqlMetaMain  = metaMain.Sql();
00932 
00933 }
00934 
00935 //.....................................................................
00936 
00937 void DbiSqlValPacket::SetSeqNo(UInt_t seqno) {
00938 //
00939 //
00940 //  Purpose:  Set Sequence number.
00941 
00942   fSeqNo = seqno;
00943 
00944   //  Update all rows 
00945   if ( this->GetNumSqlStmts() == 0 ) return;
00946 
00947   ostringstream tmp;
00948   tmp << seqno;
00949   const string seqnoStr = tmp.str();
00950 
00951   std::list<string>::iterator itr    = fSqlStmts.begin();
00952   std::list<string>::iterator itrEnd = fSqlStmts.end();
00953   for (; itr != itrEnd; ++itr) SetSeqNoOnRow(*itr,seqnoStr);
00954 
00955 
00956 }
00957 
00958 //.....................................................................
00959 
00960 void DbiSqlValPacket::SetSeqNoOnRow(string& row,const string& seqno) {
00961 //
00962 //
00963 //  Purpose:  Set Sequence number on supplied row
00964 
00965 //  Update row assuming:  "...(seqno, ...."
00966   
00967   string::size_type locStart = row.find('(');
00968   if ( locStart == string::npos ) return;
00969   ++locStart;
00970   string::size_type locEnd = row.find(',',locStart);
00971   if ( locEnd == string::npos ) return;
00972   row.replace(locStart,locEnd-locStart,seqno);
00973 
00974 }
00975 
00976 //.....................................................................
00977 
00978 Bool_t DbiSqlValPacket::Store(UInt_t dbNo, Bool_t replace) const {
00979 //
00980 //
00981 //  Purpose:  Output validity packet to specified database.
00982 //
00983 //  Arguments: 
00984 //    dbNo         in    Number of database in the cascade.
00985 //    replace      in    If true replace existing SeqNo (default: kFALSE).
00986 //
00987 //  Return:    kTRUE if successfully stored.
00988 //
00989 //  Contact:   N. West
00990 //
00991 //  Specification:-
00992 //  =============
00993 //
00994 //  o Output validity packet to specified database modifying
00995 //    InsertDate to be current date.
00996 
00997   if ( ! CanBeStored() ) return kFALSE; 
00998 
00999   //Just use any old table row object just to get a DbiDBProxy.
01000   DbiConfigSet pet;
01001   DbiTableProxy& tp =  DbiTableProxyRegistry::Instance()
01002                       .GetTableProxy(this->GetTableName(),&pet);
01003   if ( replace ) {
01004     const DbiDBProxy & proxy = tp.GetDBProxy();
01005     if ( ! proxy.RemoveSeqNo(this->GetSeqNo(),dbNo) ) return kFALSE;
01006   }
01007 
01008   // Locate required DbiStatement.
01009   auto_ptr<DbiStatement> stmtDb(DbiTableProxyRegistry::Instance()
01010                                .GetCascader()
01011                                .CreateStatement(dbNo));
01012   if ( ! stmtDb.get() ) {
01013     MSG("Dbi",Msg::kWarning) 
01014       << "Attempting to write to non-existant cascade entry " << dbNo
01015       << endl;
01016     return kFALSE;
01017   }
01018 
01019   // Decide if ROW_COUNTER column has to be removed i.e. Master
01020   // DB exported with ROW_COUNTER but Slave still has old table form
01021   // without ROW_COUNTER.
01022 
01023   bool removeRowCounter =    fgLastMetaHadRowCounter 
01024                           && ! tp.GetMetaData().HasRowCounter();
01025 
01026   // Loop processing all SQL statements
01027   Bool_t first = kTRUE;
01028   int combineInserts = 0;
01029   // Cannot group ORACLE inserts.
01030   int maxInserts = stmtDb->GetDBType() == Dbi::kOracle ? 1 : 20;  
01031   string sqlInserts;
01032 
01033   for (list<string>::const_iterator itr = fSqlStmts.begin();
01034        itr != fSqlStmts.end();
01035        ++itr) {
01036     if ( first ) {
01037 //    On first statement replace InsertDate by current datetime.
01038       string sql = *itr;
01039       list<string>::size_type locDate = sql.rfind(",\'");
01040       if ( locDate !=  string::npos ) {
01041         VldTimeStamp now;
01042         sql.replace(locDate+2,19,Dbi::MakeDateTimeString(now));
01043       }
01044       stmtDb->ExecuteUpdate(sql.c_str());
01045       if ( stmtDb->PrintExceptions() ) return kFALSE;
01046       first = kFALSE;
01047       continue;
01048     }
01049 
01050     string sql = *itr;
01051 
01052 //  On other statements remove the second (ROW_COUNTER) column if required.
01053     if (removeRowCounter) {
01054       list<string>::size_type locStart  = sql.find(',');
01055       ++locStart;
01056       list<string>::size_type locEnd    = sql.find(',',locStart);
01057       if ( locEnd !=  string::npos ) {
01058         list<string>::size_type numChars = locEnd - locStart + 1;
01059         sql.erase(locStart,numChars);
01060       }
01061     }
01062 
01063 //  Reduce database I/O by combining groups of insert commands.
01064 
01065     string::size_type insertIndex = sql.find("VALUES (");
01066     if ( insertIndex == string::npos) {
01067       MAXMSG("Dbi",Msg::kError,20) << "Unexpected SQL : " << sql
01068                              << "\n  should be of form INSERT INTO ... VALUES (...);" << endl;
01069       return kFALSE;
01070     }
01071     ++combineInserts;
01072     if ( combineInserts == 1 ) sqlInserts = sql;
01073     else {
01074       sqlInserts[sqlInserts.size()-1] = ',';
01075       sqlInserts += sql.substr(insertIndex+7);
01076     }
01077     if ( combineInserts >= maxInserts ) {
01078       stmtDb->ExecuteUpdate(sqlInserts.c_str());
01079       if ( stmtDb->PrintExceptions() ) return kFALSE;
01080       combineInserts = 0;
01081     }
01082   }
01083 
01084 // Deal with last group of inserts.
01085   if ( combineInserts ) {
01086     stmtDb->ExecuteUpdate(sqlInserts.c_str());
01087     combineInserts = 0;
01088     if ( stmtDb->PrintExceptions() ) return kFALSE;
01089   }
01090 
01091   return kTRUE;
01092 
01093 }
01094 //.....................................................................
01095 
01096 Bool_t DbiSqlValPacket::Write(std::ofstream& ios,
01097                               Bool_t addMetadata) const {
01098 //
01099 //
01100 //  Purpose:  Export to an iostream.
01101 //
01102 //  Arguments: 
01103 //     ios          in/out    Output stream.
01104 //     addMetadata  in        if kTRUE, output SQL metadata (in form
01105 //                            of a create table SQL)
01106 //
01107 //  Return:    kTRUE is I/O successful.
01108 //
01109 //  Contact:   N. West
01110 //
01111 //  Specification:-
01112 //  =============
01113 //
01114 //  o Export to an iostream.
01115 
01116 //  Program Notes:-
01117 //  =============
01118 
01119 //  None.
01120 
01121   if ( ! CanBeStored() ) return kFALSE;
01122   if ( addMetadata ) {
01123     if ( fSqlMySqlMetaMain.size() == 0 ) this->SetMetaData();
01124     if ( fSqlMySqlMetaMain.size() == 0 ) {    
01125       MSG("Dbi",Msg::kWarning) 
01126       << "Cannot write metadata; no associated DbiTableProxy "
01127       << endl;
01128     }
01129     else {
01130 
01131 
01132       ios << ">>>>>" << GetTableName() << " Metadata [MySQL]" << endl;
01133       ios << fSqlMySqlMetaVld   << endl;
01134       ios << fSqlMySqlMetaMain  << endl;
01135       ios << "<<<<<" << GetTableName() << " Metadata" << endl;
01136     }
01137   }
01138 
01139   ios << ">>>>>" << GetTableName() << "  " << GetSeqNo() << endl;
01140 
01141   for ( list<string>::const_iterator itr = fSqlStmts.begin();
01142         itr != fSqlStmts.end();
01143         ++itr) ios << (*itr) << endl;
01144 
01145   ios << "<<<<<" << GetTableName() << "  " << GetSeqNo() << endl;    
01146 
01147   return kTRUE;
01148 
01149 }
01150 

Generated on Mon Jun 16 14:56:56 2008 for loon by  doxygen 1.3.9.1