EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FairDbStatement.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file FairDbStatement.cxx
1 #include <iostream>
2 #include <sstream>
3 #include <string>
4 #include <vector>
5 
6 #include "TString.h"
7 
8 #include "FairDbStatement.h"
9 #include "FairDbTableMetaData.h"
10 #include "FairDbString.h"
11 
13 
15  fConDb(conDb),
16  fDbType(conDb.GetDbType()),
17  fExceptionLog()
18 {
19  fConDb.ConnectStatement();
20 }
21 
22 
24 {
26 }
27 
28 TSQLStatement* FairDbStatement::CreateProcessedStatement(const TString& sql)
29 {
30  TSQLStatement* stmt = fConDb.CreatePreparedStatement(sql.Data());
31  if ( ! stmt ) {
33  return 0;
34  }
35  if ( stmt->Process() ) { return stmt; }
36  this->AppendExceptionLog(stmt);
37  delete stmt;
38  stmt = 0;
39  return 0;
40 
41 }
42 
43 TSQLStatement* FairDbStatement::ExecuteQuery( const TString& sql)
44 {
45  this->ClearExceptionLog();
46 
47  std::list<TString> sqlList(this->TranslateSQL(sql));
48  std::list<TString>::const_iterator itr(sqlList.begin()), itrEnd(sqlList.end());
49 
50  TSQLStatement* stmt = 0;
51  while (itr != itrEnd) {
52  const TString& sql = *itr++;
53  cout <<"-I- FairDbStatement SQL:" << fConDb.GetDbName() << ":" << sql << endl;
54  delete stmt;
55  stmt = this->CreateProcessedStatement(sql);
56  if ( ! stmt ) { return 0; }
57  }
58  // Store results from last SQL command (when multiple commands are generated
59  // the last will be the one that performs the query).
60  if ( stmt && ! stmt->StoreResult() ) {
61  this->AppendExceptionLog(stmt);
62  delete stmt;
63  stmt = 0;
64  }
65 
66 
67  // <DB> here should be some checks
68  // implemented
69 
70  if ( stmt ) {
71  if ( ! fExceptionLog.IsEmpty() ) {
72  delete stmt;
73  stmt = 0;
74  }
75  } else if ( fExceptionLog.IsEmpty() ) {
76  ostringstream oss;
77  oss << "Unknown failure (no execption but no TSQLStatement either executing " << sql;
78  fExceptionLog.AddEntry(oss.str().c_str());
79  }
80 
81  return stmt;
82 
83 }
84 
85 //.....................................................................
86 
87 Bool_t FairDbStatement::ExecuteUpdate( const TString& sql)
88 {
89  this->ClearExceptionLog();
90  std::list<TString> sqlList(this->TranslateSQL(sql));
91  std::list<TString>::const_iterator itr(sqlList.begin()), itrEnd(sqlList.end());
92 
93  while (itr != itrEnd) {
94  const TString& sql = *itr++;
95  cout << "-I- FairDbStatement::ExecuteUpdate SQL:" << fConDb.GetDbName() << ":" << sql << endl;
96  bool ok = fConDb.GetServer()->Exec(sql.Data());
97  if ( ! ok ) {
98 
100  this->AppendExceptionLog(fConDb);
101  return kFALSE;
102  }
103  }
104  return fExceptionLog.IsEmpty();
105 }
106 
107 Bool_t FairDbStatement::PrintExceptions(Int_t level) const
108 {
109  const FairDbExceptionLog& el(this->GetExceptionLog());
110  if ( el.IsEmpty() ) { return false; }
112  return true;
113 
114 }
115 
116 std::list<TString> FairDbStatement::TestTranslateSQL(const TString& sql, FairDb::DbTypes type)
117 {
118 
119  FairDb::DbTypes fDbTypeSave = fDbType;
120  fDbType = type;
121  std::list<TString> sqlTrans(this->TranslateSQL(sql));
122  fDbType = fDbTypeSave;
123  return sqlTrans;
124 
125 }
126 
127 //.....................................................................
128 
129 std::list<TString> FairDbStatement::TranslateSQL(const TString& sql)
130 {
131 // Translate MySQL specific to other dialects of SQL.
132 
133 // This is NOT meant to be a general purpose SQL translator, but rather a very
134 // simple translater of the SQL that the DBI employs which is dialect specific.
135 
136 // The only translations supported are as follows:-
137 
138 // 1) MySQL -> Oracle:-
139 // Set date format to be compatible with MySQL.
140 //
141 // 2) MySQL -> Oracle:-
142 // Convert WHERE expressions of the form A & B to bitand(A,B) != 0
143 //
144 // 3) MySQL -> Oracle:-
145 // In CREATE TABLE
146 //
147 // Conversion is achieved by creating a FairDbTableMetaData object
148 // from the SQL and then asking it to generate the Oracle equivalent.
149 // See FairDbTableMetaData for details.
150 //
151 // 4) MySQL -> Oracle:-
152 // SHOW TABLES -> SELECT TABLE_NAME FROM ALL_TABLES
153 //
154 // 5) MySQL -> Oracle:-
155 // \' -> '' (in general ORACLE doesn't respect escape sequences
156 // except single quotes and only then as '' not \').
157 //
158 // 6) MySQL -> Oracle:-
159 // Convert now() into sysdate
160 
161  std::list<TString> sqlTransList;
162 
163  if ( fDbType != FairDb::kOracle ) {
164  sqlTransList.push_back(sql);
165  return sqlTransList;
166  }
167 // Once kTRUE, sqlTransList contains the translation. No further translation possible.
168  Bool_t translated = kFALSE;
169 // sqlTrans has been modified, but further translation possible.
170  Bool_t modified = kFALSE;
171 
172  TString sqlTrans(sql);
173  sqlTrans.ToUpper();
174 
175 // Set date format to be compatible with MySQL.
176  sqlTransList.push_back("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD hh24:mi:ss'");
177 
178 // Translate NOW()
179  if ( sqlTrans.Index("NOW()") != kNPOS ) {
180  sqlTrans.ReplaceAll("NOW()","SYSDATE");
181  modified = kTRUE;
182  }
183 
184 // Translate SHOW TABLES
185  if (sqlTrans == "SHOW TABLES" ) {
186  sqlTrans = "SELECT TABLE_NAME FROM ALL_TABLES";
187  sqlTransList.push_back(sqlTrans);
188  translated = kTRUE;
189  }
190 
191  TString sqlIncant;
192 
193 // Translate CREATE TABLE commands using FairDbTableMetaData.
194  Ssiz_t createTableIndex = sqlTrans.Index("CREATE TABLE",0,TString::kIgnoreCase );
195  if ( ! translated && createTableIndex != kNPOS ) {
197  tmd.SetFromSql(sql.Data());
198  std::vector<std::string> sql_list;
199  FairUtilString::StringTok(sql_list,tmd.Sql(FairDb::kOracle),";");
200  std::vector<std::string>::const_iterator itr(sql_list.begin()), itrEnd(sql_list.end());
201  while ( itr != itrEnd ) {
202  sqlTransList.push_back(*itr);
203  ++itr;
204  }
205  translated = kTRUE;
206  }
207 
208 
209  // Translate DROP TABLE commands.
210  Ssiz_t dropTableIndex = sqlTrans.Index("DROP TABLE",0,TString::kIgnoreCase );
211  if ( ! translated && dropTableIndex != kNPOS ) {
212  // Remove any "IF EXISTS"
213  sqlTrans.ReplaceAll(" IF EXISTS ", " ");
214 
215  //add the drop table command, then work on the synonym
216  sqlTransList.push_back(sqlTrans);
217 
218  // Extract the table name
219  Ssiz_t startIndex = dropTableIndex + 10;
220  while ( isspace(sqlTrans[startIndex]) ) { ++startIndex; }
221  Ssiz_t endIndex = startIndex + 1;
222  Ssiz_t endIndexMax = sqlTrans.Length();
223  while ( endIndex < endIndexMax
224  && (isalnum(sqlTrans[endIndex]) || sqlTrans[endIndex] == '_')
225  ) { ++endIndex; }
226  TString name(sqlTrans.Data()+startIndex,endIndex-startIndex);
227  translated = kTRUE;
228 
229  // Add extra ORACLE incantations required when dropping a table with
230  // a public synonym
231  TString sqlIncant("DROP PUBLIC SYNONYM ");
232  sqlIncant += name ;
233  sqlTransList.push_back(sqlIncant);
234  }
235 
236 // Translate commands with a WHERE keyword, but take care if this is
237 // an INSERT INTO command - it could include "where" as part of a character value!
238  Ssiz_t whereStart = sqlTrans.Index("INSERT INTO",0,TString::kIgnoreCase );
239  if ( whereStart == kNPOS ) { whereStart = 0; }
240  else {
241  // Skip to end of INSERT by looking for the trailing ")" at level 0
242  // Yes, I know this can be defeated by mismatched "(" and ")" within
243  // character values, but it's better than giving up on a possible WHERE clause
244  // altogether.
245  Ssiz_t whereStartMax = sqlTrans.Length();
246  whereStart = sqlTrans.Index("(",whereStart,TString::kIgnoreCase );
247  if ( whereStart == kNPOS ) { whereStart = whereStartMax; }
248  else { ++whereStart; }
249  int level = 1;
250  while ( whereStart < whereStartMax && level ) {
251  char chr = sqlTrans[whereStart++];
252  if ( chr == '(' ) { ++level; }
253  if ( chr == ')' ) { --level; }
254  }
255  }
256  // Look for space separated WHERE (so not fooled by e.g. FABWHERE!)
257  Ssiz_t whereIndex = sqlTrans.Index(" WHERE ",whereStart,TString::kIgnoreCase );
258  if ( ! translated && whereIndex != kNPOS ) {
259 
260  // Set limit of WHERE clause
261  ++whereIndex; // Step over leading space
262  Ssiz_t whereEnd = sqlTrans.Length();
263  std::string whereDelim[] = { "GROUP BY", "HAVING", "ORDER BY", "LIMIT" };
264  int numDelims = sizeof(whereDelim)/sizeof(string);
265  for (int iDelim = 0; iDelim < numDelims; ++iDelim ) {
266  const string& delimName = whereDelim[iDelim];
267  Ssiz_t delimIndex = sqlTrans.Index(delimName.c_str(), delimName.size(), whereIndex + 5, TString::kIgnoreCase );
268  if (delimIndex != kNPOS && delimIndex < whereEnd) { whereEnd = delimIndex; }
269  }
270 
271  // Translate all bitwise and expressions within the WHERE clause.
272 
273  TString whereClause(sql.Data()+whereIndex,whereEnd-whereIndex);
274  // Convert \n to space so that tokenising works.
275  whereClause.ReplaceAll("\n"," ");
276 
277  std::vector<std::string> tokens;
278  FairUtilString::StringTok(tokens,whereClause.Data()," ");
279  int numTokens = tokens.size();
280  for (int ithToken = 1; ithToken < numTokens-1; ++ithToken ) {
281  if ( tokens[ithToken] == "&" ) {
282  string tmp("bitand(");
283  tmp += tokens[ithToken-1] + "," + tokens[ithToken+1] + ") != 0";
284  tokens[ithToken] = tmp;
285  tokens[ithToken-1] = "";
286  tokens[ithToken+1] = "";
287  }
288  }
289 
290  sqlTrans = sql(0,whereIndex);
291  for (int ithToken = 0; ithToken < numTokens; ++ithToken ) {
292  sqlTrans += " ";
293  sqlTrans += tokens[ithToken].c_str();
294  }
295  sqlTrans += " " + sql(whereEnd,999999);
296  modified = kTRUE;
297  }
298 
299 // Translate \' to ''
300  if ( ! translated && sqlTrans.Index("\\\'") != kNPOS ) {
301 // Bit of a kludge, if not yet modified, undo upper case conversion
302 // as quoted data is likely to contain characters.
303  if ( not modified ) { sqlTrans = sql; }
304  sqlTrans.ReplaceAll("\\\'","\'\'");
305  modified = kTRUE;
306  }
307 
308  if ( modified && ! translated ) {
309  sqlTransList.push_back(sqlTrans);
310  translated = kTRUE;
311  }
312 
313  if ( translated ) {
314  cout << "-I- FairDbStatement::TranslateSQL sql: " << sql << endl
315  << "translates to " << sqlTransList.size()
316  << " statements:- \n";
317  std::list<TString>::const_iterator itr(sqlTransList.begin()), itrEnd(sqlTransList.end());
318  while (itr != itrEnd) { cout <<"-I- FairDbStatement::TranslateSQL " << " " << *itr << endl; ++itr;}
319  } else {
320  sqlTransList.push_back(sql);
321  }
322 
323  return sqlTransList;
324 
325 }