EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FairDbMultConnector.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file FairDbMultConnector.cxx
1 #include <cstdlib>
2 #include <memory>
3 using std::auto_ptr;
4 #include <sstream>
5 
6 #include "TList.h"
7 #include "TROOT.h"
8 #include "TSQLStatement.h"
9 #include "TSystem.h"
10 
11 #include "FairDbMultConnector.h"
12 #include "FairDb.h"
13 #include "FairDbString.h"
14 
16 
18  fGlobalSeqNoDbNo(-1),
19  fConnections(),
20  fTemporaryTables()
21 {
22 // Current cascader configuration comes from 3 environmental
23 // variables:-
24 //
25 // ENV_DB_URL a semi-colon separated list of URLs
26 // ENV_DB_USER user name (one or a semi-colon separated list)
27 // ENV_DB_PSWD password (one or a semi-colon separated list)
28 //
29 // or the _UPDATE alternatives e.g. ENV_DB_UPDATE_USER
30 //
31 // The _UPDATE versions take priority.
32 
33 
34 // Extract args from ENV_DB environmental variables
35  const char* strUser = gSystem->Getenv("ENV_TSQL_UPDATE_USER");
36  if ( ! strUser ) { strUser = gSystem->Getenv("ENV_TSQL_USER"); }
37  const char* strPswd = gSystem->Getenv("ENV_TSQL_UPDATE_PSWD");
38  if ( ! strPswd ) { strPswd = gSystem->Getenv("ENV_TSQL_PSWD"); }
39  const char* strUrl = gSystem->Getenv("ENV_TSQL_UPDATE_URL");
40  if ( !strUrl ) { strUrl = gSystem->Getenv("ENV_TSQL_URL"); }
41  string userList = ( strUser ) ? strUser : "";
42  string pswdList = ( strPswd ) ? strPswd : "";
43  string urlList = ( strUrl ) ? strUrl : "";
44 
45  if ( urlList == "" || userList == "" || pswdList == "" ) {
46  cout << "-E- FairDbMultConnector() "
47  << "Cannnot open a Database cascade;\n"
48  << " the environmental variables ENV_DB_*:-" << endl
49  << "USER: \"" << userList << "\" PSWD:\"" << pswdList
50  << "\" URL:\"" << urlList << endl
51  << " are either not defined or empty.\n"
52  << " Please check your settings of ENV_DB_USER,"
53  << " ENV_DB_PSWD and ENV_DB_URL\n"
54  << "Aborting due to above errors " << endl;
55  exit(1);
56  }
57 
58  std::vector<std::string> users, pswds, urls;
59  FairUtilString::StringTok(users, userList, ";");
60  FairUtilString::StringTok(pswds, pswdList, ";");
61  FairUtilString::StringTok(urls, urlList, ";");
62 
63  bool fail = false;
64 
65  for (unsigned entry = 0; entry < urls.size(); ++entry ) {
66  string url = urls[entry];
67  string user = ( entry >= users.size() ) ? users[0] : users[entry];
68  string pswd = ( entry >= pswds.size() ) ? pswds[0] : pswds[entry];
69 
70  // Handle empty password designated as '\0'
71  // (an empty null terminated character string)
72  if ( pswd == "\\0" ) { pswd = ""; }
73 
74  FairDbConnection* con = new FairDbConnection(url,user,pswd);
75  fConnections.push_back(con);
76  if ( ! con->Open() ) {
77  fail = true;
78  continue;
79  }
80 
81 // Attempt to locate first GlobalSeqNo/GLOBALSEQNO table.
82  if ( fGlobalSeqNoDbNo != -1 ) { continue; }
83  auto_ptr<FairDbStatement> stmtDb(new FairDbStatement(*con));
84  if ( ! stmtDb.get() ) { continue; }
85  TSQLStatement* stmt = stmtDb->ExecuteQuery("Select * from GLOBALSEQNO where 1=0");
86  if ( stmt ) {
87  fGlobalSeqNoDbNo = fConnections.size()-1;
88  delete stmt;
89  stmt = 0;
90  }
91 
92 // Check for presence of a DB_STATE_FLAG table
93  if ( this->GetTableDbNo("DBI_STATE_FLAGS",entry) != -1 ) {
94  cout << " -E- FairDbMultConnector POSSIBLE VERSION SHEAR DETECTED !!!\n"
95  << " The DB_STATE_FLAGS table is present on cascade entry " << entry << ". This table will\n"
96  << " only be introduced to manage backward incompatible changes that could lead\n"
97  << " to version shear between the code and the database. This version of the\n"
98  << " code does not support the change the presence of that table indicates\n"
99  << " so has to shut down. \n";
100  fail = true;
101  }
102  }
103 
104  cout << *this;
105 
106 // Abort, if there have been any failures.
107  if ( fail ) {
108 
109  cout << " -E- FairDbMultConnector() Aborting due to above errors" << endl;
110  exit(1);
111  }
112 
113 }
114 
115 
117  : fGlobalSeqNoDbNo(conn.fGlobalSeqNoDbNo),
118  fConnections(conn.fConnections),
119  fTemporaryTables(conn.fTemporaryTables)
120 {
121  /*
122  fGlobalSeqNoDbNo=conn.fGlobalSeqNoDbNo;
123  fConnections=conn.fConnections;
124  fTemporaryTables=conn.fTemporaryTables;
125  */
126 }
127 //.....................................................................
128 
130 {
131 
132  cout << "-I- FairDbMultConnector : Destroying FairDbMultConnector"
133  << endl;
134  for (Int_t dbNo = this->GetNumDb()-1; dbNo >= 0; --dbNo) {
135  delete fConnections[dbNo];
136  }
137 
138 }
139 
140 //.....................................................................
141 
142 ostream& operator<<(ostream& os, const FairDbMultConnector& cascader)
143 {
144 
145  os << "-I- FairDbMultConnector() Status:- " << endl
146  << "Status URL" << endl << endl;
147 
148  int maxDb = cascader.GetNumDb();
149  for (Int_t dbNo = 0; dbNo < maxDb; ++dbNo)
150  os << cascader.GetStatusAsString(dbNo) << " "
151  << ( ( dbNo == cascader.fGlobalSeqNoDbNo ) ? "(auth) " : " ")
152  << cascader.GetURL(dbNo) << endl;
153  os << endl;
154  return os;
155 
156 }
157 
158 //.....................................................................
159 
160 Int_t FairDbMultConnector::AllocateSeqNo(const string& tableName,
161  Int_t requireGlobal, /* =0 */
162  Int_t dbNo /* = 0 */) const
163 {
164  bool isTemporary = IsTemporaryTable(tableName,dbNo);
165 
166  // Deal with global requests.
167 
168  if ( requireGlobal > 0
169  || ( requireGlobal == 0 && dbNo == fGlobalSeqNoDbNo && ! isTemporary ) ) {
170  if ( fGlobalSeqNoDbNo < 0 ) {
171  cout << " -I- FairDbMultConnector: Unable to issue global SEQNO - no authorising DB in cascade\n"
172  << " will issue local one instead" << endl;
173  } else if ( isTemporary ) {
174  cout << " -I- FairDbMultConnector: Unable to issue global SEQNO - " << tableName << " is temporary\n"
175  << " will issue local one instead" << endl;
176  } else { return this->ReserveNextSeqNo(tableName,true,fGlobalSeqNoDbNo); }
177  }
178 
179  // Deal with local requests
180  return this->ReserveNextSeqNo(tableName,false,dbNo);
181 
182 }
183 
184 //.....................................................................
185 
187 {
188 // <NB>
189 // As the caller is responsible for destroying the statement after use
190 // consider:-
191 //
192 // #include <memory>
193 // using std::auto_ptr;
194 //
195 // ...
196 //
197 // auto_ptr<FairDbStatement> stmt(cascader.CreateStatement(dbNo));
198 
199 
200  if ( this->GetStatus(dbNo) == kFailed ) { return 0; }
201  FairDbConnection& conDb = *fConnections[dbNo];
202  FairDbStatement* stmtDb = new FairDbStatement(conDb);
203  stmtDb->PrintExceptions();
204  return stmtDb;
205 
206 }
207 //.....................................................................
208 
209 Int_t FairDbMultConnector::CreateTemporaryTable(const string& tableNameMc,
210  const string& tableDescr)
211 {
212 
213  string tableName = FairUtilString::ToUpper(tableNameMc);
214  if ( tableName == ""
215  || tableDescr[0] != '('
216  || tableDescr[tableDescr.size()-1] != ')' ) {
217  cout << "-I- FairDbMultConnector:: Illegal input args:-" << endl
218  << " Table Name: " << tableName
219  << " Table Description: " << tableDescr
220  <<endl;
221  return -1;
222  }
223 
224 // Find a DB that will accept the command.
225  string sqlMakeTable;
226 
227  Int_t dbNoAcc = -1;
228  auto_ptr<FairDbStatement> stmtDb;
229  for (UInt_t dbNoTry = 0; dbNoTry < fConnections.size(); ++dbNoTry ) {
230  stmtDb.reset(this->CreateStatement(dbNoTry));
231  if ( stmtDb.get() ) {
232  sqlMakeTable = " create temporary table ";
233  sqlMakeTable += tableName;
234  sqlMakeTable += " ";
235  sqlMakeTable += tableDescr;
236  sqlMakeTable += ";";
237  stmtDb->ExecuteUpdate(sqlMakeTable.c_str());
238  if ( stmtDb->GetExceptionLog().IsEmpty() ) {
239  dbNoAcc = dbNoTry;
240  this->GetConnection(dbNoAcc)->SetTableExists(tableName);
241  break;
242  }
243  // print exceptions
244 
245  }
246  }
247 
248  if ( dbNoAcc < 0 ) {
249  if ( stmtDb.get()) { stmtDb->PrintExceptions(); }
250  return -1;
251  }
252 
253 // Make connection permanent if not already.
254  FairDbConnection& conDb = *fConnections[dbNoAcc];
255  if ( conDb.IsTemporary() ) {
256  conDb.SetPermanent();
257  cout << "-I- FairDbMultConnector: Making connection: " << conDb.GetUrl()
258  << " permanent to preserve temporary tables." << endl;
259  }
260 
261 // Create SQL to create auxillary validity table and write to same Db.
262  sqlMakeTable = FairDb::GetValDescr(tableName.c_str(),true);
263 
264  cout << "-I- FairDbMultConnector: Validity Table creation: "
265  << " Database: " << dbNoAcc << " "
266  << sqlMakeTable << endl;
267  stmtDb->ExecuteUpdate(sqlMakeTable.c_str());
268  if ( stmtDb->PrintExceptions() ) { return -1; }
269  this->GetConnection(dbNoAcc)->SetTableExists(tableName+"VAL");
270  fTemporaryTables[tableName] = dbNoAcc;
271  return dbNoAcc;
272 
273 }
274 
275 
277 {
278 
279  if ( this->GetStatus(dbNo) == kFailed ) { return 0; }
280  return fConnections[dbNo];
281 
282 }
283 //.....................................................................
284 
286 {
287 
288  if ( this->GetStatus(dbNo) == kFailed ) { return 0; }
289  return fConnections[dbNo];
290 
291 }
292 
293 string FairDbMultConnector::GetDbName(UInt_t dbNo) const
294 {
295 
296  string dbName;
297 
298  if ( dbNo < this->GetNumDb() ) { dbName = fConnections[dbNo]->GetDbName(); }
299  else { cout << "-I- FairDbMultConnector: Database does not contain entry " << dbNo << endl; }
300  return dbName;
301 
302 }
303 
304 
305 Int_t FairDbMultConnector::GetDbNo(const string& dbName) const
306 {
307  for ( unsigned dbNo = 0; dbNo < this->GetNumDb(); ++dbNo) {
308  if ( dbName == fConnections[dbNo]->GetDbName() ) { return dbNo; }
309  }
310 
311  cout << "-I- FairDbMultConnector:Database does not contain entry " << dbName << endl;
312  return -1;
313 
314 }
315 
316 
317 string FairDbMultConnector::GetStatusAsString(UInt_t dbNo) const
318 {
319 
320  Int_t status = GetStatus(dbNo);
321 
322  switch ( status ) {
323  case kClosed:
324  return "Closed";
325  case kOpen:
326  return "Open ";
327  default:
328  return "Failed";
329  }
330 
331 }
332 Int_t FairDbMultConnector::GetTableDbNo(const string& tableName,
333  Int_t selectDbNo /* -1 */) const
334 {
335  string::const_iterator itr = tableName.begin();
336  string::const_iterator itrEnd = tableName.end();
337  while ( itr != itrEnd ) if ( islower(*itr++) ) { return -1; }
338 
339 // Loop over cascade looking for table.
340  for (UInt_t dbNoTry = 0; dbNoTry < fConnections.size(); ++dbNoTry ) {
341  if ( selectDbNo >= 0 && (UInt_t) selectDbNo != dbNoTry ) { continue; }
342  const FairDbConnection* con = this->GetConnection(dbNoTry);
343  if ( con && con->TableExists(tableName) ) { return dbNoTry; }
344  }
345 
346  return -1;
347 
348 }
349 //.....................................................................
350 
352 {
353 
354  for (UInt_t dbNo = 0; dbNo < fConnections.size(); ++dbNo ) {
355  fConnections[dbNo]->ConnectStatement();
356  }
357 }
358 
359 Bool_t FairDbMultConnector::IsTemporaryTable(const string& tableName,
360  Int_t dbNo) const
361 {
362 
363  map<string,Int_t>::const_iterator itr
364  = fTemporaryTables.find(tableName);
365  return ( itr != fTemporaryTables.end()
366  && (*itr).second == dbNo );
367 
368 }
369 
370 
371 
372 FairDbMultConnector::BLock::BLock(FairDbStatement* stmtDB, const string& seqnoTable, const string& dataTable) :
373  fStmt(stmtDB),
374  fSeqnoTableName(seqnoTable),
375  fDataTableName(dataTable),
376  fLocked(kFALSE)
377 {
378  if ( ! fStmt ) {
379  cout << "-E FairDbMultConnector::Block Cannot obtain statment to set lock" << endl;
380  return;
381  }
382 
383  this->SetBLock(kTRUE);
384 
385 }
386 
388 {
389 
390  this->SetBLock(kFALSE);
391  delete fStmt;
392  fStmt = 0;
393 
394 }
395 
397 {
398  if ( setting == fLocked || ! fStmt ) { return; }
399 
400  if ( fStmt->GetDBType() != FairDb::kMySQL ) {
401  fLocked = setting;
402  return;
403  }
404 
405  string sql;
406 
407  if ( setting ) {
408  sql = "LOCK TABLES ";
409  sql += fSeqnoTableName + " WRITE";
410  if ( fDataTableName != "" ) { sql += ", " + fDataTableName + "VAL WRITE"; }
411  } else {
412  sql = "UNLOCK TABLES;";
413  }
414  cout << "Lock requested: " << setting
415  << " issuing lock command: " << sql << endl;
416  fStmt->ExecuteUpdate(sql.c_str());
417  if ( fStmt->GetExceptionLog().IsEmpty() ) { fLocked = setting; }
418  fStmt->PrintExceptions();
419 
420 }
421 
423 {
424 
425  for (UInt_t dbNo = 0; dbNo < fConnections.size(); ++dbNo ) {
426  fConnections[dbNo]->DisConnectStatement();
427  }
428 }
429 
430 Int_t FairDbMultConnector::ReserveNextSeqNo(const string& tableName,
431  Bool_t isGlobal,
432  UInt_t dbNo) const
433 {
434 
435  FairDbString sql;
436 
437  string seqnoTableName = isGlobal ? "GLOBALSEQNO" : "LOCALSEQNO";
438  bool seqnoTableNameExists = this->TableExists(seqnoTableName,dbNo);
439  bool tableNameExists = this->TableExists(tableName,dbNo);
440 
441  auto_ptr<FairDbStatement> stmtDb(this->CreateStatement(dbNo) );
442  if ( ! stmtDb.get() ) { return 0; }
443 
444  // Check that required SEQNO table exists.
445 
446  if ( isGlobal ) {
447  if ( ! seqnoTableNameExists ) {
448  cout<< "-I- FairDbMultConnector: Unable to issue global SEQNO - " << dbNo
449  << " is not an authorising DB" << endl;
450  return 0;
451  }
452  } else {
453  if ( ! seqnoTableNameExists ) {
454  sql.Clear();
455  sql << "CREATE TABLE " << seqnoTableName
456  << "(TABLENAME CHAR(64) NOT NULL PRIMARY KEY,\n"
457  << " LASTUSEDSEQNO INT )";
458 
459  cout<< "-I- FairDbMultConnector: Database: " << dbNo
460  << " create local SEQNO table query: " << sql.c_str() << endl;
461  stmtDb->ExecuteUpdate(sql.c_str());
462  if ( stmtDb->PrintExceptions() ) { return 0; }
463  sql.Clear();
464  sql << "INSERT INTO " << seqnoTableName << " VALUES ('*',0)";
465  cout<< "-I- FairDbMultConnector: Database: " << dbNo
466  << " prime local SEQNO table query: " << sql.c_str() << endl;
467  stmtDb->ExecuteUpdate(sql.c_str());
468  if ( stmtDb->PrintExceptions() ) { return 0; }
469  }
470  }
471 
472 // Lock seqno table by creating a lock object on the stack.
473 // Table will be unlocked when lock object destroyed.
474 
475  string dataTable;
476 // Only pass in table name if it's not temporary and exists in
477 // the selected DB otherwise Lock will try to lock a non-existent table.
478  if ( ! this->IsTemporaryTable(tableName,dbNo)
479  && tableNameExists ) { dataTable = tableName; }
480  BLock Block(this->CreateStatement(dbNo),seqnoTableName,dataTable);
481  if ( ! Block.IsBLocked() ) {
482  cout<< "-I- FairDbMultConnector: Unable to lock " << seqnoTableName << endl;
483  return 0;
484  }
485 
486 // Find row containing last used SeqNo for this table.
487 // Not that comparison is case insensitive.
488  sql.Clear();
489  sql << "select * from " << seqnoTableName << " where TABLENAME = '*' or TABLENAME = '";
490  sql << tableName + "' order by TABLENAME";
491  cout<< "-I- FairDbMultConnector: "<< seqnoTableName << " query: " << sql.c_str() << endl;
492  TSQLStatement* stmt = stmtDb->ExecuteQuery(sql.c_str());
493  stmtDb->PrintExceptions(0);
494  Int_t seqNoDefault = 0;
495  if ( stmt && stmt->NextResultRow() ) {
496  seqNoDefault = stmt->GetInt(1);
497  } else {
498  cout<< "-I- FairDbMultConnector: Unable to find default SeqNo"
499  << " due to above error" << endl;
500  delete stmt;
501  stmt = 0;
502  return 0;
503  }
504  Int_t seqNoTable = seqNoDefault;
505  if ( stmt->NextResultRow() ) {
506  seqNoTable = stmt->GetInt(1);
507  }
508  delete stmt;
509  stmt = 0;
510  cout<< "-I- FairDbMultConnector: query returned last used seqno: " << seqNoTable << endl;
511 
512 // If the table exists, make sure that the seqNo hasn't already been used.
513 // This is paranoia code and expensive, so only do the check once for
514 // each tableName/isGlobal/dbNo combination.
515 
516  static std::string checkedCombinations;
517  ostringstream combination;
518  combination << ":" << tableName << isGlobal << dbNo << ":";
519  bool notChecked = checkedCombinations.find(combination.str()) == std::string::npos;
520  if ( notChecked ) { checkedCombinations += combination.str(); }
521  if ( tableNameExists && notChecked ) {
522  Int_t seqNoMin = seqNoDefault;
523  Int_t seqNoMax = seqNoDefault + FairDb::kMAXLOCALSEQNO;
524  sql.Clear();
525  sql << "select max(SEQNO) from " << tableName << "VAL"
526  << " where SEQNO between " << seqNoMin << " and " << seqNoMax;
527  cout<< "-I- FairDbMultConnector: Database: " << dbNo
528  << " max SEQNO query: " << sql.c_str() << endl;
529  stmt = stmtDb->ExecuteQuery(sql.c_str());
530  if ( stmtDb->PrintExceptions() ) { return 0; }
531  Int_t minValue = 0;
532  // Queries returning group function results can be null.
533  if ( stmt && stmt->NextResultRow() && ! stmt->IsNull(0) ) {
534  minValue = stmt->GetInt(0);
535  if ( minValue <= 0 ) { minValue = 0; } // Should never happen.
536  }
537  delete stmt;
538  stmt = 0;
539 
540  if ( minValue > seqNoTable ) {
541  cout<< "-I- FairDbMultConnector: "
542  << "Database: " << dbNo << " "
543  << seqnoTableName << " has last used SEQNO of "
544  << seqNoTable << " for table " << tableName
545  << ",\n but the highest SEQNO in the band " << seqNoMin << " to " << seqNoMax
546  << " is " << minValue << " for that table\n "
547  << seqnoTableName << " is out of date! It will be updated for " << tableName << endl;
548  seqNoTable = minValue;
549  }
550  }
551 
552 
553 // Update last used SeqNo and record in table.
554  sql.Clear();
555  sql << "delete from " << seqnoTableName << " where TABLENAME='";
556  sql << tableName + "'";
557  cout<< "-I- FairDbMultConnector: SEQNO entry removal: "
558  << sql.c_str() << endl;
559  stmtDb->ExecuteUpdate(sql.c_str());
560  if ( stmtDb->PrintExceptions() ) { return 0; }
561 
562  seqNoTable++;
563 
564  sql.Clear();
565  sql << "insert into " << seqnoTableName << " values('";
566  sql << tableName + "'," << seqNoTable << ")";
567  cout<< "-I- FairDbMultConnector: SEQNO entry add: "
568  << sql.c_str() << endl;
569  stmtDb->ExecuteUpdate(sql.c_str());
570  if ( stmtDb->PrintExceptions() ) { return 0; }
571 
572  return seqNoTable;
573 
574 }
575 
577  Bool_t permanent )
578 {
579 
580  if ( dbNo < fConnections.size() ) {
581  fConnections[dbNo]->SetPermanent(permanent);
582  }
583 
584 }