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

DbiStatement.cxx

Go to the documentation of this file.
00001 
00005 
00006 
00007 #include <iostream>
00008 #include <sstream>
00009 #include <string>
00010 #include <vector>
00011 
00012 #include "TString.h"
00013 
00014 #include "DatabaseInterface/DbiStatement.h"
00015 #include "DatabaseInterface/DbiTableMetaData.h"
00016 #include "LeakChecker/Lea.h"
00017 #include "MessageService/MsgService.h"
00018 #include "Util/UtilString.h"
00019 
00020 ClassImp(DbiStatement)
00021 
00022 
00023 //   Definition of static data members
00024 //   *********************************
00025 
00026 CVSID("$Id: DbiStatement.cxx,v 1.37 2007/07/05 09:26:34 west Exp $");
00027 
00028 //    Definition of all member functions (static or otherwise)
00029 //    *******************************************************
00030 //
00031 //    -  ordered: ctors, dtor, operators then in alphabetical order.
00032 
00033 //.....................................................................
00034 
00035 DbiStatement::DbiStatement(DbiConnection& conDb) :
00036 fConDb(conDb),
00037 fDbType(conDb.GetDbType())
00038 {
00039 //
00040 //
00041 //  Purpose:  Constructor
00042 //
00043 //  Arguments:  None.
00044 //
00045 //  Return:   
00046 //
00047 //  conDb    in    The connection associated with the statement.
00048 //
00049 
00050   LEA_CTOR    //Leak Checker
00051 
00052   MSG("Dbi", Msg::kVerbose) << "Creating DbiStatement" << endl;
00053   fConDb.ConnectStatement();
00054 
00055 }
00056 
00057 //.....................................................................
00058 
00059 DbiStatement::~DbiStatement() {
00060 //
00061 //
00062 //  Purpose: Destructor
00063 
00064   LEA_DTOR    //Leak Checker
00065   MSG("Dbi", Msg::kVerbose) << "Destroying DbiStatement" << endl;
00066 
00067   fConDb.DisConnectStatement();
00068 }
00069 
00070 //.....................................................................
00071 
00072 TSQLStatement* DbiStatement::CreateProcessedStatement(const TString& sql /* ="" */) {
00073 
00074 // Attempt to create a processed statement (caller must delete).  Return 0 if failure.
00075 
00076   TSQLStatement* stmt = fConDb.CreatePreparedStatement(sql.Data());
00077   if ( ! stmt ) {
00078     this->AppendExceptionLog(fConDb);
00079     return 0;
00080   }
00081   if ( stmt->Process() ) return stmt;
00082   this->AppendExceptionLog(stmt);
00083   delete stmt;
00084   stmt = 0;
00085   return 0;
00086 
00087 }
00088 
00089 
00090 //.....................................................................
00091 
00092 TSQLStatement* DbiStatement::ExecuteQuery( const TString& sql) {
00093 //
00094 //
00095 //  Purpose:  Translate SQL if required and execute. 
00096 //  Return:   TSQLStatement with Process() and StoreResult() already performed.
00097 
00098   this->ClearExceptionLog();
00099 
00100   std::list<TString> sqlList(this->TranslateSQL(sql));
00101   std::list<TString>::const_iterator itr(sqlList.begin()), itrEnd(sqlList.end());
00102 
00103   TSQLStatement* stmt = 0;
00104   while (itr != itrEnd) {
00105     const TString& sql = *itr++;
00106     MSG("Dbi",Msg::kSynopsis) << "SQL:" << fConDb.GetDbName() << ":" << sql << endl;
00107     delete stmt;
00108     stmt = this->CreateProcessedStatement(sql);
00109     if ( ! stmt ) return 0;
00110   }
00111   // Store results from last SQL command (when multiple commands are generated
00112   // the last will be the one that performs the query).
00113   if ( stmt && ! stmt->StoreResult() ) {
00114     this->AppendExceptionLog(stmt);
00115     delete stmt;
00116     stmt = 0;
00117   }
00118 
00119   // Final sanity check: If there is a statement then the exception log should still
00120   // be clear otherwise it should not be.
00121   if ( stmt ) {
00122     if ( ! fExceptionLog.IsEmpty() ) {
00123       delete stmt;
00124       stmt = 0;
00125     }
00126   }
00127   else if ( fExceptionLog.IsEmpty() ) {
00128     ostringstream oss;
00129     oss << "Unknown failure (no execption but no TSQLStatement either executing " << sql;
00130     fExceptionLog.AddEntry(oss.str().c_str());
00131   }
00132   return stmt;
00133   
00134 }
00135 
00136 //.....................................................................
00137 
00138 Bool_t DbiStatement::ExecuteUpdate( const TString& sql) {
00139 //
00140 //
00141 //  Purpose:  Translate SQL if required and Execute.
00142 //
00143 //  Return true if all updates successful.
00144 
00145 
00146   this->ClearExceptionLog();
00147 
00148   std::list<TString> sqlList(this->TranslateSQL(sql));
00149   std::list<TString>::const_iterator itr(sqlList.begin()), itrEnd(sqlList.end());
00150 
00151   while (itr != itrEnd) {
00152     const TString& sql = *itr++;
00153     MSG("Dbi",Msg::kSynopsis) << "SQL:" << fConDb.GetDbName() << ":" << sql << endl;
00154     bool ok = fConDb.GetServer()->Exec(sql.Data());
00155     if ( ! ok ) {
00156       fConDb.RecordException();
00157       this->AppendExceptionLog(fConDb);
00158       return false;
00159     }
00160   }
00161   return fExceptionLog.IsEmpty();
00162   
00163 }
00164 
00165 //.....................................................................
00166 
00167 Bool_t DbiStatement::PrintExceptions(Int_t level) const {
00168 
00169 //  Purpose:  Print accumulated exceptions at supplied Msg level,
00170 //            add them to the Global Exception Log if level >= kWarning
00171 //            and return true if there are any.
00172 
00173   const DbiExceptionLog& el(this->GetExceptionLog());
00174   if ( el.IsEmpty() ) return false;
00175   MSG("Dbi", level) << el;
00176   if ( level >= Msg::kWarning )  DbiExceptionLog::GetGELog().AddLog(el);
00177   return true;
00178 
00179 }
00180 
00181 //.....................................................................
00182 
00183 std::list<TString>  DbiStatement::TestTranslateSQL(const TString& sql, Dbi::DbTypes type) {
00184 //
00185 //
00186 //  Purpose:  Debugging aid: Test translate from MySQL to other dialects of SQL.
00187 //
00188 //  Arguments: 
00189 //    sql          in    The string to be translated
00190 //    type         in    The database type to translate to.
00191 //
00192 //  Return:    A list of translated SQL
00193 //             NB A single MySQL command can generate multiple ORACLE ones.
00194 
00195   Dbi::DbTypes fDbTypeSave = fDbType;
00196   fDbType = type;
00197   std::list<TString> sqlTrans(this->TranslateSQL(sql));
00198   fDbType = fDbTypeSave;
00199   return sqlTrans;
00200 
00201 }
00202 
00203 //.....................................................................
00204 
00205 std::list<TString>  DbiStatement::TranslateSQL(const TString& sql) {
00206 //
00207 //
00208 //  Purpose:  Translate from MySQL to other dialects of SQL.
00209 //
00210 //  Arguments: 
00211 //    sql          in    The string to be translated
00212 //
00213 //
00214 //  Return:    A list of translated SQL
00215 //             NB A single MySQL command can generate multiple ORACLE ones.
00216 // 
00217 //  Contact:   N. West
00218 //
00219 //  Specification:-
00220 //  =============
00221 //
00222 //  Translate  MySQL to other dialects of SQL.
00223 
00224 //  This is NOT meant to be a general purpose SQL translator, but rather a very
00225 //  simple translater of the SQL that the DBI employs which is dialect specific.
00226 
00227 //  The only translations supported are as follows:-
00228 
00229 //  1)  MySQL -> Oracle:-
00230 //      Set date format to be compatible with MySQL.
00231 //
00232 //  2)  MySQL -> Oracle:-
00233 //      Convert WHERE expressions of the form A & B to bitand(A,B) != 0
00234 //
00235 //  3)  MySQL -> Oracle:-
00236 //      In CREATE TABLE 
00237 //
00238 //      Conversion is achieved by creating a DbiTableMetaData object
00239 //      from the SQL and then asking it to generate the Oracle equivalent.
00240 //      See DbiTableMetaData for details.
00241 //      
00242 //  4)  MySQL -> Oracle:-
00243 //      SHOW TABLES        ->  SELECT TABLE_NAME FROM ALL_TABLES
00244 //
00245 //  5)  MySQL -> Oracle:-
00246 //      \' -> '' (in general ORACLE doesn't respect escape sequences
00247 //                except single quotes and only then as '' not \').
00248 //
00249 //  6)  MySQL -> Oracle:-
00250 //      Convert now() into sysdate
00251 
00252   std::list<TString> sqlTransList;
00253 
00254   if ( fDbType != Dbi::kOracle ) {
00255     sqlTransList.push_back(sql);
00256     return sqlTransList;
00257 }
00258 
00259   Bool_t translated = false;  //Once true,  sqlTransList contains the translation. No further translation possible.
00260   Bool_t modified  = false;   //sqlTrans has been modified, but  further translation possible.
00261 
00262   TString sqlTrans(sql);   
00263   sqlTrans.ToUpper();
00264 
00265 // Set date format  to be compatible with MySQL.
00266   sqlTransList.push_back("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD hh24:mi:ss'");
00267 
00268 // Translate NOW()
00269   if ( sqlTrans.Index("NOW()") != kNPOS ) {
00270     sqlTrans.ReplaceAll("NOW()","SYSDATE");
00271     modified = true;
00272   }
00273 
00274 // Translate SHOW TABLES
00275   if (sqlTrans == "SHOW TABLES" ) {
00276     sqlTrans = "SELECT TABLE_NAME FROM ALL_TABLES";
00277     sqlTransList.push_back(sqlTrans);
00278     translated = true;
00279   }
00280 
00281    TString sqlIncant;
00282 
00283 // Translate CREATE TABLE commands using DbiTableMetaData.
00284   Ssiz_t createTableIndex = sqlTrans.Index("CREATE TABLE",0,TString::kIgnoreCase );
00285   if ( ! translated && createTableIndex != kNPOS ) {
00286     DbiTableMetaData tmd;
00287     tmd.SetFromSql(sql.Data());
00288     std::vector<std::string> sql_list;
00289     UtilString::StringTok(sql_list,tmd.Sql(Dbi::kOracle),";");
00290     std::vector<std::string>::const_iterator itr(sql_list.begin()), itrEnd(sql_list.end());
00291     while ( itr != itrEnd ) { 
00292       sqlTransList.push_back(*itr); 
00293       ++itr;
00294     }
00295     translated = true;
00296   }
00297 
00298   
00299   // Translate DROP TABLE commands.
00300   Ssiz_t dropTableIndex = sqlTrans.Index("DROP TABLE",0,TString::kIgnoreCase );
00301   if ( ! translated && dropTableIndex != kNPOS ) {
00302     // Remove any "IF EXISTS"
00303     sqlTrans.ReplaceAll(" IF EXISTS ",    " ");
00304 
00305     //add the drop table command, then work on the synonym
00306     sqlTransList.push_back(sqlTrans);
00307 
00308     // Extract the table name
00309     Ssiz_t startIndex = dropTableIndex + 10;
00310     while ( isspace(sqlTrans[startIndex]) ) ++startIndex;
00311     Ssiz_t endIndex = startIndex + 1;
00312     Ssiz_t endIndexMax = sqlTrans.Length();
00313     while (     endIndex < endIndexMax
00314             && (isalnum(sqlTrans[endIndex]) || sqlTrans[endIndex] == '_') 
00315           ) ++endIndex;
00316     TString name(sqlTrans.Data()+startIndex,endIndex-startIndex);
00317     translated = true;
00318     
00319     // Add extra ORACLE incantations required when dropping a table with
00320     // a public synonym
00321     TString sqlIncant("DROP PUBLIC SYNONYM ");
00322     sqlIncant  += name ;
00323     sqlTransList.push_back(sqlIncant);
00324   }
00325   
00326 // Translate commands with a WHERE keyword, but take care if this is
00327 // an INSERT INTO command - it could include "where" as part of a character value!
00328   Ssiz_t whereStart = sqlTrans.Index("INSERT INTO",0,TString::kIgnoreCase );
00329   if ( whereStart == kNPOS ) whereStart = 0;
00330   else {
00331     // Skip to end of INSERT by looking for the trailing ")" at level 0
00332     // Yes, I know this can be defeated by mismatched "(" and ")" within
00333     // character values, but it's better than giving up on a possible WHERE clause 
00334     // altogether.
00335     Ssiz_t whereStartMax = sqlTrans.Length();
00336     whereStart = sqlTrans.Index("(",whereStart,TString::kIgnoreCase );
00337     if ( whereStart == kNPOS ) whereStart = whereStartMax;
00338     else ++whereStart;
00339     int level = 1;
00340     while ( whereStart < whereStartMax && level ) {
00341       char chr = sqlTrans[whereStart++];
00342       if ( chr == '(' ) ++level;
00343       if ( chr == ')' ) --level;
00344     }
00345   }
00346   // Look for space separated WHERE (so not fooled by e.g. FABWHERE!)
00347   Ssiz_t whereIndex = sqlTrans.Index(" WHERE ",whereStart,TString::kIgnoreCase );
00348   if ( ! translated && whereIndex != kNPOS ) {
00349 
00350     // Set limit of WHERE clause
00351     ++whereIndex; // Step over leading space
00352     Ssiz_t whereEnd = sqlTrans.Length();
00353     std::string whereDelim[] = { "GROUP BY", "HAVING", "ORDER BY", "LIMIT" };
00354     int numDelims = sizeof(whereDelim)/sizeof(string);
00355     for (int iDelim = 0; iDelim < numDelims; ++iDelim ) {
00356       const string& delimName = whereDelim[iDelim];
00357       Ssiz_t delimIndex = sqlTrans.Index(delimName.c_str(), delimName.size(), whereIndex + 5, TString::kIgnoreCase );
00358       if (delimIndex != kNPOS && delimIndex < whereEnd) whereEnd = delimIndex;
00359     }
00360 
00361     // Translate all bitwise and expressions within the WHERE clause.
00362 
00363     TString whereClause(sql.Data()+whereIndex,whereEnd-whereIndex);
00364     // Convert \n to space so that tokenising works.
00365     whereClause.ReplaceAll("\n"," ");
00366    
00367     std::vector<std::string> tokens;
00368     UtilString::StringTok(tokens,whereClause.Data()," ");
00369     int numTokens = tokens.size();
00370     for (int ithToken = 1; ithToken < numTokens-1; ++ithToken ) {
00371       if ( tokens[ithToken] == "&" ) {
00372         string tmp("bitand(");
00373         tmp += tokens[ithToken-1] + "," +  tokens[ithToken+1] + ") != 0";
00374         tokens[ithToken] = tmp;
00375         tokens[ithToken-1] = "";
00376         tokens[ithToken+1] = "";
00377       }
00378     }
00379 
00380     sqlTrans = sql(0,whereIndex);
00381     for (int ithToken = 0; ithToken < numTokens; ++ithToken ) {
00382       sqlTrans += " ";
00383       sqlTrans += tokens[ithToken].c_str();
00384     }
00385     sqlTrans += " " + sql(whereEnd,999999);
00386     modified = true;
00387   }
00388 
00389 // Translate \' to ''
00390   if ( ! translated && sqlTrans.Index("\\\'") != kNPOS ) {
00391 //  Bit of a kludge, if not yet modified, undo upper case conversion
00392 //  as quoted data is likely to contain characters.
00393     if ( not modified ) sqlTrans = sql;
00394     sqlTrans.ReplaceAll("\\\'","\'\'");
00395     modified = true;
00396   }
00397 
00398   if ( modified && ! translated ) {
00399     sqlTransList.push_back(sqlTrans);
00400     translated = true;
00401   }
00402 
00403   if ( translated ) {
00404     MSG("Dbi",Msg::kSynopsis) << "sql: " << sql  << endl
00405                            << "translates to " << sqlTransList.size() 
00406                            << " statements:- \n";
00407     std::list<TString>::const_iterator itr(sqlTransList.begin()), itrEnd(sqlTransList.end());
00408     while (itr != itrEnd) { MSG("Dbi",Msg::kSynopsis) << "   " << *itr << endl; ++itr;}
00409   }
00410   else {
00411     sqlTransList.push_back(sql);
00412   }
00413 
00414   return sqlTransList;
00415 
00416 }
00417 

Generated on Mon Feb 15 11:06:34 2010 for loon by  doxygen 1.3.9.1